类和对象的加载过程原理

2022-05-30 11:58:12 浏览数 (1)

1. 类加载的结论和代码演示

顺序:

  1. 父类静态属性 (可以是对象) 和静态代码块,看其在类中的先后顺序
  2. 子类静态属性和静态代码块 ,看其在类中的先后顺序
  3. 父类非静态属性和非静态代码块 ,看其在类中的先后顺序
  4. 父类构造方法
  5. 子类非静态属性和非静态代码块 ,看其在类中的先后顺序
  6. 子类构造方法
  7. 难点:与前面的过程分开讲解,类中的静态属性是自己,这个时候应该如何加载呢?即如下的第一行代码:
代码语言:javascript复制
class Root {
    public static Root s = new Root();
    
    static {
        System.out.println("Root static block");
    }

    {
        System.out.println("Root no static block");
    }

    public Root() {
        System.out.println("Root no parameter");
    }
}

public class LeafTest {
    public static void main(String[] args) {
        new Root();
    }
}
复制代码

这里先给出结论:当类加载到第一行代码时:

代码语言:javascript复制
public static Root s = new Root();
复制代码

因为此时正在执行类加载的过程,内存中已经开始加载 Root 这个类了,这时会认为已经加载过一次 Root 类了,这个时候先中断类的加载,开始对象的加载,对象加载完成之后,就会恢复类的加载,继续开始完成未完成的类加载。

1.1 第 1-6 条结论演示,第七点结论是重难点,单独举例

静态的随类的加载而加载,有且只会加载一次,其他的属于对象的,随对象的加载而加载,可加载多次。

咱们先看比较简单的 1-6 条的例子,先弄清楚简单的,在来看稍微复杂一点的第七条。

先来个超级简单的,如下:

代码语言:javascript复制
class StaticAttribute {

    static {
        System.out.println("StaticAttribute static Block");
    }

    {
        System.out.println("StaticAttribute Block");
    }

    StaticAttribute(String name) {
        System.out.println(name   "StaticAttribute");
    }
}

class Attribute {
    static {
        System.out.println("Attribute static Block");
    }

    {
        System.out.println("Attribute Block");
    }

    Attribute(String name) {
        System.out.println(name   "Attribute");
    }
}

class Root {
    public static StaticAttribute s;
    public Attribute s1;

    static {
        System.out.println("Root static block");
    }

    {
        System.out.println("Root no static block");
    }

    public Root() {
        super();
        System.out.println("Root no parameter");
    }
}

class Sub extends Root {
    static {
        System.out.println("Sub static block");
    }

    {
        System.out.println("Sub no static block");
    }

    public Sub() {
        super();
        System.out.println("Sub no parameter");
    }

    public Sub(String msg) {
        this();
        System.out.println("Sub constructor:"   msg);
    }
}

public class LeafTest {
    public static void main(String[] args) {
        new Sub();
        System.out.println();
        new Sub();
    }
}
复制代码

输出结果如下:

代码语言:javascript复制
Root static block
Sub static block
Root no static block
Root no parameter
Sub no static block
Sub no parameter

Root no static block
Root no parameter
Sub no static block
Sub no parameter

Process finished with exit code 0

复制代码

可以看出,静态代码块内的语句由父类到子类最先执行,且只执行一次,这是因为静态代码块随类的加载而加载; 之后每创建一次对象的时候,由父及子,先执父类中的普通代码块,再执行父类中的构造器,然后在执行子类中的普通代码块,再执行子类中的构造器。

以上是没有对类的属性和静态属性初始化赋值,这些都好说,下面我们来看加载类属性和静态属性的例子,在 Root 类中加上两行代码,如下,其他代码没有任何变化:

输出结果如下:

代码语言:javascript复制
StaticAttribute static Block
StaticAttribute Block
YuShiwenStaticAttribute
Root static block
Sub static block
Attribute static Block
Attribute Block
YuShiwenAttribute
Root no static block
Root no parameter
Sub no static block
Sub no parameter

Attribute Block
YuShiwenAttribute
Root no static block
Root no parameter
Sub no static block
Sub no parameter

Process finished with exit code 0

复制代码

我们从主方法开始,先 new Sub ()

代码语言:javascript复制
new Sub();
复制代码

这个时候会找到 Sub 类,找到其父类,从父类开始加载,Sub 类继承了 Root 类,所以先加载 Root 类的静态属性:

  1. 加载 Root 类,静态属性和代码块随着类的加载而加载
代码语言:javascript复制
public static StaticAttribute s = new StaticAttribute("YuShiwen");
复制代码

先加载静态属性,new StaticAttribute ("YuShiwen"); 这个时候就要加载 StaticAttribute 类了,加载过程如输出结果的前三行所示,加载了其类 StaticAttribute 的静态代码块、代码块、构造方法。

