Java基础面试题

重头开始学习Java近两个月。这两个月,学习了挺多,加上自己的基础还行,所以学的有点快。话说回来,学是学了,又记住了多少呢?好像也没记住多少,所以学太快也不见得是什么好事。那只好回头捋捋,复习复习了。

文章目录

1、JVM、JRE 和 JDK 的关系

JVM:Java Virtual Machine,即:Java虚拟机。它只认识后缀名为.class的文件,能够将class文件中的字节码指令进行识别并调用操作系统向上的API完成动作。
JRE:Java Runtime Environment,即:Java运行时环境。主要包括JVM的标准实现和java的一些基本类库。与JVM相比,JRE多了Java类库。
JDK:Java Development Kit,即:Java开发工具包。jdk是Java开发的核心,继承了JRE和一些小工具,如:javac.exe、java.exe、jar.exe等。
这三者的关系是:一层层的嵌套关系。JDK>JRE>JVM

2、什么是跨平台性?原理是什么?

所谓跨平台性,是指java语言编写的程序,一次编译后,可以在多个系统平台上运行。即:一次编写,多次运行。(Write Once,Run AnyWhere!)

实现原理:Java程序是通过java虚拟机即JVM在系统平台上运行的,只要该系统可以安装相应的java虚拟机,该系统就可以运行java程序。

也就是说,Java能够跨平台的核心在于JVM。并不是说Java本身能够跨平台,而是它的 JVM能够跨平台。不同的操作系统向上的API是不同的,如果想要调用系统的设备,就要针对不同系统的API写出不同的代码来完成。而Java引入了字节码的概念,JVM只认识字节码,并将他们解释到系统的API调用。针对不同的系统有不同的JVM实现,但是同一段代码在编译后的字节码是一样的。同一段字节码,在不同的JVM实现上会映射到不同系统的API调用,从而实现代码的不加修改即可跨平台运行。

3、Java是解析运行吗?

不是。

1、Java源代码经过javac编译成.class文件
2、.class文件经过JVM解析或编译运行。
(1)、解析:.class文件经过JVM内嵌的解析器解析执行
(2)、编译:存在JIT编译器(Just In Time Compile 即时编译器)把经常运行的代码作为"热点代码"编译与本地平台相关的机器码,并进行各种层次的优化。
(3)、AOT编译器: Java 9提供的直接将所有代码编译成机器码执行。
写个程序直接执行字节码就是解释执行。写个程序运行时把字节码动态翻译成机器码就是jit。写个程序把java源代码直接翻译为机器码就是aot。造个CPU直接执行字节码,字节码就是机器码。

解析执行:描述的是将源语言直接作为输入,每执行一条就将源代码解析成机器码,并不保留中间结果。因为每次都需要解释所以移植性和跨平台性很高。
编译执行:描述的是将源代码事先编译成目标机器的机器码文件,这样一来就直接可以在目标机器上运行,但是由于目标机器不同,每次更换目标都需要重新编译与之相对性的机器码文件,所以移植性较差。

4、&和&&的区别

&&:逻辑与运算符。当运算符左右两边的表达式都为 true,才返回 true。同时具有短路性,如果第一个表达式为 false,则直接返回 false。
&:逻辑与运算符、按位与运算符。
按位与运算符:用于二进制的计算,只有对应的两个二进位均为1时,结果位才为1 ,否则为0。
逻辑与运算符:& 在用于逻辑与时,和 && 的区别是不具有短路性。所在通常使用逻辑与运算符都会使用 &&,而 & 更多的适用于位运算。

5、用最有效率的方法计算 2 乘以 8

2 << 3

将一个数左移n 位,就相当于乘以了2 的n 次方,那么,一个数乘以8 只要将其左移3 位
即可,而位运算cpu 直接支持的,效率最高

6、不使用临时变量如何交换两个整数变量的值?

方法1:使用位运算。一个整数a在异或另一个整数b两次以后所得的值还是整数a。例如(a ^ b ^ b = a)

public class Demo001 {
    public static void main(String[] args) {
        int a = 5;
        int b = 3;
        System.out.println("交换前:a="+a+",b="+b);
        a = a ^ b;
        b = a ^ b;
        a = a ^ b;
        System.out.println("交换后:a="+a+",b="+b);
    }
}

