Java面试宝典2020系列 基础篇(二)

白菜Java自习室 涵盖核心知识

Java面试宝典2020系列 基础篇(一)

Java面试宝典2020系列 基础篇(二)

Java面试宝典2020系列 基础篇(三)

1. 字符型常量和字符串常量的区别?

  1. 形式上: 字符常量是单引号引起的一个字符; 字符串常量是双引号引起的若干个字符

  2. 含义上: 字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)

  3. 占内存大小: 字符常量只占2个字节; 字符串常量占若干个字节(至少一个字符结束标志) (注意: char在Java中占两个字节)

image

2. Java中的String为什么不可变?

什么是不可变对象?

如果一个对象它被创建后,状态不能改变,则这个对象被认为是不可变的。

String是如何实现其对象不可变?

当使用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。但对于引用类型变量而言,它保存的仅仅是一个引用,final只保证这个引用变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。

String类的两个主要成员变量,其中value指向的是一个字符串数组,字符串中的字符就是用这个value变量存储起来的,并且用final修饰,也就是说value一旦赋予初始值之后,value指向的地址就不能再改变了。

虽然value指向的数组是可以改变的,但是String也没有提供相应的方法让我们去修改value指向的数组的元素。然而在StringBuilder中是提供了响应的方法让我们去修改value指向的数组的元素,这也是StringBuilder的字符串序列可变的原因。

 /** The value is used for character storage. */
  private final char value[];
 
  /** Cache the hash code for the string */
  private int hash; // Default to 0

String对象真的不可变吗?

虽然value是final修饰的,只是说明value不能再重新指向其他的引用。但是value指向的数组可以改变,一般情况下我们是没有办法访问到这个value指向的数组的元素。Java反射可以反射出String对象中的value属性,进而改变通过获得的value引用改变数组的结构。

3. String、StringBuffer、StringBuilder的区别是什么?

  1. 操作少量的数据: 适用 String

  2. 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder

  3. 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer

三者的继承结构

image

String 的值是不可变的,这就导致每次对 String 的操作都会生成新的 String 对象,这样不仅效率低下,而且大量浪费有限的内存空间。

当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。

StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

4. Java的自动装箱与拆箱的实现原理?

Java中为每一种基本类型都提供相应的包装类型。

image

自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。

当基础类型与它们的包装类有如下几种情况时,编译器会自动帮我们进行装箱或拆箱。

  1. 进行 = 赋值操作(装箱或拆箱)

  2. 进行+,-,*,/混合运算 (拆箱)

  3. 进行>,<,==比较运算(拆箱)

  4. 调用equals进行比较(装箱)

  5. ArrayList,HashMap等集合类 添加基础类型数据时(装箱)

自动装箱与拆箱的实现原理,以float和Float为例,装箱就是调用Float的valueOf方法new一个Float并赋值,拆箱就是调用Float对象的floatValue方法并赋值返回给float。其他基础类型都是大同小异的,具体可以查看源码。

5. Java的重载和重写的区别是什么?

一、定义上的区别:

1、重载是指不同的函数使用相同的函数名,但是函数的参数个数或类型不同。调用的时候根据函数的参数来区别不同的函数。

2、覆盖(也叫重写)是指在派生类中重新对基类中的虚函数(注意是虚函数)重新实现。即函数名和参数都一样,只是函数的实现体不一样。

二、规则上的不同:

1、重载度的规则:

  1. 必须具有不同的参数列表。
  2. 可以有不同的访问修饰符。
  3. 可以抛出不同的异常。

2、重写方法内的规则:

  1. 参数列表必须完全与被重容写的方法相同,否则不能称其为重写而是重载。
  2. 返回的类型必须一直与被重写的方法的返回类型相同,否则不能称其为重写而是重载。
  3. 访问修饰符的限制一定要大于被重写方法的访问修饰符。
  4. 重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常。

三、类的关系上的区别:

重写是子类和父类之间的关系,是垂直关系;重载是同一个类中方法之间的关系,是水平关系。

6. Java的接口和抽象类的区别是什么?

