代码块,构造方法的初始化顺序

目录

复习知识点

一、 先说结论, 只对一个类而言,初始化先后顺序:

二,有继承的情况下⭐⭐⭐⭐⭐

三、参考博文



复习知识点

1,变量"先声明,再使用";

2,staic方法和代码块,不能访问普通的成员变量和普通方法 

3,子类构造方法如果没有显示调用父类的构造方法,就会默认调用父类无参的构造方法;

4,static代码块只会被执行一次,普通代码块,每当创建对象,都会被执行

先看简单的,然后逐步深入。

一、 先说结论, 只对一个类而言,初始化先后顺序:

(静态变量、静态代码块):按照声明的先后顺序,进行初始化。

(普通变量、普通代码块):按照声明的先后顺序,进行初始化。

(静态变量、静态初始化块)----->(普通变量、普通初始化块)-----> 构造器

 

  1. (静态变量、静态代码块):按照声明的先后顺序,进行初始化。

public class Test {
​
    public void main(String[] args){
        new A();
    }
}
​
class A{
    //静态变量
    static int s_i = 888;
​
    static{                     //注意这里用到了静态变量s_i
        System.out.println("静态代码块, s_i = " + s_i);
    }
}

结果正常输出

静态代码块, s_i = 888

 

2.(普通变量、普通代码块):按照声明的先后顺序,进行初始化。

同上1.2.1,把静态变量、静态代码块的“static"修饰符去掉,就可以验证,不多赘述。

 

3. ⭐⭐⭐(静态变量、静态初始化块)----->(普通变量、普通初始化块)-----> 构造器

 

3.1 (静态变量、静态初始化块)----->(普通变量、普通初始化块)

public class Test {
​
    public static void main(String[] args){
        new A();
    }
}
​
class A{
​
    //普通变量i
    int i = 0;
    
    static{
        System.out.println("静态代码块, i = " + i);
    }
}

编译时就会报错。

static方法和static代码块,不能访问普通的成员变量和普通方法

因为static方法和static代码块,会先进行初始化

普通成员变量和普通代码块,后进行初始化。

3.2. 小杂烩

public class Test {
​
    public static void main(String[] args){
        new A();
    }
}
​
class A{
    
    A(){
        System.out.println("A的构造方法" );
    }
​
    //普通变量i
    B b = new B();
    
        //静态变量
    static B s_b = new B(1);
    
    //普通代码块
    {
        System.out.println("普通代码块" );
    }
    
    //静态代码块
    static{
        System.out.println("静态代码块" );
    }
}
​
class B{
    B(){
        System.out.println("B()无参构造方法");
    }
    
    B(int i){
        System.out.println("B(int i)有参构造方法");
    }
}

结果

创建静态变量s_b,调用B(int i)有参构造方法
静态代码块
创建普通变量b,调用B()无参构造方法
普通代码块
A的构造方法

可以看到,A构造方法即使放在最前面,也依旧要垫底输出

说明了初始化顺序:(静态变量、静态初始化块)----->(普通变量、普通初始化块)-----> 构造器

 

二,有继承的情况下⭐⭐⭐⭐⭐

先执行(静态变量、静态初始化块),从最顶层父类到子类,再到当前类,依次执行。

【同时注意,(静态变量、静态初始化块) 只会执行一次!!后面无论创建多少次对象,都不会再执行】

再执行(普通变量、普通初始化块)-----> 构造器,从最顶层父类到子类,再到当前类,依次执行。

【同时注意,这里的构造器,是被子类调用的构造器(因为父类的构造器可以有多个呀)。】

继承关系下的初始化顺序

如上图,总体上依旧是先执行static静态的,然后再执行普通的。具体过程:

会先执行类A的(静态变量、静态初始化块);再执行B的(静态变量、静态初始化块);再执行。。。。;最后执行N的(静态变量、静态初始化块),最终执行N的main()方法(如果有的话)。

如果N中有main()方法,那么再执行完main()方法,就结束了。

如果没有main()方法,或者main()方法里new 了N的对象。

那就会再从类A开始,执行类A的(普通变量、普通初始化块),然后执行A的被调用的构造方法;再执行B的(普通变量、普通初始化块),然后执行B的被调用的构造方法;.............;

最后执行N的(普通变量、普通初始化块),然后执行N的构造方法

上代码,先看简单的

package com.lei.java.test;
​
class A{
    
    //普通代码块
    {
        System.out.println("A的普通代码块" );
    }
    
    //静态代码块
    static{
        System.out.println("A的static代码块" );
    }
​
    A(){
        System.out.println("A()无参构造" );
    }
​
}
​
class B extends A{
    
    //普通代码块
    {
        System.out.println("B的普通代码块" );
    }
    
    //静态代码块
    static{
        System.out.println("B的static代码块" );
    }
​
    B(){
        System.out.println("B()无参构造" );
    }
    
}
​
public class C extends B{
    
    //普通代码块
    {
        System.out.println("C的普通代码块" );
    }
    