Java基础面试题
方法2:(此方法有可能会发生溢出!)

public class Demo001 {
    public static void main(String[] args) {
        int a = 5;
        int b = 3;
        System.out.println("交换前:a="+a+",b="+b);
        a = a + b;
        b = a - b;
        a = a - b;
        System.out.println("交换后:a="+a+",b="+b);
    }
}

Java基础面试题

7、下面两个代码块能正常编译和执行吗?

        //代码块1
        short s1 = 1;
        s1 = s1 + 1;
        //代码块2
        short s1 = 1;
        s1+=1;

代码块1:不能。
原因:不兼容的类型: 从int转换到short可能会有损失。
代码块2:能。
原因:s1 += 1 相当于 s1 = (short)(s1 + 1)。

8、指出下题的输出结果

       Integer a = 127,b = 127;
       Integer c = 127;
       Integer d = 127;
       System.out.println(a==b);
       System.out.println(b==c);
       System.out.println(c==d);

Java基础面试题

9、String 是 Java 基本数据类型吗?

不是。

Java 中的基本数据类型只有8个,可分为以下四种类型:

  • 逻辑类型:boolean(true、false);
  • 整数类型: byte(1字节)、short(2字节)、int(4字节)、long(8字节)
  • 字符类型:char(1字节)
  • 浮点类型:float(4字节)、double(8字节)

除了基本类型(primitive type),剩下的都是引用类型(reference type)。
基本数据类型:数据直接存储在栈上
引用数据类型区别:数据存储在堆上,栈上只存储引用地址

10、Java 中操作字符串都有哪些类?它们之间有什么区别?

String、StringBuffer、StringBuilder

String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象。String对象是放在常量池里面的,不会被CG回收,从而会产生一定的内存垃圾。而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下尽量不要使用 String。
StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下尽量使用 StringBuilder,多线程环境下尽量使用 StringBuffer。

11、String str="ab"与 String str=new String(“ab”)一样吗?

不一样

  • 内存的分配方式不一样。String str="ab"的方式,Java 虚拟机会将其分配到常量池中;而 String str=new String(“ab”) 则会被分到堆内存中。
  • 本质上是:两个语句都会先去字符串常量池中检查是否已经存在 “ab”,如果有则直接使用,如果没有则会在常量池中创建 “ab” 对象。另外,String s = new String(“ab”) 还会通过 new String() 在堆里创建一个内容与 “ab” 相同的对象实例。所以前者其实理解为被后者的所包含。

12、== 和 equals 的区别是什么?

==
对于基本类型和引用类型的作用效果是不同的
基本类型:比较的是值是否相同;
引用类型:比较的是引用是否相同;

从equals的源码可以看出,如果比较的不是String类型的数据,equals的本质就是“==”。

Java基础面试题
而当比较的是String 类型的数据时,比较的是引用类型的数据,可分两种情况。

  1. 没重写equals方法:比较的是内存地址,
  2. 重写equals方法:比较的是两个对象的变量值,也就是内容是否相等。
    1)在Object中:==和equals没有区别,都是比较地址
    2)在String类中(对Object中的equals方法进行了重写):equals比较的是两个对象的内容是否相等

Java基础面试题

13、String 类可以继承吗?

不可以。
String 类使用 final 修饰,无法被继承。

14、两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?

不对

当有 a.equals(b) == true 时,则 a.hashCode() == b.hashCode() 必然成立。
反过来,当 a.hashCode() == b.hashCode() 时,a.equals(b) 不一定为 true。

15、final 在 Java 中有什么作用?

