static 对 子类、父类加载初始化顺序的影响

1.static关键字的介绍

static是一个成员修饰符.其作用:

  • 1.静态修饰 类的属性 和 类的方法
static String name; // 修饰类的属性
static void test1(){...}  // 修饰类的方法
  • 2.在类中构成代码块
static{...}
  • 3.构成静态内部类
static class xxx{}
  • 4.静态导包
import static java.lang.Integer.*;

如需具体了解static的用法,请先看别的博客吧!我还没写

2.子类父类的加载初始化顺序

在首次使用类(子类)时

1.查看该子类的父类信息是否加载在 内存方法区如果没有加载到方法区,则先加载父类信息
2.先加载父类的静态属性和静态代码块(静态属性和静态代码块的加载顺序由在类中的前后位置决定.)
3.父类的静态属性和代码块加载完,再加载子类的静态属性和静态代码块。
4.然后加载 父类的 成员变量(非static修饰的属性)之后 执行父类的构造方法
5.再加载子类的 成员变量 之后 执行子类的构造方法

顺序就是 :

父类静态变量/父类静态代码块

——>> 子类静态变量/子类静态代码块

——>> 父类成员变量

——>> 父类构造方法

——>> 子类成员变量

——>> 子类构造方法

代码为先:

public class Person {

    //静态变量(类变量)
    public static String name = print("父类的name静态变量初始化");
	
    //成员变量
    public String age = print("父类的age成员变量初始化");
    
    静态代码块
    static{
        System.out.println("父类的静态代码块");
    }

    //构造方法
    public Person(){
        System.out.println("父类构造器");
    }
    
	//输出显示用的
    public static String print(String aaa){
        System.out.println(aaa);
        return aaa;
    }
}
public class Student extends Person {

    static{
        System.out.println("子类的静态代码块");
    }
    
    public static String name = print("子类的name静态变量初始化");

    public String age = print("子类的age成员变量初始化");

    public Student(){
        System.out.println("子类对象构造器");
    }
}
public static void main(String[] args) {
    Student student = new Student();
}


//输出结果
父类的name静态变量初始化
父类的静态代码块
子类的静态代码块
子类的name静态变量初始化
父类的age成员变量初始化
父类构造器
子类的age成员变量初始化
子类对象构造器

如果你对加载顺序了解,那你很棒。但以上不是本博客重点

3.重点(加载顺序可能错乱吗?答案:可能)

错误点: 在静态代码块中初始化本类

先看一个单类(没有父类)

public class Person {
    
    public String age = print("父类的age成员变量初始化");
    static{
        System.out.println("父类的静态代码块");
        Person p1 = new Person();
        System.out.println("父类的静态代码块---------"+Person.name);
    }
    public static String name = print("父类的name静态变量初始化");

    public Person(){
        System.out.println("父类构造器");
    }

    public static String print(String aaa){
        System.out.println(aaa);
        return aaa;
    }
}

public static void main(String[] args) {
    Person p2 = new Person();
    System.out.println(Person.name);
}

//输出
①父类的静态代码块
②父类的age成员变量初始化
③父类构造器
④父类的静态代码块---------null
⑤父类的name静态变量初始化
⑥父类的age成员变量初始化
⑦父类构造器
⑧父类的name静态变量初始化        //这行输出的是name值(不要当作在这里又初始化了)

我们会发现父类的成员变量和构造器先于静态变量初始化了。
反编译后的部分person.class代码:

类信息加载反编译class文件图

由上面我们可以知道,在类信息加载的时候,是先定义后赋值的。而我们实例化类的时候,需要(获得成员变量信息,构造器信息)对成员变量信息进行初始化及本对象进行实例化(不用管类的静态变量是否完成赋值)。

现在我们对上面代码一步一步进行梳理:

1.在主方法中 发现 new Person();所以去寻找类信息,但是类还没有加载过,所以加载person类信息

2.加载Person类信息,把Person类中的信息先定义(未赋值)。然后从上向下寻找static修饰符修饰的变量或者代码块(谁在前,先执行谁,如果是静态变量name在前,则会执行对name的赋值操作)。发现代码块

static{
     System.out.println("父类的静态代码块");
     Person p1 = new Person();
 	System.out.println("父类的静态代码块---------"+Person.name);
}

然后执行代码块

3.执行代码块中的代码,输出第一行信息①。然后发现 Person p1 = new Person(); 再去寻找Person类的信息。这时发现Person类信息(未赋值)已经存在。则对p1对象进行实例化。

4.实例化时不考虑对象的类的静态变量,只对成员变量进行实例化 ,输出第二行信息②. 然后执行构造方法内的代码,输出第三行信息③。

5.这时完成对p1的实例化,执行代码 System.out.println("父类的静态代码块---------"+Person.name);,但是这是的Person类的静态变量name只是开辟空间还没有赋值。所以输出④

6.这是静态代码块执行完成所以继续向下寻找static修饰的变量和代码块,发现:类静态变量name ,对静态变量那么进行赋值,所以执行输出⑤。

7.这时相对于main方法,第2~6步,算是完成对Person类信息的加载。开始进行对p2的实例化。先对成员变量实例化,输出⑥。在执行构造方法,输出⑦。

8.在执行main方法中的 System.out.println(Person.name); 输出⑧(Person.name已在第6步完成赋值)。

还可以吗? 试试下面的一段代码。(提示:即使有父类,也要明白。类信息加载不等于类变量完成赋值。类信息加载后(未赋值)也可以拿去对 对象进行实例化)

4.试试代码
public class Person {

    public String age = print("父类的age成员变量初始化");

    static{
        System.out.println("父类的静态代码块");
        Student student = new Student();
        System.out.println("父类的静态代码块Stadent的name---------"+Student.name);
    }

    public static String name = print("父类的name静态变量初始化");

    public Person(){
        System.out.println("父类构造器");
    }

    public static String print(String aaa){
        System.out.println(aaa);
        return aaa;
    }
}

public class Student extends Person {

    static{
        System.out.println("子类的静态代码块");
    }

    public static String name = print("子类的name静态变量初始化");

    public String age = print("子类的age成员变量初始化");

    public Student(){
        System.out.println("子类对象构造器");
    }
}
public static void main(String[] args) {
    Student student = new Student();
}

//输出
父类的静态代码块
父类的age成员变量初始化
父类构造器
子类的age成员变量初始化
子类对象构造器
父类的静态代码块Stadent的name---------null
父类的name静态变量初始化
子类的静态代码块
子类的name静态变量初始化
父类的age成员变量初始化
父类构造器
子类的age成员变量初始化
子类对象构造器

有兴趣慢慢的分析一下,(工作中,没人这样写。因为这样写的人都被辞退了)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值