深入理解Java(一)类加载原理

本文转载自http://colinwang.cn/2014/12/12/UnderstandingTheJavaClassLoadingPrinciple/
感觉作者写的很好,就转载了。

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. 静态域的初始化在类的静态初始化期间,非静态域的初始化时在类的实例创建期间,这意味这静态域初始化在非静态域之前
  7. 非静态域通过构造器初始化,子类在做任何初始化之前构造器会隐含地调用父类的构造器

示例代码

定义父类

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需要保证父类的成员初始化早于子类的成员初始化,否则,在子类中使用父类的成员变量就会出现问题)。当这个类被实例化的时候,会初始化类的非静态部分。因为静态部分是属于类本身的,类初始化就要初始化它。而非静态部分是属于具体实例的,所以在类被实例化时初始化。

猜你喜欢

转载自blog.csdn.net/niuwei22007/article/details/50883713
今日推荐