final作为Java中的关键字可以用于三个地方。用于修饰类、类属性和类方法。
特征:凡是引用final关键字的地方皆不可修改!
(1)修饰类:表示该类不能被继承;
(2)修饰方法:表示方法不能被重写;
(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。

16、Math类需要注意的点

       System.out.println("大于该数的最小整数:"+Math.ceil(1.5));
        System.out.println("大于该数的最小整数:"+Math.ceil(-1.5));
        System.out.println("小于该数的最大整数:"+Math.floor(1.5));
        System.out.println("小于该数的最大整数:"+Math.floor(-1.5));
        System.out.println("四舍五入:"+Math.round(1.5));
        System.out.println("四舍五入:"+Math.round(-1.5));

Java基础面试题

17、手动实现一下冒泡排序

冒泡排序

       for(int i=0;i<nums.length-1;i++) {
			for(int j=0;j<nums.length-i-1;j++) {
				if(nums[j]>nums[j+1]) {
					t = nums[j];
					nums[j] = nums[j+1];
					nums[j+1] = t;
				}
			}
		}

18、实现打印指定行数的空心菱形的功能

 System.out.println("请输入行数,行数必须为奇数");
        Scanner scanner = new Scanner(System.in);
        int num = scanner.nextInt();
        if(num%2==0){
            num = num/2;
        }else {
            num = num/2+1;
        }
        // 上半部分
        for (int i = 1; i <= num; i++) {
            // 打印上半部分空格
            for (int j = 1; j <= num - i; j++) {
                System.out.print(" ");
            }
            // 打印原本实心的*部分
            for (int j = 1; j <= 2 * i - 1; j++) {
                // 仅在一行的开头和末尾打印*
                if (j == 1 || j == 2 * i - 1) {
                    System.out.print("*");
                } else {
                    System.out.print(" ");
                }
            }
            System.out.println();
        }
        // 下半部分
        for (int i = 1; i <= num - 1; i++) {
            for (int j = 1; j <= i; j++) {
                System.out.print(" ");
            }
            for (int j = 1; j <= 2 * (num - i - 1) + 1; j++) {
                if (j == 1 || j == 2 * (num - i - 1) + 1) {
                    System.out.print("*");
                } else {
                    System.out.print(" ");
                }
            }
            System.out.println();
        }

Java基础面试题

19、将一个正整数分解质因数。例如:输入 90,打印出90=2 * 3 * 3 * 5。

Scanner scanner = new Scanner(System.in);
        int num = scanner.nextInt();
        System.out.print(num+"=");
        int i =2;
        while (i<=num){
            if (i==num){
                System.out.println(num);
                break;
            }else if(num%i==0){
                System.out.print(i+"*");
                num = num/i;
            }else {
                i++;
            }
        }

Java基础面试题

20、int和Integer的区别?

1、Integer是int的包装类,int则是java的一种基本数据类型
2、Integer变量必须实例化后才能使用,而int变量不需要
3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
4、Integer的默认值是null,int的默认值是0

       Integer a = new Integer(2);
        Integer b = new Integer(2);
        Integer c =  2;
        int d = 2;
        Integer e = 200;
        Integer f = 200;
        System.out.println(a==b);
        System.out.println(a==c);
        System.out.println(a==d);
        System.out.println(c==d);
        System.out.println(e==f);

Java基础面试题

解释:

  • 由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。
  • 非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)
  • Integer变量和int变量比较时,只要两个变量的值是相等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较
  • 对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false

21、char 型变量中能不能存贮一个中文汉字,为什么?

能。

  • 在Java中,char类型变量可以存储一个中文汉字,因为Java中使用的编码是Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个char类型占2个字节(16比特),所以放一个中文是没问题的。但是,如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字。
  • 使用Unicode意味着字符在JVM内部和外部有不同的表现形式,在JVM内部都是Unicode,当这个字符被从JVM内部转移到外部时(例如存入文件系统中),需要进行编码转换。

22、Java 对象初始化顺序?

初始化包含两部分:
1.类的初始化:是指初始化static field 和执行static初始化块
2.对象的创建

顺序:
类初始化 -> 子类构造函数 -> 父类构造函数 -> 实例化成员变量 -> 继续执行子类构造函数的语句

一个类是先初始化static的变量和static句块,然后在分配该类以及父类的成员变量的内存空间,赋予默认值,然后开始调用构造函数。而子类和父类之间,则先初始化和创建父类,然后在初始化和创建子类的。

23、Overload 和 Override 的区别?Overload 的方法是否可以改变返回值的类型?