然后在继续加载 Root 类的静态代码块 (静态的安装在类中的先后顺序加载),输出第四行

  1. 然后加载 Root 的子类 Sub,静态属性和代码块随着类的加载而加载 由于只有一个静态代码块,所以就只加载一个,输出第五行
  2. 然后是 new 对象,所以再回到父类中加载非静态的属性、代码块(属性和代码块按照再类中写的先后顺序加载),最后再加载父类的构造方法。 ​ 3.1. 加载非静态属性:
代码语言:javascript复制
public Attribute s1 = new Attribute("YuShiwen");
复制代码

new 了一个 Attribute 类,这个时候就要加载 Attribute 类了,因为是第一次用到 Attribute 类,所以先加载 Attribute 类,静态的代码块、属性随着类的加载而加载,输出第六行;

然后 new 对象,加载代码块、属性,输出第七行; 最后在执行构造方法,输出第八行。

​ 3.2. 加载非静态代码块:输出第九行;

​ 3.3. 最后执行构造方法:输出第十行。

  1. new 对象过程,加载 Root 的子类,先加载非静态的代码块和方法,只有代码块,输出第十一行;然后最后再执行构造方法,输出第十二行。

回单 Main 方法中继续执行

代码语言:javascript复制
System.out.println();
复制代码

换行之后,我们又 new 了一个对象,这个时候重复 3 和 4 过程就行,因为类只需要加载一次,静态属性和代码块是随类的加载而加载的,所以不需要 1,2 过程了,并且再 3.1 中再次 new Attribute 的时候,如下:

代码语言:javascript复制
public Attribute s1 = new Attribute("YuShiwen");
复制代码

我们不在需要加载 Attribute 类了,因为已经加载过一次了。

1.2 第 7 条结论演示

当我们把上述 Root 类中的第一行代码,

代码语言:javascript复制
public static StaticAttribute s = new StaticAttribute("YuShiwen");
复制代码

从 new 其他类作为自己的静态类属性,改为 new 自己作为自己的静态类属性,即如下:

我 new 我自己,而且是作为类的静态属性,这个时候会肿么样加载呢,我们来看下运行结果:

代码语言:javascript复制
Attribute static Block
Attribute Block
YuShiwenAttribute
Root no static block
Root no parameter
Root static block
Sub static block
Attribute Block
YuShiwenAttribute
Root no static block
Root no parameter
Sub no static block
Sub no parameter

Attribute Block
YuShiwenAttribute
Root no static block
Root no parameter
Sub no static block
Sub no parameter

Process finished with exit code 0

复制代码

分析:

我们还是从主方法开始,先 new Sub ()

代码语言:javascript复制
new Sub();
复制代码

这个时候会找到 Sub 类,找到其父类,从父类开始加载,其父类为 Root 类,加载 Root 类,静态属性和代码块随着类的加载而加载,此时 Root 类中第一行代码为

代码语言:javascript复制
public static Root s = new Root();
复制代码

我 new 我自己,我这才刚开始加载我自己呢,还没加载完 Root 类,怎么现在要创建一个静态的 Root 对象呢?

现有要咋办呢,再次加载 Root 类吗,但是 Root 类中已经存在了内存中了,类只会加载一次,肯定是不会再次加载了的。

但是我现在在加载 Root 类的过程中,遇到了 new Root 对象,我 Root 类还没加载完呢,这个时候怎么处理呢?

重点:

这个时候会认为 Root 类已经加载到内存中去了(实际还在加载的过程中,还没加载完),认为已经加载过一次类了,所以会把这次当作第二次 new 对象,所以会去加载非静态的代码块,非静态属性,最后加载构造方法,这个 new 对象的过程加载完成了,就会继续加载类的静态属性、静态代码块。

所以就会在输出结果中出现第 1、2、3 行先加载了 Attribute 类,因为先去加载了类 Root 的非静态属性,即先执行了 public static Root s = new Root (); 代码后面的 public Attribute s1 = new Attribute ("YuShiwen"); 让非静态属性先加载了;

然后在按照在类中的顺序加载 Root 类中的非静态代码块,输出了第 4 行;

最后执行 Root 的构造方法,输出第 5 行。

接着回来继续加载 Root 类的,静态代码块和静态方法随着类的加载而加载,所以按照在类中的先后顺序,加载完静态属性后,接着加载了静态代码块,输出第 6 行。接下来的内容与章节 1.1 中的内容相似,笔者这里就不再进行赘述了。

源码附件已经打包好上传到百度云了,大家自行下载即可~

代码语言:javascript复制
链接: https://pan.baidu.com/s/14G-bpVthImHD4eosZUNSFA?pwd=yu27
提取码: yu27

百度云链接不稳定,随时可能会失效,大家抓紧保存哈。

如果百度云链接失效了的话,请留言告诉我,我看到后会及时更新~

开源地址

码云地址: http://github.crmeb.net/u/defu

Github 地址: http://github.crmeb.net/u/defu

0 人点赞