我们知道,.java文件会被编译成.class文件。JVM虚拟机会把.class文件在必要的时候load到内存中。
那么,class究竟是在什么时候被load的呢?启动的时候就load吗?
不同的虚拟机有不同的策略,但是最根本的策略是:“只有在需要该class的时候,才会把class load到内存”
举个例子:
有两个java文件,TestJava.java和A.java
//TestLoader.java
public class TestLoader {
public static void main(String[] args) {
System.out.println("test");
}
}
//A.java
public class A {
public void method(){
System.out.println("inside of A");
}
}
工程的目录结构如下所示:
下面,我们通过命令启动程序,看看类是什么时候被加载的。在命令行中,添加-verbose:class
参数,可以查看类被加载的情况:
java -verbose:class -classpath /home/ron/workspace/UltimateTest/bin/ compiler.TestLoader
部分输出为:
[Loaded sun.misc.JavaSecurityProtectionDomainAccess from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.security.ProtectionDomain from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.security.ProtectionDomain from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.security.Principal from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded compiler.TestLoader from file:/home/xiwang/workspace/UltimateTest/bin/]
test
[Loaded java.lang.Shutdown from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
可见,只有TestJava
被加载了。
我们将TestJava.java修改一下:
//TestJava.java
public class TestLoader {
public static void main(String[] args) {
System.out.println("test");
A a = new A();
a.method();
}
}
执行上面同样的命令,部分输出为:
[Loaded sun.misc.JavaSecurityProtectionDomainAccess from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.security.ProtectionDomain from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.security.ProtectionDomain from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.security.Principal from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded compiler.TestLoader from file:/home/xiwang/workspace/UltimateTest/bin/]
test
[Loaded compiler.A from file:/home/xiwang/workspace/UltimateTest/bin/]
inside of A
[Loaded java.lang.Shutdown from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
[Loaded java.lang.Shutdown$Lock from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
可见,A这时候被load了。
再举个例子:
public class SomethingCaller {
public static Something something = null; // (1) does not cause class loading
public static Class<?> somethingClass = Something.class; // (2) causes class loading
public void doSomething() {
new Something(); // (3) causes class loading
}
}
上面的(1)没有触发类加载,因为这个语句不需要知道Something
这个类的结构,也就是不需要这个类
(2)和(3)是触发了类加载的。因为,对(2)来讲,Something.class
这样的语句是需要知道Something
的类结构的。同样,对(3)来讲,调用构造函数,更是需要知道类结构的。也就是在需要这个类的时候,类才被加载。
例子3:
public class SomethingTest {
public static void main(String[] args) {
new SomethingCaller();
}
}
public class SomethingCaller {
public void doSomething() {
Class<?> somethingClass = Something.class;
}
}
public class Something {}
上面的代码不会触发Something
的类加载。但是如果doSomething()
方法被调用的话,就会触发Something
的类加载。