OverLoad : 重载。表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或类型不同,但与返回值的类型无关)。因此,Overloaded的方法是可以改变返回值的类型重载是一个类中多态性的一种表现。

Override : 重写。表示子类中的方法可以与父类中的某个方法的名称和参数完全相同,通过子类创建的实例对象调用这个方法时,将调用子类中的定义方法,这相当于把父类中定义的那个完全相同的方法给覆盖了。重写是父类与子类之间多态性的一种表现

24、抽象类和接口有什么区别?

相同点:

  • 都是上层的抽象层。
  • 都不能被实例化
  • 都能包含抽象的方法,这些抽象的方法用于描述类具备的功能,但是不比提供具体的实现。

区别:

  1. 抽象类中可以有非抽象的方法,从而避免在子类中重复书写他们,这样可以提高代码的复用性,这是抽象类的优势。接口中只能有抽象的方法
  2. 一个类只能继承一个直接父类,这个父类可以是具体的类也可是抽象类;但是一个类可以实现多个接口
  3. 抽象类中的成员变量类型没有限制,接口中的成员变量只能是 public static final 类型的。
  4. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法
  5. 接口中不允许存在构造方法,因为接口的成员变量都是static final变量,是在编译的时候就完成了初始化操作了,也不需要通过构造方法来进行初始化操作,而接口的方法是抽象方法,和抽象类一样只是声明之后让实现它的类去完善该方法体的;而抽象类可以有构造方法 ,因为抽象类除了有抽象的方法以外,其余地方与普通的类其实是没什么区别的,所以可以通过构造方法完成一些初始化的操作。

25、阐述 final、finally、finalize 的区别。

final:

final作为Java中的关键字可以用于三个地方。用于修饰类、类属性和类方法。
特征:凡是引用final关键字的地方皆不可修改!
(1)修饰类:表示该类不能被继承
(2)修饰方法:表示方法不能被重写
(3)修饰属性:表示变量只能一次赋值以后值不能被修改(常量)。

finally:

  • finally作为异常处理的一部分,它只能在try/catch语句中,并且附带一个语句块表示这段语句最终一定被执行,经常被用在需要释放资源的情况下。在try、catch中,finally 的基础用法,在 return 前会先执行 finally 语句块
  • 但是不排除会有特殊情况的出现导致finally中的语句没有被执行
    例如:
    1) 执行try语句块之前已经返回或抛出异常,所以try对应的finally语句并没有执行。
    2)在 try 语句块中执行了 System.exit (0) 语句,终止了 Java 虚拟机的运行。
    3)当一个线程在执行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed),与其相对应的 finally 语句块可能不会执行。
    4)在线程运行 try 语句块或者 catch 语句块时,突然死机或者断电,finally 语句块肯定不会执行。

finalize: 是Object类中定义的方法,每个对象都会有finalize方法。

  • finalize的作用往往被认为是用来做最后的资源回收。当垃圾回收器将要回收对象所占内存之前被调用,即当一个对象被虚拟机宣告死亡时会先调用它finalize()方法,让此对象处理它生前的最后事情(这个对象可以趁这个时机挣脱死亡的命运)。
  • 这个方法在gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。
  • 特殊情况下,需要程序员实现finalize,当对象被回收的时候释放一些资源,比如:一个socket链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现finalize,关闭这个链接。
  • 一个对象的finalize()方法只会被调用一次,而且finalize()被调用不意味着gc会立即回收该对象,所以有可能调用finalize()后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用finalize(),从而产生问题。

26、Java 中的异常处理机制的简单原理和应用?

异常是在程序中导致程序中断运行的一种指令流。一旦产生异常之后,异常之后的语句将不再执行了,所以现在的程序并没有正确的执行完毕之后就退出了。

异常处理:如果要想对异常进行处理,则必须采用标准的处理格式,处理格式语法如下:

try{
// 有可能发生异常的代码段
}catch(异常类型1 对象名1){
// 异常的处理操作
}catch(异常类型2 对象名2){
// 异常的处理操作
} ...
finally{
// 异常的统一出口
}

try catch的处理流程:

