深入理解类加载机制

之前的博客说了,类加载分为五个阶段

加载->链接(验证+准备+解析)->初始化(使用前的准备)->使用->卸载

今天,遇到一个很有趣的现象:

public class Dervied extends Base {

    private  String name = "dervied";

    public Dervied() {
        tellName();
        printName();
    }
    
    public void tellName() {
        System.out.println("Dervied tell name: " + name);
    }
    
    public void printName() {
        System.out.println("Dervied print name: " + name);
    }

    public static void main(String[] args){
        
        new Dervied();    
    }
}


class Base {

private String name = "base";


public Base() {
tellName();
printName();
}

public void tellName() {
System.out.println("Base tell name: " + name);
}

public void printName() {
System.out.println("Base print name: " + name);
}
}



 

猜猜输出的结果是什么,或许你会说

Dervied tell name: base
Dervied print name: base
Dervied tell name: dervied
Dervied print name: dervied

一开始我也是这么觉得的,但是,实际上输出的结果却是

Dervied tell name: null
Dervied print name: null
Dervied tell name: dervied
Dervied print name: dervied

为什么会是Null?,是不是很奇怪,下面我会详细分析

结合着 加载->链接(验证+准备+解析)->初始化(使用前的准备)->使用->卸载 这几个阶段,我们先来看看。

首先,new一个Dervied是调用他的构造方法,

  public Dervied() {
        tellName();
        printName();
    }
我们知道,创建一个子类的实例时,首先会调用其父类无参的构造方法,而调用无参的构造方法之前,会调用父类属性,即调用
private String name = "base";
然后
public Base() {
tellName();
printName();
}


而tellName();这句会输出
System.out.println("Base tell name: " + name);
你可能会想,此时不是已经读取了name吗,为什么最后显示的是Null呢,实际上,虽然此时父类的name是base,但是子类的name还是null,此刻调用的name不是父类的name,而是子类的name,即使写在父类里面。
所以输出的结果是上面我们看到的那样。
我们可以对上面的例子做一点改变:
private  String name = "dervied";改成
private  static String name = "dervied";
这样,就可以访问到子类的name了。
原因是static修饰的变量在类加载的时候就已经被载入了,而我们上面讨论的都是发生在类初始化的时候进行的,所以输出的结果是
 

Dervied tell name: dervied
Dervied print name: dervied
Dervied tell name: dervied
Dervied print name: dervied




原文地址:https://www.cnblogs.com/yzjT-mac/p/5868669.html