首页 > Java, 挨踢(IT) > 简易Java(03):Java类何时以及如何加载并初始化?

简易Java(03):Java类何时以及如何加载并初始化?

2014年5月14日 发表评论 阅读评论 750 人阅读    

开发Java程序时,我们想编辑一个.java文件,然后该文件被编译器编译成.class文件。Java在运行时,自己加载所需要的类。但是,加载初始化有什么不同,却让人难以区分。一个Java类在何时以及如何被加载和初始化呢?让我们通过下面的例子来给大家清晰地描述一下。

1、 “加载一个类”是什么意思?

在C/C++中,首先被编译成本地机器代码,然后在编译之后有一个链接过程。所谓链接,就是将不同地方的源代码搜集起来,然后生成一个可执行文件。但是,Java并不这样。在Java中,类似链接的过程是在加载到Java虚拟机时完成的。

不同Java虚拟机,加载类的方式也不同。(D瓜哥注:即使同一个虚拟机,不同的类,加载方式也可能不同。)但是,最基本的加载规则是按需加载。如果已经加载过的类需要其他类,则这些被需要的类就加载。这个加载过程是递归进行的。

2、一个类何时以及如何加载?

在Java语言中,加载规则由ClassLoade来处理。下面,通过一个简单的程序来演示一下如何以及何时加载一个类。

TestLoader.java源代码如下:

/**
 * Coder: D瓜哥,http://www.diguage.com/
 */
package compiler;

public class TestLoader {
    public static void main(String[] args) {
        System.out.println("test");
    }
}

A.java源代码如下:

/**
 * Coder: D瓜哥,http://www.diguage.com/
 */
package compiler;

public class A {
    public void method() {
        System.out.println("inside of A");
    }
}

在Eclipse中,目录结构如下:

目录结构

运行如下命令,我们就可以看到每一个类的加载信息。-verbose:class参数就是用于显示每一个类的加载信息的。

java -verbose:class compiler.TestLoader

部分输出如下(输出将近四百行,所以删除中间部分,保留开头以及结尾部分):

[Opened /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Object from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.io.Serializable from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.String from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.GenericDeclaration from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.Type from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.AnnotatedElement from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Class from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Cloneable from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ClassLoader from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.System from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
……
[Loaded sun.misc.URLClassPath$FileLoader$1 from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded sun.nio.ByteBuffered from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.security.PermissionCollection from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.security.Permissions from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.net.URLConnection from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.security.BasicPermissionCollection from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded compiler.TestLoader from file:/Users/diguage/Documents/darticle/series/SimpleJava/src/]
[Loaded java.lang.Void from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
test
[Loaded java.lang.Shutdown from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]

现在,我们将TestLoader.java修改成如下内容:

/**
 * Coder: D瓜哥,http://www.diguage.com/
 */
package compiler;

public class TestLoader {
    public static void main(String[] args) {
        System.out.println("test");
        A a = new A();
        a.method();
    }
}

再次运行上面同样的执行命令,这次的输出如下:

[Opened /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Object from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.io.Serializable from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.String from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.GenericDeclaration from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.Type from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.AnnotatedElement from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Class from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Cloneable from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.ClassLoader from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.System from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
……
[Loaded sun.misc.URLClassPath$FileLoader$1 from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.io.FilePermissionCollection from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.security.AllPermission from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.security.UnresolvedPermission from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.security.BasicPermissionCollection from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded compiler.TestLoader from file:/Users/diguage/Documents/darticle/series/SimpleJava/src/]
[Loaded java.lang.Void from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
test
[Loaded compiler.A from file:/Users/diguage/Documents/darticle/series/SimpleJava/src/]
inside of A
[Loaded java.lang.Shutdown from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]

我们可以输出中,高亮部分显示是不同。A.class只有在被调用时才加载。总结一下,一个类在如下情况下才会被加载:

  • 当遇到new字节码被执行时。例如,SomeClass f = new SomeClass();;
  • 当静态引用一个类时。例如,System.out;

一个类在首次被另外一个类引用时才进行初始化。当一个类被加载时,并用初始化。

Java虚拟机将先初始化父类,然后安装书写顺序初始化静态变量和常量,同时在初始化之前赋予每个属性一个默认值。

Java类实例的初始化是展示属性、静态属性和构造函数执行顺序的完美示例。

D瓜哥注:

根据Java虚拟机规范,有且仅有如下五种情况必须立即对类进行“初始化”(而加载、验证、准备自然要在此之前开始):

  1. 遇到new、getstatic、putstatic或in-vokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
  2. 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
  3. 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
  4. 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
  5. 当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_in-vokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

《Simple Java》翻译,《简易Java》走起!

参考资料



作 者: D瓜哥,https://www.diguage.com/
原文链接:https://wordpress.diguage.com/archives/77.html
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

分类: Java, 挨踢(IT) 标签: ,
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.