1、 一旦产生异常,则系统会自动产生一个异常类的实例化对象
2、 那么,此时如果异常发生在try语句,则会自动找到匹配的catch语句执行,如果没有在try语句中,则会将异常抛出.
3、 所有的catch根据方法的参数匹配异常类的实例化对象,如果匹配成功,则表示由此catch进行处理。

简单原理

  • 发生了异常(JVM根据异常的情况,创建了一个异常对象,其中包含了异常信息)
  • main未处理,自动将异常抛给main的调用者JVM
  • JVM对异常信息进行了响应(将异常显示到控制台,中断处理)

异常体系结构

异常指的是Exception , Exception类, 在Java中存在一个父类Throwable(可能的抛出)
Throwable存在两个子类:
1.Error:表示的是错误,是JVM发出的错误操作,只能尽量避免,无法用代码处理。
2.Exception:一般表示所有程序中的错误,所以一般在程序中将进行try…catch的处理。

Java基础面试题

27、RuntimeExcepion与Exception的区别?

  • RuntimeException:运行时异常。是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。这种异常不需要编译器来检测。它是Exception的子类。
  • Exception:受检查异常。编译时异常,编译器会分析哪些异常会在执行一个方法或者构造函数的时候抛出。通过throws语句或者try{}cathch{} 语句块来处理检测异常。
  • 一个方法除要捕获异常外,如果它执行的时候可能会抛出RuntimeException的子类,那么它就不需要用throw语句来声明抛出的异常。

28、throws和thow的区别?

throws:

格式:
返回值 方法名称()throws Exception{
}
  • 用来声明一个方法可能产生的所有异常,不做任何处理而是将异常往上传,谁调用我我就抛给谁。
  • throws在方法后边声明异常,其实就是自己不想对异常做出任何的处理,告诉别人自己可能出现的异常,交给别人处理。
  • throw关键字表示在程序中人为的抛出一个异常,因为从异常处理机制来看,所有的异常一旦产生之后,实际上抛出的就是一个异常类的实例化对象,那么此对象也可以由throw直接抛出。

throw:

  • 用来抛出一个具体的异常类型。
  • 自己捕获异常try…catch代码块。

29、try-catch-finally 中,如果 catch 中return 了,finally 还会执行吗?

finally中的代码会执行。

  1. 先计算返回值, 并将返回值存储起来, 等待返回。
  2. 执行finally代码块。
  3. 将之前存储的返回值, 返回出去;(即使需要返回的值在finally语句块中修改了,之前存储的返回值依旧不会改变)。

注意:

  1. 返回值是在finally运算之前就确定了,并且缓存了,不管finally对该值做任何的改变,返回的值都不会改变
  2. finally代码中不建议包含return,因为程序会在上述的流程中提前退出,也就是说返回的值不是try或catch中的值
  3. 如果在try或catch中停止了JVM,则finally不会执行.例如停电- -, 或通过如下代码退出
    JVM:System.exit(0)

30、什么是反射?

Java在将.class字节码文件载入时,JVM将产生一个java.lang.Class对象代表该.class字节码文件,从该Class对象中可以获得类的许多基本信息,这就是反射机制。所以要想完成反射操作,就必须首先认识Class类。所有 Java 类均继承了 Object 类,在 Object 类中定义了一个 getClass() 方法,该方法返回同一个类型为 Class 的对象。JAVA中反射是动态获取信息以及动态调用对象方法的一种反射机制

  • 获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等
  • 获取任意对象的属性,并且能改变对象的属性
  • 调用任意对象的方法
  • 判断任意一个对象所属的类
  • 实例化任意一个类的对象

通过反射可以实现动态装配,降低代码的耦合度;动态代理等。反射的过度使用会严重消耗系统资源。


一天又过去了。感觉自己啥也没干,好难过。最近心情有点复杂,不知道为啥。可能是因为太累了吧,今天中午终于想要认真睡个午觉,结果外面足球场一直传来踢足球的喊叫声,导致也没能睡好,白躺了一中午。只好起来泡个咖啡,继续学习了。

版权声明:程序员胖胖胖虎阿 发表于 2022年11月13日 下午12:08。
转载请注明:Java基础面试题 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...