简易Java(03):Java类何时以及如何加载并初始化?
开发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虚拟机规范,有且仅有如下五种情况必须立即对类进行“初始化”(而加载、验证、准备自然要在此之前开始):
- 遇到new、getstatic、putstatic或in-vokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候。
- 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
- 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。
- 当使用JDK 1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_in-vokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
《Simple Java》翻译,《简易Java》走起!
参考资料
原文链接:https://wordpress.diguage.com/archives/77.html
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。