Java父类与子类中静态代码块 实例代码块 静态变量 实例变量 构造函数执行顺序
2016-11-03 20:16
1006 查看
实例化子类时,父类与子类中的静态代码块、实例代码块、静态变量、实例变量、构造函数的执行顺序是怎样的?
代码执行的优先级为:
firest:静态部分
second:实例化过程
详细顺序为:
1.父类静态代码块与父类静态变量赋值(取决于代码书写顺序)
2.子类静态代码块与子类静态变量赋值(取决于代码书写顺序)
3.父类实例变量赋值与父类代码块(取决于代码书写顺序)
4.父类构造函数
5.子类实例变量赋值与子类代码块(取决于代码书写顺序)
6.子类构造函数
在JVM加载完类以后,类在被使用的时候初始化,静态部分只在类初始化的时候执行一次。
测试代码:
调用代码:
执行结果:
-------------2018.1.5---------------
我们通过反编译工具(IntelliJ IDEA或JD-JUI)反编译一下class文件看能发现什么有意思的东西
测试代码:
1、在 Father.java 中静态变量a、b的赋值操作写在了静态代码块的前面,静态变量z、c、d的赋值操作写在了静态代码块的后面,查看反编译之后的代码,可见静态变量z、c、d的赋值被写在了静态代码块中。
2、在 Father.java 中实例变量aa的赋值操作写在了实例代码块的前面,实例变量bb的赋值操作写在了实例代码块的后面,查看反编译之后的代码,可见代码块中的操作和实例变量b的赋值都被写在了构造函数中。
下面分情况删除一些代码,我们看一下反编译之后的不同
情况一:删掉静态代码块,此时的 Father.java 代码:
任何一个静态变量的赋值都没有被写在静态代码块中
情况二:删掉实例代码块,此时的 Father.java 代码:
可以发现:
任何一个实例变量的赋值都没有被写在构造函数中
情况三:删掉静态变量,此时的 Father.java 代码:
情况四:删掉实例变量,此时的 Father.java 代码:
用 IntelliJ IDEA 打开上述代码编译之后的 Father.class :
总结一下:
一、静态代码块 与 静态变量赋值 在反编译成的代码中的位置
1、如果存在静态代码块,并且有静态变量的赋值写在了静态代码块的下面,那么这样的静态变量最终的赋值操作会被写在静态代码块中
2、如果存在多个静态代码块,最终会合并为一个
二、实例变量赋值 与 实例代码块 在反编译成的代码中的位置
1、如果存在实例代码块,并且有实例变量的赋值写在了实例代码块的下面,那么实例代码块中的操作和这样的实例变量最终的赋值操作会被写在构造函数中
2、实例代码块中的操作都会被写在构造函数中
3、如果存在多个实例代码块,都会被写在构造函数中
了解了变量赋值与代码块的执行顺序,就能从代码执行的角度解释有时候在代码块中使用变量时为什么会提示“非法前向引用(illegal forward reference)”
反编译工具 IntelliJ IDEA 的BUG
根据 静态代码块 与 静态变量赋值 的执行顺序取决于代码书写顺序这一原则,测试Father.java
可见由IntelliJ IDEA反编译出的代码的执行顺序已经不是原来类该有的顺序了,IntelliJ IDEA反编译处理的 实例代码块
与 实例变量的执行顺序也有类似问题。
经过测试,用 JD-JUI 反编译也会有上述的问题,但是兼容性比 IntelliJ IDEA 好一些。
所以,这两款反编译工具反编译出来的代码并不能保证跟原来类的逻辑完全一致。
使用javap命令可以获取类初始化时代码的正确执行顺序。
代码执行的优先级为:
firest:静态部分
second:实例化过程
详细顺序为:
1.父类静态代码块与父类静态变量赋值(取决于代码书写顺序)
2.子类静态代码块与子类静态变量赋值(取决于代码书写顺序)
3.父类实例变量赋值与父类代码块(取决于代码书写顺序)
4.父类构造函数
5.子类实例变量赋值与子类代码块(取决于代码书写顺序)
6.子类构造函数
在JVM加载完类以后,类在被使用的时候初始化,静态部分只在类初始化的时候执行一次。
测试代码:
class Father { Father() { LogUtil.log(System.currentTimeMillis() + " ------ 父类构造函数"); } static { LogUtil.log(System.currentTimeMillis() + " ------ 父类静态代码块"); } long x = getTime(" ------ 父类实例变量赋值"); { long time = System.currentTimeMillis(); LogUtil.log(time + " ------ 父类代码块"); } static long y = getTime(" ------ 父类静态变量赋值"); static long getTime(String who) { long time = System.currentTimeMillis(); LogUtil.log(time + who); return time; } } class Child extends Father { Child() { LogUtil.log(System.currentTimeMillis() + " ------ 子类构造函数"); } static long y = getTime(" ------ 子类静态变量赋值"); static { LogUtil.log(System.currentTimeMillis() + " ------ 子类静态代码块"); } { long time = System.currentTimeMillis(); LogUtil.log(time + " ------ 子类代码块"); } long x = getTime(" ------ 子类实例变量赋值"); static long getTime(String who) { long time = System.currentTimeMillis(); LogUtil.log(time + who); return time; } }
调用代码:
new Thread(new Runnable() { @Override public void run() { new Child(); LogUtil.log("分隔符 ------ 分隔符"); new Child(); } }).start();
执行结果:
11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523353 ------ 父类静态代码块 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523353 ------ 父类静态变量赋值 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523353 ------ 子类静态变量赋值 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523353 ------ 子类静态代码块 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523353 ------ 父类实例变量赋值 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523353 ------ 父类代码块 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523353 ------ 父类构造函数 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523353 ------ 子类代码块 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523353 ------ 子类实例变量赋值 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523353 ------ 子类构造函数 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 分隔符 ------ 分隔符 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523353 ------ 父类实例变量赋值 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523353 ------ 父类代码块 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523353 ------ 父类构造函数 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523354 ------ 子类代码块 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523354 ------ 子类实例变量赋值 11-03 20:02:03.350 7533-7881/? E/AKADDEMO: 1478174523354 ------ 子类构造函数
-------------2018.1.5---------------
我们通过反编译工具(IntelliJ IDEA或JD-JUI)反编译一下class文件看能发现什么有意思的东西
测试代码:
class Father { Father() { System.out.print(System.currentTimeMillis() + " ------ 父类构造函数" + '\n'); } private static long a = getTime(" -----a 父类静态变量赋值" + '\n'); private final static long b = getTime(" -----b 父类静态变量赋值" + '\n'); static { System.out.print(System.currentTimeMillis() + " ------ 父类静态代码块" + '\n'); } long aa = getTime(" ----aa 父类实例变量赋值" + '\n'); { System.out.print(System.currentTimeMillis() + " ------ 父类代码块" + '\n'); } long bb = getTime(" ----bb 父类实例变量赋值" + '\n'); static long z = getTime(" -----z 父类静态变量赋值" + '\n'); static long getTime(String who) { long time = System.currentTimeMillis(); System.out.print(time + who); return time; } private static long c = getTime(" -----c 父类静态变量赋值" + '\n'); private final static long d = getTime(" -----d 父类静态变量赋值" + '\n'); }调用代码:
public static void main(String[] args) { new Father(); }执行结果:
1515134833612 -----a 父类静态变量赋值 1515134833614 -----b 父类静态变量赋值 1515134833614 ------ 父类静态代码块 1515134833614 -----z 父类静态变量赋值 1515134833614 -----c 父类静态变量赋值 1515134833614 -----d 父类静态变量赋值 1515134833614 ----aa 父类实例变量赋值 1515134833614 ------ 父类代码块 1515134833614 ----bb 父类实例变量赋值 1515134833614 ------ 父类构造函数用 IntelliJ IDEA 打开上述代码编译之后的 Father.class :
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.ak.torch.videoplayer.disassamble; public class Father { private static long a = getTime(" -----a 父类静态变量赋值\n"); private static final long b = getTime(" -----b 父类静态变量赋值\n"); long aa = getTime(" ----aa 父类实例变量赋值\n"); long bb; static long z; private static long c; private static final long d; Father() { System.out.print(System.currentTimeMillis() + " ------ 父类代码块" + '\n'); this.bb = getTime(" ----bb 父类实例变量赋值\n"); System.out.print(System.currentTimeMillis() + " ------ 父类构造函数" + '\n'); } static long getTime(String var0) { long var1 = System.currentTimeMillis(); System.out.print(var1 + var0); return var1; } static { System.out.print(System.currentTimeMillis() + " ------ 父类静态代码块" + '\n'); z = getTime(" -----z 父类静态变量赋值\n"); c = getTime(" -----c 父类静态变量赋值\n"); d = getTime(" -----d 父类静态变量赋值\n"); } }可以发现:
1、在 Father.java 中静态变量a、b的赋值操作写在了静态代码块的前面,静态变量z、c、d的赋值操作写在了静态代码块的后面,查看反编译之后的代码,可见静态变量z、c、d的赋值被写在了静态代码块中。
2、在 Father.java 中实例变量aa的赋值操作写在了实例代码块的前面,实例变量bb的赋值操作写在了实例代码块的后面,查看反编译之后的代码,可见代码块中的操作和实例变量b的赋值都被写在了构造函数中。
下面分情况删除一些代码,我们看一下反编译之后的不同
情况一:删掉静态代码块,此时的 Father.java 代码:
class Father { Father() { System.out.print(System.currentTimeMillis() + " ------ 父类构造函数" + '\n'); } private static long a = getTime(" -----a 父类静态变量赋值" + '\n'); private final static long b = getTime(" -----b 父类静态变量赋值" + '\n'); long aa = getTime(" ----aa 父类实例变量赋值" + '\n'); { System.out.print(System.currentTimeMillis() + " ------ 父类代码块" + '\n'); } long bb = getTime(" ----bb 父类实例变量赋值" + '\n'); static long z = getTime(" -----z 父类静态变量赋值" + '\n'); static long getTime(String who) { long time = System.currentTimeMillis(); System.out.print(time + who); return time; } private static long c = getTime(" -----c 父类静态变量赋值" + '\n'); private final static long d = getTime(" -----d 父类静态变量赋值" + '\n'); }用 IntelliJ IDEA 打开上述代码编译之后的 Father.class :
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.ak.torch.videoplayer.disassamble; public class Father { private static long a = getTime(" -----a 父类静态变量赋值\n"); private static final long b = getTime(" -----b 父类静态变量赋值\n"); long aa = getTime(" ----aa 父类实例变量赋值\n"); long bb; static long z = getTime(" -----z 父类静态变量赋值\n"); private static long c = getTime(" -----c 父类静态变量赋值\n"); private static final long d = getTime(" -----d 父类静态变量赋值\n"); Father() { System.out.print(System.currentTimeMillis() + " ------ 父类代码块" + '\n'); this.bb = getTime(" ----bb 父类实例变量赋值\n"); System.out.print(System.currentTimeMillis() + " ------ 父类构造函数" + '\n'); } static long getTime(String var0) { long var1 = System.currentTimeMillis(); System.out.print(var1 + var0); return var1; } }可以发现:
任何一个静态变量的赋值都没有被写在静态代码块中
情况二:删掉实例代码块,此时的 Father.java 代码:
class Father { Father() { System.out.print(System.currentTimeMillis() + " ------ 父类构造函数" + '\n'); } private static long a = getTime(" -----a 父类静态变量赋值" + '\n'); private final static long b = getTime(" -----b 父类静态变量赋值" + '\n'); static { System.out.print(System.currentTimeMillis() + " ------ 父类静态代码块" + '\n'); } long aa = getTime(" ----aa 父类实例变量赋值" + '\n'); long bb = getTime(" ----bb 父类实例变量赋值" + '\n'); static long z = getTime(" -----z 父类静态变量赋值" + '\n'); static long getTime(String who) { long time = System.currentTimeMillis(); System.out.print(time + who); return time; } private static long c = getTime(" -----c 父类静态变量赋值" + '\n'); private final static long d = getTime(" -----d 父类静态变量赋值" + '\n'); }用 IntelliJ IDEA 打开上述代码编译之后的 Father.class :
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.ak.torch.videoplayer.disassamble; public class Father { private static long a = getTime(" -----a 父类静态变量赋值\n"); private static final long b = getTime(" -----b 父类静态变量赋值\n"); long aa = getTime(" ----aa 父类实例变量赋值\n"); long bb = getTime(" ----bb 父类实例变量赋值\n"); static long z; private static long c; private static final long d; Father() { System.out.print(System.currentTimeMillis() + " ------ 父类构造函数" + '\n'); } static long getTime(String var0) { long var1 = System.currentTimeMillis(); System.out.print(var1 + var0); return var1; } static { System.out.print(System.currentTimeMillis() + " ------ 父类静态代码块" + '\n'); z = getTime(" -----z 父类静态变量赋值\n"); c = getTime(" -----c 父类静态变量赋值\n"); d = getTime(" -----d 父类静态变量赋值\n"); } }
可以发现:
任何一个实例变量的赋值都没有被写在构造函数中
情况三:删掉静态变量,此时的 Father.java 代码:
class Father { Father() { System.out.print(System.currentTimeMillis() + " ------ 父类构造函数" + '\n'); } static { System.out.print(System.currentTimeMillis() + " ------ 父类静态代码块" + '\n'); } long aa = getTime(" ----aa 父类实例变量赋值" + '\n'); { System.out.print(System.currentTimeMillis() + " ------ 父类代码块" + '\n'); } long bb = getTime(" ----bb 父类实例变量赋值" + '\n'); static long getTime(String who) { long time = System.currentTimeMillis(); System.out.print(time + who); return time; } }用 IntelliJ IDEA 打开上述代码编译之后的 Father.class :
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.ak.torch.videoplayer.disassamble; public class Father { long aa = getTime(" ----aa 父类实例变量赋值\n"); long bb; Father() { System.out.print(System.currentTimeMillis() + " ------ 父类代码块" + '\n'); this.bb = getTime(" ----bb 父类实例变量赋值\n"); System.out.print(System.currentTimeMillis() + " ------ 父类构造函数" + '\n'); } static long getTime(String var0) { long var1 = System.currentTimeMillis(); System.out.print(var1 + var0); return var1; } static { System.out.print(System.currentTimeMillis() + " ------ 父类静态代码块" + '\n'); } }
情况四:删掉实例变量,此时的 Father.java 代码:
class Father { Father() { System.out.print(System.currentTimeMillis() + " ------ 父类构造函数" + '\n'); } private static long a = getTime(" -----a 父类静态变量赋值" + '\n'); private final static long b = getTime(" -----b 父类静态变量赋值" + '\n'); static { System.out.print(System.currentTimeMillis() + " ------ 父类静态代码块" + '\n'); } { System.out.print(System.currentTimeMillis() + " ------ 父类代码块" + '\n'); } static long z = getTime(" -----z 父类静态变量赋值" + '\n'); static long getTime(String who) { long time = System.currentTimeMillis(); System.out.print(time + who); return time; } private static long c = getTime(" -----c 父类静态变量赋值" + '\n'); private final static long d = getTime(" -----d 父类静态变量赋值" + '\n'); }
用 IntelliJ IDEA 打开上述代码编译之后的 Father.class :
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.ak.torch.videoplayer.disassamble; public class Father { private static long a = getTime(" -----a 父类静态变量赋值\n"); private static final long b = getTime(" -----b 父类静态变量赋值\n"); static long z; private static long c; private static final long d; Father() { System.out.print(System.currentTimeMillis() + " ------ 父类代码块" + '\n'); System.out.print(System.currentTimeMillis() + " ------ 父类构造函数" + '\n'); } static long getTime(String var0) { long var1 = System.currentTimeMillis(); System.out.print(var1 + var0); return var1; } static { System.out.print(System.currentTimeMillis() + " ------ 父类静态代码块" + '\n'); z = getTime(" -----z 父类静态变量赋值\n"); c = getTime(" -----c 父类静态变量赋值\n"); d = getTime(" -----d 父类静态变量赋值\n"); } }
总结一下:
一、静态代码块 与 静态变量赋值 在反编译成的代码中的位置
1、如果存在静态代码块,并且有静态变量的赋值写在了静态代码块的下面,那么这样的静态变量最终的赋值操作会被写在静态代码块中
2、如果存在多个静态代码块,最终会合并为一个
二、实例变量赋值 与 实例代码块 在反编译成的代码中的位置
1、如果存在实例代码块,并且有实例变量的赋值写在了实例代码块的下面,那么实例代码块中的操作和这样的实例变量最终的赋值操作会被写在构造函数中
2、实例代码块中的操作都会被写在构造函数中
3、如果存在多个实例代码块,都会被写在构造函数中
了解了变量赋值与代码块的执行顺序,就能从代码执行的角度解释有时候在代码块中使用变量时为什么会提示“非法前向引用(illegal forward reference)”
反编译工具 IntelliJ IDEA 的BUG
根据 静态代码块 与 静态变量赋值 的执行顺序取决于代码书写顺序这一原则,测试Father.java
class Father { static { bb = getTime(" ----bb 父类实例变量" + '\n'); } static long aa = getTime(" ----aa 父类实例变量" + '\n'); static long bb; static long getTime(String who) { long time = System.currentTimeMillis(); System.out.print(time + who); return time; } }执行结果:
1515149709262 ----bb 父类实例变量 1515149709262 ----aa 父类实例变量用 IntelliJ IDEA 打开上述代码编译之后的 Father.class
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.ak.torch.videoplayer.disassamble; public class Father { static long aa = getTime(" ----aa 父类实例变量\n"); static long bb = getTime(" ----bb 父类实例变量\n"); public Father() { } static long getTime(String var0) { long var1 = System.currentTimeMillis(); System.out.print(var1 + var0); return var1; } }将此反编译之后的代码重新搞一个java文件,然后编译运行:
1515151279918 ----aa 父类实例变量 1515151279919 ----bb 父类实例变量
可见由IntelliJ IDEA反编译出的代码的执行顺序已经不是原来类该有的顺序了,IntelliJ IDEA反编译处理的 实例代码块
与 实例变量的执行顺序也有类似问题。
经过测试,用 JD-JUI 反编译也会有上述的问题,但是兼容性比 IntelliJ IDEA 好一些。
所以,这两款反编译工具反编译出来的代码并不能保证跟原来类的逻辑完全一致。
使用javap命令可以获取类初始化时代码的正确执行顺序。
相关文章推荐
- Java父类与子类中静态代码块 实例代码块 静态变量 实例变量 构造函数执行顺序
- 关于静态变量,静态代码块,实例变量,实例代码块,构造函数的执行顺序的总结
- JAVA父类子类静态代码块、静态变量、构造方法的执行顺序
- 【JAVA】探究JAVA父类子类静态代码块、静态变量、构造方法的执行顺序
- Java初始化顺序总结及其程序执行过程图- 静态变量、静态代码块、成员变量、构造函数
- java继承中父类和子类静态、非静态代码块,构造函数,静态方法的执行顺序
- 关于静态变量,静态代码块,实例变量,实例代码块,构造函数的执行顺序
- java中父类和子类中的静态代码块、非静态代码块和构造器的执行顺序
- Java 父类的构造函数执行要早于子类的实例变量初始化
- java 中的 成员变量、局部变量、静态变量、类变量、非静态变量、实例变量、向前引用、非法向前引用、静态代码块、非静态代码块 执行时机
- Java初始化顺序总结 - 静态变量、静态代码块、成员变量、构造函数
- JAVA中父类与子类静态代码块、非静态代码块、构造函数的加载顺序
- java 中的 成员变量、局部变量、静态变量、类变量、非静态变量、实例变量、向前引用、非法向前引用、静态代码块、非静态代码块 执行时机
- Java 静态变量、非静态代码块、构造函数的执行顺序
- java 父类访问子类对象的实例变量 继承过程中的执行顺序
- java 父类子类静态成员,实例成员,构造函数初始化的顺序
- 继承中,父类与子类都有静态代码块和构造函数的时执行顺序
- [C#]父类与子类的静态成员变量、实例成员变量、构造函数的执行顺序
- java中 静态成员、实例成员、构造方法在子类和父类中的执行顺序
- Java初始化顺序总结 - 静态变量、静态代码块、成员变量、构造函数