    //静态代码块
    static{
        System.out.println("C的static代码块" );
    }
​
    C(){
        System.out.println("C()无参构造" );
    }
​
    //C的main方法
    public static void main(String[] args){
        System.out.println("C的main()方法");
    }
}

结果

A的static代码块
B的static代码块
C的static代码块
C的main()方法

是不是很整齐,安排的明明白白。如我们所预料的那样,先是静态代码块,从父类到子类,被依次执行了。

不过,为什么普通代码块没有被执行呢?,因为没有创建对象。

实例对象里有它的普通成员变量,普通代码块会在实例对象创建的时候,初始化普通成员变量。实例对象创建的时候,JVM会给它分配堆的内存空间,有空间才能存东西呀。这里可以不必深究,继续。

重头戏,在C的main方法里,加一行代码,new C(),创建C对象。

  //C的main方法
    public static void main(String[] args){
        System.out.println("C的main()方法");
        new C();//加上这一行,创建C的实例对象
    }

结果

A的static代码块
B的static代码块
C的static代码块
C的main()方法
A的普通代码块
A()无参构造
B的普通代码块
B()无参构造
C的普通代码块
C()无参构造

我们之前的初始化顺序,得到了最终的验证

(静态变量、静态初始化块)----->(普通变量、普通初始化块)-----> 构造器

(静态变量、静态初始化块)(先父后子,最后main)----->【(普通变量、普通初始化块)-----> 构造器】(先父后子)

 

继续验证(静态变量、静态初始化块) 只会执行一次!!

public class Test {
​
    public static void main(String[] args){
        new A();
        new A();
    }
}
​
class A{
    //普通代码块
    {
        System.out.println("A的普通代码块" );
    }
    
    //静态代码块
    static{
        System.out.println("A的static代码块" );
    }
​
    A(){
        System.out.println("A()无参构造" );
    }
​
}

结果

A的static代码块 
A的普通代码块
A()无参构造
A的普通代码块
A()无参构造

A的static代码块 只被执行了一次。 

再复杂一些,

class A{
    
    //普通代码块
    {
        System.out.println("A的普通代码块" );
    }
    
    //静态代码块
    static{
        System.out.println("A的static代码块" );
    }
​
    A(){
        System.out.println("A()无参构造" );
    }
}
​
public class B extends A{
    
    //普通变量
    A a = new A();
    
    B(){
        System.out.println("B()无参构造");
    }
    
    public static void main(String[] args){
        System.out.println("B的main方法========");
        new B();
    }
}

结果,及逐行分析

A的static代码块    加载A,执行static代码块
B的main方法======== B没有其它static成员,于是执行main方法
A的普通代码块       mian方法中new B(),调用B的无参构造,而它默认调用父类无参构造,于是回到A,由于A已经被加载过,A的static代码块或变量只会被执行一次,所以不执行了。于是执行A的普通代码块。
A()无参构造        执行A的构造方法。至此,所有static内容执行完毕,下面开始执行普通内容。
A的普通代码块       回到A,执行普通代码块
A()无参构造        执行A的无参构造,A结束
B()无参构造       执行B的无参构造,new B()结束
        

千万注意,调用父类的构造方法,不是意味着,它只执行父类的构造方法中的内容,而是会进行成员变量初始化,运行代码块,执行构造方法中的内容等等。

 

大招,留给各位练习。

class A{
    
    //普通代码块
    {
        System.out.println("A的普通代码块" );
    }
    
    //静态代码块
    static{
        System.out.println("A的static代码块" );
    }
​
    A(){
        System.out.println("A()无参构造" );
    }
​
    A(int ai){
        System.out.println("A(int i)有参构造, ai = " + ai);
    }
}
​
public class B extends A{
    
    //普通代码块
    {
        System.out.println("B的普通代码块" );
    }
    
    //静态代码块
    static{
        System.out.println("B的static代码块" );
    }
​
    B(){
        System.out.println("B()无参构造" );
    }
    
    //普通变量
    A a = new A();
    
    //静态变量
    static A s_a = new A(888);
    
    B(int bi){
        //因为B的无参构造,默认调用父类的无参构造,
        //为了效果,我们这里就让B的有参构造,调用父类的有参构造
        super(bi);
        System.out.println("B(int i)有参构造,bi = " + bi );
    }
    
    public static void main(String[] args){
        System.out.println("B的main方法==================================");
        new B();
        new B(888);
    }
}

结果,

A的static代码块
B的static代码块
A的普通代码块
A(int i)有参构造, ai = 888
B的main方法==================================
A的普通代码块
A()无参构造
B的普通代码块
A的普通代码块
A()无参构造
B()无参构造
A的普通代码块
A(int i)有参构造, ai = 888
B的普通代码块
A的普通代码块
A()无参构造
B(int i)有参构造,bi = 888

三、参考博文

其中原理,是jvm的内容,推荐博文

深入JVM字节码,解析类加载、链接、初始化、对象初始化、程序执行的流程

Java的类初始化的详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值