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

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

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

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

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

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

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

TestLoader.java源代码如下:

01 /**
02  * Coder: D瓜哥,http://www.diguage.com/
03  */
04 package compiler;
05  
06 public class TestLoader {
07     public static void main(String[] args) {
08         System.out.println("test");
09     }
10 }

A.java源代码如下:

01 /**
02  * Coder: D瓜哥,http://www.diguage.com/
03  */
04 package compiler;
05  
06 public class A {
07     public void method() {
08         System.out.println("inside of A");
09     }
10 }

在Eclipse中,目录结构如下:

目录结构

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

1 java -verbose:class compiler.TestLoader

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

01 [Opened /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
02 [Loaded java.lang.Object from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
03 [Loaded java.io.Serializable from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
04 [Loaded java.lang.Comparable from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
05 [Loaded java.lang.CharSequence from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
06 [Loaded java.lang.String from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
07 [Loaded java.lang.reflect.GenericDeclaration from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
08 [Loaded java.lang.reflect.Type from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
09 [Loaded java.lang.reflect.AnnotatedElement from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
10 [Loaded java.lang.Class from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
11 [Loaded java.lang.Cloneable from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
12 [Loaded java.lang.ClassLoader from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
13 [Loaded java.lang.System from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
14 ……
15 [Loaded sun.misc.URLClassPath$FileLoader$1from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
16 [Loaded sun.nio.ByteBuffered from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
17 [Loaded java.security.PermissionCollection from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
18 [Loaded java.security.Permissions from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
19 [Loaded java.net.URLConnection from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
20 [Loaded java.security.BasicPermissionCollection from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
21 [Loaded compiler.TestLoader from file:/Users/diguage/Documents/darticle/series/SimpleJava/src/]
22 [Loaded java.lang.Void from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
23 test
24 [Loaded java.lang.Shutdown from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
25 [Loaded java.lang.Shutdown$Lock from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]

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

01 /**
02  * Coder: D瓜哥,http://www.diguage.com/
03  */
04 package compiler;
05  
06 public class TestLoader {
07     public static void main(String[] args) {
08         System.out.println("test");
09         A a = new A();
10         a.method();
11     }
12 }

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

01 [Opened /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
02 [Loaded java.lang.Object from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
03 [Loaded java.io.Serializable from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
04 [Loaded java.lang.Comparable from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
05 [Loaded java.lang.CharSequence from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
06 [Loaded java.lang.String from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
07 [Loaded java.lang.reflect.GenericDeclaration from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
08 [Loaded java.lang.reflect.Type from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
09 [Loaded java.lang.reflect.AnnotatedElement from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
10 [Loaded java.lang.Class from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
11 [Loaded java.lang.Cloneable from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
12 [Loaded java.lang.ClassLoader from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
13 [Loaded java.lang.System from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
14 ……
15 [Loaded sun.misc.URLClassPath$FileLoader$1from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
16 [Loaded java.io.FilePermissionCollection from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
17 [Loaded java.security.AllPermission from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
18 [Loaded java.security.UnresolvedPermission from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
19 [Loaded java.security.BasicPermissionCollection from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
20 [Loaded compiler.TestLoader from file:/Users/diguage/Documents/darticle/series/SimpleJava/src/]
21 [Loaded java.lang.Void from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
22 test
23 [Loaded compiler.A from file:/Users/diguage/Documents/darticle/series/SimpleJava/src/]
24 inside of A
25 [Loaded java.lang.Shutdown from /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home/jre/lib/rt.jar]
26 [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瓜哥,http://www.diguage.com/
原文链接: http://www.diguage.com/archives/77.html
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值