Java代码的执行顺序

该文章转载自:

总结:

对于JAVA中类的初始化是一个很基础的问题,其中的一些问题也是易被学习者所忽略。当在编写代码的时候碰到时,常被这些问题引发的错误,感觉莫名其妙。而且现在许多大公司的面试题,对于这方面的考查也是屡试不爽。不管基于什么原因,我认为,对于java类中的初始化问题,有必要深入的了解。Java类的初始化,其实就是它在JVM的初始化问题(类加载的问题),对于它在JVM中的初始化是一个相当复杂的问题,是给专家们来探讨的,所以在这里我只是对一些容易忽略的问题,发表一下个人观点:

1.内部类的顺序

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  

通过输出,可见当生成的实例test时,它并不是首先调用其构造方法而是先是成员变量的初始化,而且成员的初始化的顺序以成员变量的定义顺序有关,先定义的先初始化java 类初始化,初始化后再调用构造方法。其实成员变量的初始化,在类的所有方法调用之前进行,包括构造方法

当类中有 修饰的成员呢?测试下面一段代码:

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!  

可见 的成员比普通的成员变量先初始化。

我们都知道,如果一个类的成员变量没有在定义时,系统会给予系统默认的值,有=号的就直接给予右值,系统在给予初值和=号给予值这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  

为什么是2和3呢?其实代码的执行顺序是这样的:首先执行给t1,i1,i2分别给予初始值null,0,0,再执行

t1 =new (),这样i1++,i2++被执行,i1,i2都变为1,执行完毕后接着执行int i1; i1,i2的值仍然是1,1,当执行int i2 = 2时i2被赋予了值,即i1 = 1,i2=2;再执行 t2 = new (),i1,i2再执行++,此时i1 =2,i2 =3,输出i1,i2,结果就是:t2.i1 = 2,t2.i2 = 3。 通过上面的代码我们可以认为系统默认值的给予比通过等号的赋予先执行。

2,一个类还有上层的类,即父类:

当生成一个子类时java 类初始化,大家到知道会调用父类的构造方法。如果子类和父类中都有的成员变量呢,其实我们在深入分析一个类的内部初始化后,对于存在父类的类的初始化其实原理都一样,具体以下面的代码为例:

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  

可见当父类,和子类有时,先初始化,再初始化子类的,再初始化父类的其他成员变量->父类构造方法->子类其他成员变量->子类的构造方法。

父类上层还有父类时,总是先执行最顶层父类的-->派生类-->派生类-->…-->子类-->顶层父类的其他成员变量-->父类构造方法--> 派生类的其他成员变量 --> 派生类构造方法--> …-->子类其他成员变量-->子类构造方法

3.讨论到继承Java代码的执行顺序,就不得提一下多态:

如果父类构造方法的代码中有子类中被重写得方法,当执行这样的语句

super = new ();

初始化时调用父类的构造方法Java代码的执行顺序,是执行父类的原方法,还是执行子类中被重写的方法呢?

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  

在生成对象时java 类初始化,父类调用的M()方法,不是父类的 M()方法,而时子类中被重写了的M()方法!!并且还出现一个怪异的现象,子类的 int i 也被父类访问到,这不是和我们说的成员只能在本类使用的原则相违背了吗?其实我们说的这条原则是编译期间所遵守的,在JAVA程序的编译期间,它只检查语法的合法性,在JAVA的JVM中,即运行期间,不管你声明的什么,对于JVM来说都是透明的,而多态是在运行期间执行的,所以能拿到的成员,一点都不奇怪,只是此时还没执行 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  ---- 
Process finished with exit code 0

下面需要说明的一点也是至关重要的一点:那就是成员变量的初始化和非初始化块之间的执行顺序是按照他们出现的先后顺序来执行的

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();  
    }  
      
}  

公告:
1. 本站所有资源来源于用户上传和网络,如有侵权请联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系站长处理!
6. 本站不售卖代码,资源标价只是站长收集整理的辛苦费!如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
7. 站长QQ号码 2205675299

资源库 - 资源分享下载网 » Java代码的执行顺序

常见问题FAQ

关于资源售价和售后服务的说明?
本站所有资源的标价均为本站收集资源的辛苦费,不代表资源本身的价值。软件是高智慧高价值的商品,不可能是白菜价。本站资源标价只是赞助费用,收取的赞助费仅用来维持本站的日常运营!毕竟收集整理资料需要投入云计算资源和站长大量的精力。
代码有没有售后服务和技术支持?
由于代码的运行具有不可预见性,本站不保证代码完整可运行,不提供技术支持和售后服务。 本站原创代码都是站长自己开发的,可以有偿提供技术支持服务。 网站里标明【亲测】的代码都是站长亲测过的,其他的代码由于精力有限,没有一一测试,不能保证代码就一定能够使用,更没有技术支持服务,下载前请自行斟酌。
有没有搭建服务?
由于搭建服务比较费时费力,所以本站除了原创代码外均不提供搭建服务。本站分享代码纯属兴趣爱好,不以盈利为目的,请勿咨询有没有搭建服务,谢谢理解。
链接地址失效了怎么办?
请带上资源链接地址联系客服,工作时间内我们看到后将第一时间回复。
关于解压密码
本站资源一般都没有加密,如果发现需要解压密码的,那么就输入 hao.35dc.com 试试。

发表评论

资源库,由老程序员细心甄别、精心筛选,只为提供优质的源码资源

关于我们 联系我们