Java类初始化

一、背景

平时在代码里,会发现除了显式地set方法外,还有多种方式给类中的字段塞值。

那有哪些设置方式?这些方式的优先级是什么样的?最终生效的是哪个值呢?

二、结论

以下步骤按顺序执行

  1. 静态字段和静态初始化块 - 类加载时执行
  2. 默认值初始化 - JVM在初始化一个对象时,会为实例变量提供默认值(“零值”)如int=0, 引用=null等
  3. 实例字段和实例初始化块 - JVM设置零值后执行(有多个则按照代码顺序执行
  4. 构造函数

可以看一个例子:

执行main方法,代码执行结果:

static block a=2

block1 a=3

block2 a=4

Init a=5

block1 a=6

block2 a=7

init a=8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Main {
public static void main(String[] args) {
MyClass myclass = new MyClass();
MyClass myclass2 = new MyClass();
}

static class MyClass {
static int a = 1;

static {
a++;
System.out.println("static block a=" + a);
}

{
a++;
System.out.println("block1 a=" + a);
}

{
a++;
System.out.println("block2 a=" + a);
}

public MyClass() {
a++;
System.out.println("init a=" + a);
}
}
}

三、扩展

而众所周知,在代码里 我们并不只通过new 来得到一个对象实例,那么其他实例化方式会有什么影响呢?

  1. 反射

没啥影响。和new一样(反射实际上就是调用构造器)

  1. 反序列化

这里分为两类,

第三方序列化器 如fastJSON 会通过反射来获得类的实例

然后根据文本内容来设置字段值。

JDK自带反序列化工具 不会调用构造函数和对象实例逻辑。

如下代码块执行结果(输出是myClass1对象实例时产生的

block a=2
init a=3
10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
MyClass myClass1 = new MyClass();
myClass1.a = 10;

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(myClass1);
oos.close();
bos.close();

byte[] bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
MyClass myClass = (MyClass) ois.readObject();
System.out.println(myClass.a);
ois.close();
bis.close();
}

static class MyClass implements Serializable {
int a = 1;

{
a++;
System.out.println("block a=" + a);
}

public MyClass() {
a++;
System.out.println("init a=" + a);
}
}
  1. clone

默认clone实现也不会调用构造函数

还是上代码

block a=2
init a=3
3
4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
MyClass myClass = new MyClass();
MyClass clone = (MyClass) myClass.clone();

myClass.a = 4;

System.out.println(clone.a);
System.out.println(myClass.a);

}

static class MyClass implements Cloneable {
int a = 1;

{
a++;
System.out.println("block a=" + a);
}

public MyClass() {
a++;
System.out.println("init a=" + a);
}

@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
}
  1. Spring

@component:被注入到容器里的对象,也是通过反射获得

@PostConstruct 在Spring框架创建bean的过程中被框架调用,如果不在IOC框架中,声明也没有作用。


Java类初始化
https://yzaf.top/2024/java/Java-init/
作者
why
发布于
2024年2月22日
许可协议