Java的接口和抽象类都不能实例化对象,都可以包含抽象方法,而且抽象方法必须被继承的类全部实现。

  1. 抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。

  2. 抽象类要被子类继承,接口要被类实现。

  3. 接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现

  4. 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。

  5. 抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。

  6. 抽象方法只能申明,不能实现,接口是设计的结果 ,抽象类是重构的结果

  7. 抽象类里可以没有抽象方法

  8. 如果一个类里有抽象方法,那么这个类只能是抽象类

  9. 抽象方法要被实现,所以不能是静态的,也不能是私有的。

  10. 接口可继承接口,并可多继承接口,但类只能单根继承。

7. Java的对象实体与对象引用有何不同?

对象引用的目的地才是对象的实体。在Java引用对象的赋值中,实际上是对引用的赋值;而对象实体不发生改变。特别能够说明问题的是在引用型对象用 final 关键字修饰的时候,表示引用的确定而不是对象实体的不可变。

final StringBuffer s = new StringBuffer();
s.append("abc");

代码这样是可以的,因为s引用指向确定的 StringBuffer 对象,对象实体变化是可以的。但是如果是 String 的话,就不可以显式的更改了。

8. Java的对象相等与引用相等有何不同?

  1. 对象相等性:即堆中的两个不同对象在意义上相等,其实也就是两个对象在堆中的内容相等。

  2. 引用相等性:即两个对象的引用指向堆中同一个对象,也就是两个对象在栈中的内容相等。

equals()方法

假设有两个对象x,y,判断x.equals(y)有结果false或true,它表示x与y是对象相等的,即两个对象的引用(实际上x、y是两个对象的引用,习惯上我们说成对象)指向堆中同一个对象,即在堆中的内容相同。
String的equals方法是重写了Object的equals方法,保留了Object的equals中的默认比较,默认比较两个对象的地址是否相等(即==比较),如果不等,再去比较两个对象的内容是否相等。

==

它是判断指向两个对象的引用(指针/地址)是相等的,即栈用的内容相等,属于引用相等性。
我们知道在实际的存储内存中有堆内存和栈内存,堆内存中存放的是new创建或者构造器创建的对象,而栈内存中存放的是对象的引用。例如String s=new String(“abc”); s是一个字符串类型的引用,它存放在栈内存中,指向堆内存中的通过new关键字创建的字符串对象,该对象的内容为abc。

9. Java的成员变量与局部变量的区别有哪些?

  1. 代码区域不同:

成员变量声明在所有方法体外部使用范围可覆盖到整个类。

局部变量声明在方法体内部只能在方法体内使用。

  1. 默认值不同:

成员变量有默认值,而局部变量没有默认值必须赋值后才能使用。

  1. 生命周期不同:

成员变量是随着具体new出来的对象存在或销毁只要对象在,成员变量就在,对象销毁成员变量也销毁。

局部变量随着方法执行而产生方法结束而销毁。

  1. 两者在内存中不同:

基础类型:成员变量在堆内存(包括变量名及值),局部变量在栈内存(包括变量名及值)。

引用类型:实例化后,必定指向另一块新的堆内存(局部变量是:栈→堆,成员变量是:堆→堆)。

10. Java的静态方法和实例方法有何不同?

  1. 在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。

  2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法,如果需要调用,则需要先实例化;实例方法则无此限制。

public class Main {
    private int text01;
    private static int text02;
    public void testMethods() {
        text01=1;       //正确,实例方法可以调用实例变量;
        testMethods();  //正确,实例方法可以调用实例方法。
        text02=1;       //正确,实例方法可以调用静态变量;
        testMethods2(); //正确,实例方法可以调用静态方法;
        
    }
    public static void testMethods2(){
        text01=1;        //错误,静态方法不可以调用实例变量;
        testMethods();  //错误,静态方法不可以调用实例方法;
        text02=1;       //正确,静态方法可以调用静态变量;
        testMethods2();  //错误,静态方法可以调用静态方法;
    }
}

你可能感兴趣的:(Java面试宝典2020系列 基础篇(二))