一、背景
平时在代码里,会发现除了显式地set方法外,还有多种方式给类中的字段塞值。
那有哪些设置方式?这些方式的优先级是什么样的?最终生效的是哪个值呢?
二、结论
以下步骤按顺序执行
- 静态字段和静态初始化块 - 类加载时执行
- 默认值初始化 - JVM在初始化一个对象时,会为实例变量提供默认值(“零值”)如int=0, 引用=null等
- 实例字段和实例初始化块 - JVM设置零值后执行(有多个则按照代码顺序执行
- 构造函数
可以看一个例子:
执行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 来得到一个对象实例,那么其他实例化方式会有什么影响呢?
- 反射
没啥影响。和new一样(反射实际上就是调用构造器)
- 反序列化
这里分为两类,
第三方序列化器 如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); } }
|
- 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(); } } }
|
- Spring
@component:被注入到容器里的对象,也是通过反射获得
@PostConstruct 在Spring框架创建bean的过程中被框架调用,如果不在IOC框架中,声明也没有作用。