深入理解Java类加载原理

虽然每天都在用Java,现在做的项目后台也是用的Java语言,近期帮师兄做的算法实验也是用Java实现的,但是总觉得自己对这门语言学习的不够深入,仅仅停留在会用的层面上。今天看了一位中科大师姐的文章,觉得自己很有必要再深入学习这门语言,还需要加深自己对Java的理解。我个人觉得,对一个程序员来说,理解力十分重要。一门编程语言,学会使用很容易,但是要真正理解这门语言的设计哲学似乎就没那么容易了。而一但真正理解了,那么就会对自身有一个质的提升。


接下来就谈谈正题吧,Java类加载机制这个词相信对于任何一个使用过Java的人都不会陌生,但是并不是每一个写Java程序的人都十分清楚Java类是什么时候被加载的,是什么时候被初始化的,下面我们就来分析一下吧。

类的加载

首先要清楚,类的加载和类的初始化是不同的。类的加载是由类加载器完成的,类加载器也是一个程序,如ClassLoader类就是一个类加载器,它也是用Java语言写的,可以通过继承并重写它的方法来实现自己的类加载方式。常见的类加载方式是当用到这个类的时候,系统才加载这个类。一个JVM中可以有多个类加载器,如下图

Java类加载器的树状结构

类的初始化

而类的初始化发生在类加载完成之后,这时会把类的静态属性初始化。当类发生以下情况时也会初始化这个类:

  1. 通过使用new关键字创建实例
  2. 使用Class.forName()反射创建实例(有可能导致ClassNotFoundException)
  3. 类的静态方法被调用
  4. 类的静态域被赋值
  5. 类的静态域被访问,而且它不是常量(没有final修饰,被final关键字修饰的常量为编译时常量,不会触发类的初始化)
  6. 在顶层类中执行assert语句

类的初始化步骤

  1. 类按照代码从上到下的顺序初始化,所以声明在顶部的字段初始化早于底部的字段
  2. 超类的初始化早于子类和衍生类
  3. 如果类的初始化是由于访问静态域而触发,那么只有该声明静态域的类被初始化,而不会触发超类或者子类的初始化,即使该类的静态域被子类或子接口或者它的实现类所引用
  4. 接口初始化不会导致父接口的初始化
  5. 静态域的初始化在类的静态初始化期间,非静态域的初始化时在类的实例创建期间,这意味这静态域初始化在非静态域之前
  6. 非静态域通过构造器初始化,子类在做任何初始化之前构造器会隐含地调用父类的构造器

示例代码

定义父类

package org.colin.classinit;
public class SuperClass {
    static {
        System.out.println("SuperClass:Static block of Super class is initialized!");
    }
    {
        System.out.println("SuperClass:Non static block of Super class is initialized!");
    }
}

定义子类

package org.colin.classinit;
public class SubClass extends SuperClass {
    static {
        System.out.println("SubClass:Static block of Sub class is initialized!");
    }
    {
        System.out.println("SubClass:Non static blocks of Sub class is initialized!");
    }
}

定义一个不被使用的类

package org.colin.classinit;
public class NotUsedClass {
    static {
        System.out.println("NotUsedClass:NotUsedClass has been initialized!");
    }
}

测试类

package org.colin.classinit;
public class ClassInitializationTest {
    public static void main(String[] args) {
        NotUsedClass notUsedClass = null;
        SubClass subClass = new SubClass();
        System.out.println((Object) notUsedClass == (Object) subClass);
    }
}

输出结果为:

SuperClass:Static block of Super class is initialized!
SubClass:Static block of Sub class is initialized!
SuperClass:Non static block of Super class is initialized!
SubClass:Non static blocks of Sub class is initialized!
false

由此可见:
1. 超类初始化早于子类
2. 静态变量或代码块初始化早于非静态块和域
3. 没使用的类根本不会被初始化,因为他没有被使用

总结

Java类在第一次被用到的时候,被类加载器加载,接着初始化静态部分(被final关键字修饰的常量不会被初始化,因为它在编译的时候就已经确定了),如果有父类,会先进行父类的初始化(Java需要保证父类的成员初始化早于子类的成员初始化,否则,在子类中使用父类的成员变量就会出现问题)。当这个类被实例化的时候,会初始化类的非静态部分。因为静态部分是属于类本身的,类初始化就要初始化它。而非静态部分是属于具体实例的,所以在类被实例化时初始化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值