java变量,初始化快,构造函数的执行顺序
转自 http://blog.csdn.net/macheng365/article/details/6403050
——————————————————————————————————————————————————
对于JAVA中类的初始化是一个很基础的问题,其中的一些问题也是易被学习者所忽略。当在编写代码的时候碰到时,常被这些问题引发的错误,感觉莫名其妙。而且现在许多大公司的面试题,对于这方面的考查也是屡试不爽。不管基于什么原因,我认为,对于java类中的初始化问题,有必要深入的了解。Java类的初始化,其实就是它在JVM的初始化问题(类加载的问题),对于它在JVM中的初始化是一个相当复杂的问题,是给专家们来探讨的,所以在这里我只是对一些容易忽略的问题,发表一下个人观点:
1,在一个类的内部(不考虑它是另一个类的派生类):很多人认为,类的成员变量是在构造方法调用之后再初始化的,先不考虑这种观点的正确性,先看一下下面的代码:
- class Test01...{
- public Test01(int i)...{
- System.out.println("Test01 of constractor : " + i);
- }
- }
- public class Test02 ...{
- private Test01 t1 = new Test01(1);
- private int n = 10;
- public Test02()...{
- System.out.println("Test02 of constructor : " + n);
- }
- private Test01 t2 = new Test01(2);
- public static void main(String[] args) ...{
- Test02 test = new Test02();
- }
- }
- 输出的结果为:
- Test01 of constractor : 1
- Test01 of constractor : 2
- Test02 of constructor : 10
class Test01...{ public Test01(int i)...{ System.out.println("Test01 of constractor : " + i); } } public class Test02 ...{ private Test01 t1 = new Test01(1); private int n = 10; public Test02()...{ System.out.println("Test02 of constructor : " + n); } private Test01 t2 = new Test01(2); public static void main(String[] args) ...{ Test02 test = new Test02(); } } 输出的结果为: Test01 of constractor : 1 Test01 of constractor : 2 Test02 of constructor : 10
通过输出,可见当生成Test02的实例test时,它并不是首先调用其构造方法而是先是成员变量的初始化,而且成员的初始化的顺序以成员变量的定义顺序有关,先定义的先初始化,初始化后再调用构造方法。其实成员变量的初始化,在类的所有方法调用之前进行,包括构造方法
当类中有Static 修饰的成员呢?测试下面一段代码:
- public class Test03 ...{
- private int i1 = printCommon();
- private static int i2 = printStatic();
- public Test03()...{
- }
- public static int printCommon()...{
- System.out.println("i1 is init!");
- return 1;
- }
- public static int printStatic()...{
- System.out.println("i2 is init!");
- return 2;
- }
- public static void main(String[] args) ...{
- Test03 t = new Test03();
- }
- }
- 输出结果为:
- i2 is init!
- i1 is init!
public class Test03 ...{ private int i1 = printCommon(); private static int i2 = printStatic(); public Test03()...{ } public static int printCommon()...{ System.out.println("i1 is init!"); return 1; } public static int printStatic()...{ System.out.println("i2 is init!"); return 2; } public static void main(String[] args) ...{ Test03 t = new Test03(); } } 输出结果为: i2 is init! i1 is init!
可见static的成员比普通的成员变量先初始化。
我们都知道,如果一个类的成员变量没有在定义时,系统会给予系统默认的值,有=号的就直接给予右值,系统在给予初值和=号给予值这2中方式,在执行时间上有先后吗?为了测试,我编写了如下代码:
- public class Test04 ...{
- private static Test04 t1 = new Test04();
- private static int i1;
- private static int i2 = 2;
- public Test04()...{
- i1++;
- i2++;
- }
- public static void main(String[] args) ...{
- Test04 t2 = new Test04();
- System.out.println("t2.i1 = " + t2.i1);
- System.out.println("t2.i2 = " + t2.i2);
- }
- }
- 我们先预计一下输出,可能有几种答案:2和3,3和3,2和2
- 执行代码后:
- t2.i1 = 2
- t2.i2 = 3
public class Test04 ...{ private static Test04 t1 = new Test04(); private static int i1; private static int i2 = 2; public Test04()...{ i1++; i2++; } public static void main(String[] args) ...{ Test04 t2 = new Test04(); System.out.println("t2.i1 = " + t2.i1); System.out.println("t2.i2 = " + t2.i2); } } 我们先预计一下输出,可能有几种答案:2和3,3和3,2和2 执行代码后: t2.i1 = 2 t2.i2 = 3
为什么是2和3呢?其实代码的执行顺序是这样的:首先执行给t1,i1,i2分别给予初始值null,0,0,再执行
Test04 t1 =new Test04(),这样i1++,i2++被执行,i1,i2都变为1,执行完毕后接着执行int i1; i1,i2的值仍然是1,1,当执行int i2 = 2时i2被赋予了值,即i1 = 1,i2=2;再执行Test04 t2 = new Test04(),i1,i2再执行++,此时i1 =2,i2 =3,输出i1,i2,结果就是:t2.i1 = 2,t2.i2 = 3。 通过上面的代码我们可以认为系统默认值的给予比通过等号的赋予先执行。
2,一个类还有上层的类,即父类:
当生成一个子类时,大家到知道会调用父类的构造方法。如果子类和父类中都有Static的成员变量呢,其实我们在深入分析一个类的内部初始化后,对于存在父类的类的初始化其实原理都一样,具体以下面的代码为例:
- class SuperClass ...{
- static...{
- System.out.println("SuperClass of static block");
- }
- public SuperClass()...{
- System.out.println("SuperClass of constracutor");
- }
- }
- public class SubClass extends SuperClass...{
- static...{
- System.out.println("SubClass of static block");
- }
- public SubClass()...{
- System.out.println("SubClass of constracutor");
- }
- public static void main(String[] args)...{
- SuperClass t = new SubClass();
- }
- }
- 输出结果:
- SuperClass of static block
- SubClass of static block
- SuperClass of constracutor
- SubClass of constracutor
class SuperClass ...{ static...{ System.out.println("SuperClass of static block"); } public SuperClass()...{ System.out.println("SuperClass of constracutor"); } } public class SubClass extends SuperClass...{ static...{ System.out.println("SubClass of static block"); } public SubClass()...{ System.out.println("SubClass of constracutor"); } public static void main(String[] args)...{ SuperClass t = new SubClass(); } } 输出结果: SuperClass of static block SubClass of static block SuperClass of constracutor SubClass of constracutor
可见当父类,和子类有Static时,先初始化Static,再初始化子类的Static,再初始化父类的其他成员变量->父类构造方法->子类其他成员变量->子类的构造方法。
父类上层还有父类时,总是先执行最顶层父类的Static-->派生类Static-->派生类Static-->.......-->子类Static-->顶层父类的其他成员变量-->父类构造方法--> 派生类的其他成员变量 --> 派生类构造方法--> ...............-->子类其他成员变量-->子类构造方法
讨论到继承,就不得提一下多态:
如果父类构造方法的代码中有子类中被重写得方法,当执行这样的语句
SuperClass super = new SubClass();
初始化时调用父类的构造方法,是执行父类的原方法,还是执行子类中被重写的方法呢?
- class SuperClass...{
- public SuperClass()...{
- System.out.println("SuperClass of constructor");
- m();
- }
- public void m()...{
- System.out.println("SuperClass.m()");
- }
- }
- public class SubClassTest extends SuperClass ...{
- private int i = 10;
- public SubClassTest()...{
- System.out.println("SubClass of constructor");
- super.m();
- m();
- }
- public void m()...{
- System.out.println("SubClass.m(): i = " + i);
- }
- public static void main(String[] args)...{
- SuperClass t = new SubClassTest();
- }
- }
- 可能很多人会认为输出为:
- SuperClass of constructor
- SubClass.m(): i = 10
- SubClass of constructor
- SuperClass.m()
- SubClass.m(): i = 10
- 其实不然!
- 正确输出为:
- SuperClass of constructor
- SubClass.m(): i = 0
- SubClass of constructor
- SuperClass.m()
- SubClass.m(): i = 10
- 在生成对象时,父类调用的M()方法,不是父类的 M()方法,而时子类中被重写了的M()方法!!并且还出现一个怪异的现象,子类的privte int i 也被父类访问到,这不是和我们说private的成员只能在本类使用的原则相违背了吗?其实我们说的这条原则是编译期间所遵守的,在JAVA程序的编译期间,它只检查语法的合法性,在JAVA的JVM中,即运行期间,不管你声明的什么,对于JVM来说都是透明的,而多态是在运行期间执行的,所以能拿到SubClass的private成员,一点都不奇怪,只是此时还没执行 i = 10,所以在父类的构造方法中调用m()时,系统只能将i赋予系统初值0。
- 下面是我设计的一道完整的初始化例子,可测试你对类的初始化问题是否完整掌握:
- 写出程序运行的结果:
- class A...{
- private int i = 9;
- protected static int j;
- static...{
- System.out.println("-- Load First SuperClass of static block start!-- ");
- System.out.println("j = " + j);
- System.out.println("-- Load First SuperClass of static block End -- ");
- }
- public A()...{
- System.out.println("------- Load SuperClass of structor start --------");
- System.out.println("Frist print j = " + j);
- j = 10;
- m();
- System.out.println("k = " + k);
- System.out.println("Second print j = " + j);
- System.out.println("----------- Load SuperClass End ----------- ");
- }
- private static int k = getInt();
- public static int getInt()...{
- System.out.println("Load SuperClass.getInt() ");
- return 11;
- }
- static...{
- System.out.println("--- Load Second SuperClass of static block!-------");
- System.out.println("j = " + j);
- System.out.println("k = " + k);
- System.out.println("-- Load Second SuperClass of static block End -- ");
- }
- public void m()...{
- System.out.println("SuperClass.m() , " + "j = " +j);
- }
- }
- class B extends A ...{
- private int a = 10;
- static...{
- System.out.println("---- Load SubClass of static block!------");
- System.out.println("-- Load SubClass of static block End -- ");
- }
- public B()...{
- System.out.println("Load SubClass of structor");
- m();
- System.out.println("--- Load SubClass End ---- ");
- }
- public void m()...{
- System.out.println("SubClass.m() ," + "a = " + a );
- }
- }
- public class Test1...{
- public static void main(String[] args)...{
- A a = new B();
- }
- }
- 正确的答案为:
- -- Load First SuperClass of static block start!--
- j = 0
- -- Load First SuperClass of static block End --
- Load SuperClass.getInt()
- --- Load Second SuperClass of static block!-------
- j = 0
- k = 11
- -- Load Second SuperClass of static block End --
- ---- Load SubClass of static block!------
- -- Load SubClass of static block End --
- ------- Load SuperClass of structor start --------
- Frist print j = 0
- SubClass.m() ,a = 0
- k = 11
- Second print j = 10
- ----------- Load SuperClass End -----------
- Load SubClass of structor
- SubClass.m() ,a = 10
- --- Load SubClass End ----
class SuperClass...{ public SuperClass()...{ System.out.println("SuperClass of constructor"); m(); } public void m()...{ System.out.println("SuperClass.m()"); } } public class SubClassTest extends SuperClass ...{ private int i = 10; public SubClassTest()...{ System.out.println("SubClass of constructor"); super.m(); m(); } public void m()...{ System.out.println("SubClass.m(): i = " + i); } public static void main(String[] args)...{ SuperClass t = new SubClassTest(); } } 可能很多人会认为输出为: SuperClass of constructor SubClass.m(): i = 10 SubClass of constructor SuperClass.m() SubClass.m(): i = 10 其实不然! 正确输出为: SuperClass of constructor SubClass.m(): i = 0 SubClass of constructor SuperClass.m() SubClass.m(): i = 10 在生成对象时,父类调用的M()方法,不是父类的 M()方法,而时子类中被重写了的M()方法!!并且还出现一个怪异的现象,子类的privte int i 也被父类访问到,这不是和我们说private的成员只能在本类使用的原则相违背了吗?其实我们说的这条原则是编译期间所遵守的,在JAVA程序的编译期间,它只检查语法的合法性,在JAVA的JVM中,即运行期间,不管你声明的什么,对于JVM来说都是透明的,而多态是在运行期间执行的,所以能拿到SubClass的private成员,一点都不奇怪,只是此时还没执行 i = 10,所以在父类的构造方法中调用m()时,系统只能将i赋予系统初值0。 下面是我设计的一道完整的初始化例子,可测试你对类的初始化问题是否完整掌握: 写出程序运行的结果: class A...{ private int i = 9; protected static int j; static...{ System.out.println("-- Load First SuperClass of static block start!-- "); System.out.println("j = " + j); System.out.println("-- Load First SuperClass of static block End -- "); } public A()...{ System.out.println("------- Load SuperClass of structor start --------"); System.out.println("Frist print j = " + j); j = 10; m(); System.out.println("k = " + k); System.out.println("Second print j = " + j); System.out.println("----------- Load SuperClass End ----------- "); } private static int k = getInt(); public static int getInt()...{ System.out.println("Load SuperClass.getInt() "); return 11; } static...{ System.out.println("--- Load Second SuperClass of static block!-------"); System.out.println("j = " + j); System.out.println("k = " + k); System.out.println("-- Load Second SuperClass of static block End -- "); } public void m()...{ System.out.println("SuperClass.m() , " + "j = " +j); } } class B extends A ...{ private int a = 10; static...{ System.out.println("---- Load SubClass of static block!------"); System.out.println("-- Load SubClass of static block End -- "); } public B()...{ System.out.println("Load SubClass of structor"); m(); System.out.println("--- Load SubClass End ---- "); } public void m()...{ System.out.println("SubClass.m() ," + "a = " + a ); } } public class Test1...{ public static void main(String[] args)...{ A a = new B(); } } 正确的答案为: -- Load First SuperClass of static block start!-- j = 0 -- Load First SuperClass of static block End -- Load SuperClass.getInt() --- Load Second SuperClass of static block!------- j = 0 k = 11 -- Load Second SuperClass of static block End -- ---- Load SubClass of static block!------ -- Load SubClass of static block End -- ------- Load SuperClass of structor start -------- Frist print j = 0 SubClass.m() ,a = 0 k = 11 Second print j = 10 ----------- Load SuperClass End ----------- Load SubClass of structor SubClass.m() ,a = 10 --- Load SubClass End ----
下面需要说明的一点也是至关重要的一点:那就是成员变量的初始化和非static初始化块之间的执行顺序是按照他们出现的先后顺序来执行的
- public class Test04
- {
- //下面的这两行代码放置的顺序,跟执行结果是有关系的
- private String t1 = test();
- {
- System.out.println("初始化快!");
- }
- //上面的这两行代码放置的顺序,跟执行结果是有关系的
- private String test(){
- System.out.println("实例变量的执行过程");
- return "test";
- }
- public Test04()
- {
- System.out.println("构造方法!");
- }
- public static void main(String[] args)
- {
- Test04 t2 = new Test04();
- }
- }
转载于:https://www.cnblogs.com/kaikailele/p/3994004.html
java变量,初始化快,构造函数的执行顺序相关推荐
- java构造函数的执行顺序,java构造函数和初始化函数的执行顺序
1,静态变量.静态代码块.变量.普通代码块.mian方法.构造函数的执行顺序是:(静态变量.静态代码块)> main方法 >(变量.普通代码块)>构造函数. 2,如果子类调用了 ...
- Java变量初始化顺序
文章目录 Java变量初始化顺序 Java变量初始化顺序 Ⅰ.Java变量初始化顺序:静态变量或静态语句块(按声明顺序)–> 非静态变量或构造代码块(按声明顺序)–> 构造方法 –> ...
- java 构造函数的执行顺序
在此我用类似<Thinking in Java>的一个例子来说明 pakage com.yqs.test class Milk { publcic Milk() { System.out. ...
- 静态代码块、非静态代码块、构造函数三者执行顺序
主要探讨一下关于静态代码块,非静态代码块,构造函数的执行顺序. 如有错误,欢迎指出. 首先: 静态成员变量和静态代码块的优先级是一样的,先定义的先执行. 在创建一个对象的时候会执行非静态代码块和构造函 ...
- 继承下构造函数的执行顺序
这里先给出结论,在贴出代码与执行结果~ 一个派生类构造函数的执行顺序如下: 第一步执行:虚拟基类的构造函数(多个虚拟基类则按照继承的顺序执行构造函数). 第二步执行:基类的构造函数(多个普通基类也按照 ...
- 派生类构造函数的执行顺序
一个派生类构造函数的执行顺序如下: 虚拟基类的构造函数(多个虚拟基类则按照继承的顺序执行构造函数). 基类的构造函数(多个普通基类也按照继承的顺序执行构造函数). 对象的vptr被初始化: 成员对象构 ...
- 构造代码块、静态代码块、无参构造函数和有参构造函数的执行顺序
一直对构造代码块.静态代码块.无参构造函数和有参构造函数的执行顺序和执行次数混淆不清,所以记录一下它们的执行顺序以及执行次数.记录的不是很详细,留待补充. 代码: package com.yoko.t ...
- Java成员变量初始化和构造函数顺序
1.Java虚拟机会将所有的初始化动作手机到<init>()方法中,在分配内存后,就开始调用<init>()方法,其中,成员变量的初始化在构造函数里的初始化动作之前. publ ...
- java类初始化顺序_「漫画」Java中的父子类的执行顺序到底是怎么一回事?
----下课后---- 结论 类的加载顺序. (1) 父类静态代码块(包括静态初始化块,静态属性,但不包括静态方法) (2) 子类静态代码块(包括静态初始化块,静态属性,但不包括静态方法 ) (3) ...
最新文章
- 中科院学生经常看的几个公众号
- Open*** 配置参数详解
- SAP SD微观研究之销售订单类型配置里有关PO的几个字段
- uibot和按键精灵区别_uibot和按键精灵哪个强大
- Apache http强制转为https页面访问(转)
- php yii2 获取表里最大的id_Yii2中自带分页类实现分页
- 如何从操作系统安装文件提取驱动文件/ How to extract driver files from setup files (win 7)...
- 除夕快乐 | 2月11日 星期四 | B站发文回应热搜风波;美团上线“团好货”独立App;国内首家自动驾驶企业获网约车运营许可...
- 业务、架构、技术,我们应该关注什么
- 《高并发Oracle数据库系统的架构与设计》一1.4 本章小结
- 关于单行和多行文本溢出显示省略号的解决方案
- python 列表输入sql_将Python列表传递到SQL查询
- 33.MySQL高可用架构
- 抓linux肉鸡教程视频,超简单的菜鸟网吧抓肉鸡教程
- cad卸载_CAD一键卸载工具
- 超实用!教你用 Python 获取并下载美股数据
- 5-RNN-01_字符集RNN
- EasyUI上传图片,前台预览,后台读取
- 解决中标麒麟QQ乱码和WPS缺失字体的错误
- 数据包设置了不允许分片Don't fragment: Set