Java程序员的日常 —— Java类加载中的顺序

之前说过Java中类的加载顺序,这次看完继承部分,就结合继承再来说说类的加载顺序。

继承的加载顺序

由于static块会在首次加载类的时候执行,因此下面的例子就是用static块来测试类的加载顺序。

package xing.test.thinking.chap7;
class A{
    static{
        System.out.println("A static");
    }
}
class B extends A{
    static{
        System.out.println("B static");
    }
}
class C extends B{
    private static D d = new D();
    static{
        System.out.println("C static");
    }
}
class D{
    static{
        System.out.println("D static");
    }
}
public class ExtendTest {
    public static void main(String[] args) {
        C c = new C();
    }
}

在上面的例子中,类C继承B,B继承A,而C有依赖于D。因此当创建C的时候,会自动加载C继承的B和依赖的D,然后B又会加载继承的A。只有A加载完,才能顺利的加载B;BD加载完,才能加载C。这就是类的加载顺序了。

A static
B static
D static
C static

所有的变量初始化完,才会执行构造方法

在类的加载过程中,只有内部的变量创建完,才会去执行这个类的构造方法。

package xing.test.thinking.chap7;
class A2{
    B2 b2 = new B2();
    static{
        System.out.println("A static");
    }
    public A2() {
        System.out.println("A2()");
    }
}
class B2{
    C2 c2 = new C2();
    D2 d2 = new D2();
    static{
        System.out.println("B static");
    }
    public B2() {
        System.out.println("B2()");
    }
}
class C2{
    static{
        System.out.println("C static");
    }
    public C2() {
        System.out.println("C2()");
    }
}
class D2{
    static{
        System.out.println("D static");
    }
    public D2() {
        System.out.println("D2()");
    }
}
public class VarTest {
    public static void main(String[] args) {
        A2 a2 = new A2();
    }
}

在上面的例子中,A2里面有B2变量,B2则有C2D2变量。因此类的加载还是先读取到哪个,就执行相应的静态块。 当依赖的对象都定义完,才会执行构造方法:

A static
B static
C static
C2()
D static
D2()
B2()
A2()

静态成员与普通成员类的加载区别

在类的加载过程中,静态成员类的对象,会优先加载;而普通成员类的对象则是使用的时候才回去加载。

package xing.test.thinking.chap7;
class A3{
    B3 b3 = new B3();
    static C3 c4 = new C3();
    static{
        System.out.println("A3");
    }
}
class B3{
    static{
        System.out.println("B3");
    }
}
class C3{
    static{
        System.out.println("C3");
    }
}
public class StaticTest {
    public static void main(String[] args) {
        A3 a3 = new A3();
    }
}

输出:

C3
A3
B3

总结

第一点,所有的类都会优先加载基类 第二点,静态成员的初始化优先 第三点,成员初始化后,才会执行构造方法 第四点,静态成员的初始化与静态块的执行,发生在类加载的时候。 第四点,类对象的创建以及静态块的访问,都会触发类的加载。

补充类构造方法的顺序

看代码:

package xing.test.thinking.chap8;
class A{
    public A() {
        System.out.println("A");
    }
}
class B extends A{
    public B() {
        System.out.println("B");
    }
}
class C extends B {
    private D d1 = new D("d1");
    private D d2 = new D("d2");
    public C() {
        System.out.println("C");
    }
}
class D {
    public D(String str) {
        System.out.println("D "+str);
    }
}
public class ExtendTest {
    public static void main(String[] args) {
        C c = new C();
    }
}

执行结果:

A
B
D d1
D d2
C

因此可以得出结论:

  • 首先会调用基类的构造方法
  • 其次,调用成员的构造方法
  • 最后,调用自己的构造方法

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏hbbliyong

C#,一些非常简单但应该知道的知识点

1.本地变量 一看这个标题你可能会一愣,这是个什么东东。看个小例子: static void main() {    int a=10;    MyClass ...

4369
来自专栏owent

C++ 新特性学习(四) — Bind和Function

木有错,这是C++,并且很方便地实现了委托 这就是传说中的绑定库和增强型的函数对象 接下来一个一个来

1181
来自专栏java一日一条

Java的内存机制

Java 把内存划分成两种:一种是栈内存,另一种是堆内存。在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时...

890
来自专栏待你如初见

反射

1475
来自专栏小樱的经验随笔

Vijos P1784 数字统计【模拟】

数字统计 背景 来自 NOIP2010 普及组 第一题 描述 请统计某个给定范围[L, R]的所有整数中,数字2出现的次数。 比如在给定范围[2, 22],数...

2639
来自专栏编程

Python3:复杂数据结构的排序

排序是非常常见的一个场景,相比于Python2,Python3中的排序有不少优化,今天谈一谈Python3中常见排序场景~~更多细节可参考Ref中的Python...

30510
来自专栏浪淘沙

实训day03--循环,内存,数组

2018.06.06 1.switch用法 Scanner sc = new Scanner(System.in); while(t...

1283
来自专栏开发与安全

从零开始学C++之从C到C++(二):引用、数组引用与指针引用、内联函数inline、四种类型转换运算符

一、引用 (1)、引用是给一个变量起别名 定义引用的一般格式:类型  &引用名 = 变量名; 例如:int a=1;  int  &b=a;// b是a的别...

1910
来自专栏zingpLiu

四句话总结JavaScript作用域

前言:JavaScript的作用域一直以来都是前端开发中比较难以理解的知识点,JavaScript6中新引入了 let 关键字,用于指定变量属于块级作用域,本次...

942
来自专栏吾爱乐享

JAVA之学习system类的概述和成员方法

982

扫码关注云+社区

领取腾讯云代金券