一:构造方法、成员变量初始化以及静态成员变量三者初始化顺序
先后顺序:静态成员变量、成员变量、构造方法
详细的先后顺序:父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、父类非静态变量、父类非静态代码块、父类构造函数、子类非静态变量、子类非静态代码块、子类构造函数
详细解释:
1.静态成员变量(类变量):
- 静态成员变量在类被加载到JVM时进行初始化。这通常发生在第一次使用该类时(例如,创建类的实例或访问类的静态成员时)。
- 如果在声明静态成员变量时为其指定了初始值,那么它将在类加载时直接使用这个初始值。
- 如果在静态初始化块中进行了初始化,那么静态初始化块中的代码将在类加载时执行,以设置静态成员变量的值。
- 静态成员变量在所有实例被创建之前就已经被初始化了。
2.成员变量(实例变量):
- 成员变量在创建类的实例(即对象)时进行初始化。
- 如果在声明成员变量时为其指定了初始值,那么它将在创建对象时直接使用这个初始值。
- 如果没有为成员变量指定初始值,并且该变量是类的基本类型,那么它将自动初始化为该类型的默认值(例如,int为0,boolean为false等)。
- 如果在实例初始化块(非静态初始化块)中进行了初始化,那么这些代码将在创建对象时执行,以设置成员变量的值。
- 实例初始化块在构造方法之前执行。
3.构造方法:
- 构造方法是在创建对象时调用的,用于初始化对象的状态。
- 在构造方法中,可以访问和修改对象的成员变量。
- 如果存在多个构造方法,则可以根据需要选择性地调用它们来创建对象。
- 构造方法在实例初始化块之后执行。
例如:
java
public class InitializationOrder {
// 静态成员变量
static int staticVar = 0;
static {
staticVar = 1; // 静态初始化块
System.out.println("Static block: " + staticVar);
}
// 成员变量
int instanceVar = 0;
{
instanceVar = 2; // 实例初始化块
System.out.println("Instance block: " + instanceVar);
}
// 构造方法
public InitializationOrder() {
instanceVar = 3;
System.out.println("Constructor: " + instanceVar);
}
public static void main(String[] args) {
new InitializationOrder(); // 首次使用类,触发类加载和静态初始化块
new InitializationOrder(); // 创建第二个对象,只触发实例初始化和构造方法
}
}
二:接口和抽象类的相同点和区别
相同点:
- 都可以被用来定义抽象方法,需要子类来实现这些方法。
- 都不能直接实例化,而是需要通过子类来实现并实例化。
区别:
- 接口可以定义方法但不能包含实现,而抽象类可以包含具体的方法实现。
- 类可以实现多个接口,但只能继承一个抽象类。
- 接口中的方法默认是public的,而抽象类中的方法可以有不同的访问修饰符。
- 接口中不能包含成员变量,而抽象类可以包含成员变量。
- 接口用于描述行为,而抽象类更多用于描述对象的特性
三:为什么Java不支持多重继承
1.复杂性管理:
多重继承会引入复杂性,特别是在解决方法名冲突(diamond problem)和继承路径不明确的情况下。这可能导致代码理解和维护的困难。
2.歧义性:
当一个类继承自多个父类时,如果这些父类中有相同的方法或字段,编译器无法确定应该使用哪个版本,造成歧义。
3.设计哲学:
Java语言的设计哲学是简洁和易于理解。限制类只能单一继承可以帮助开发人员更容易地理解和推断类的行为。
4.接口的替代:
Java提供接口来实现多态性和多继承类似的功能,使得类可以实现多个接口,从而避免了多继承带来的复杂性和潜在问题。
四:final、finally、和finalize的区别是什么
-
final用于声明属性、方法和类,分别表示属性不可变、方法不可覆盖、类不可继承。
-
finally作为异常处理的一部分,只能在try/catch语句中使用,finally附带一个语句块用来表示这个语句最终一定被执行,经常被用在需要释放资源的情况下。
-
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的finalize()方法。当垃圾回收器准备好释放对象占用空间时,首先会调用finalize()方法,并在下一次垃圾回收动作发生时真正回收对象占用的内存。
五:String和StringBuffer有什么区别
String用于字符串操作,属于不可变类。String对象一旦被创建,其值将不能被改变。
StringBuffer是可变类,当对象创建后,仍然可以对其值进行修改。
六:为什么要把String设计为不可变变量?
-
节省空间:字符串常量存储在JVM的字符串池中可以被用户共享。
-
提高效率: String会被不同线程共享,是线程安全的。在涉及多线程操作中不需要同步操作。
-
安全:String常被用于用户名、密码、文件名等使用,由于其不可变,可避免黑客行为对其恶意修改。
七:序列化是什么
序列化是一种将对象转换成字节序列的过程,用于解决在对对象流进行读写操作时所引发的问题。序列化可以将对象的状态写在流里进行网络传输,或者保存到文件、数据库等系统里,并在需要的时候把该流读取出来重新构造成一个相同的对象。
八:Java的反射机制是什么
Java反射机制是指在程序的运行过程中可以构造任意一个类的对象、获取任意一个类的成员变量和成员方法、获取任意一个对象所属的类信息、调用任意一个对象的属性和方法。反射机制使得Java具有动态获取程序信息和动态调用对象方法的能力。
可以通过以下类调用反射API:
Class类:可获得类属性方法
Field类:获得类的成员变量
Method类:获取类的方法信息
Construct类:获取类的构造方法等信息
九:自动拆箱和自动装箱
对于Java基本数据类型,均对应一个包装类。装箱就是自动将基本数据类型转换为包装器类型,如int->Integer。
拆箱就是自动将包装器类型转换为基本数据类型,如Integer->int。
十:简述内部类及其作用
成员内部类:作为成员对象的内部类。可以访问private及以上外部类的属性和方法。外部类想要访问内部类属性或方法时,必须要创建一个内部类对象,然后通过该对象访问内部类的属性或方法。外部类也可访问private修饰的内部类属性。局部内部类:存在于方法中的内部类。访问权限类似局部变量,只能访问外部类的final变量。匿名内部类:只能使用一次,没有类名,只能访问外部类的final变量。静态内部类:类似类的静态成员变量。
十一:简述Java的HashMap
JDK8 之前底层实现是数组 + 链表,JDK8 改为数组 + 链表/红黑树。主要成员变量包括存储数据的table 数组、元素数量 size、加载因子 loadFactor。
HashMap 中数据以键值对的形式存在,键对应的 hash 值用来计算数组下标,如果两个元素 key 的hash 值一样,就会发生哈希冲突,被放到同一个链表上。
table 数组记录 HashMap 的数据,每个下标对应一条链表,所有哈希冲突的数据都会被存放到同一条链表,Node/Entry 节点包含四个成员变量:key、value、next 指针和 hash 值。在JDK8后链表超过8会转化为红黑树。
若当前数据/总数据容量>负载因子,Hashmap将执行扩容操作。默认初始化容量为 16,扩容容量必须是 2 的幂次方、最大容量为 1