java中的类加载

Class实例

java在真正需要一个类时才由Java虚拟机JVM加载类,所谓真正需要是要通过类来构造对象或者用户自己指定要加载类。被夹在的类在java虚拟机JVM中都以一个Class实例存在。Class对象没有公开的构造器,Class对象有虚拟机JVM自动产生。也就是说,每一个类被加载,JVM虚拟机就自动为该类产生一个Class实例。

Class的信息是在编译时期就被加入至.class文中,这是Java支持运行时类型识别(RTTI)的一种方式。一个类在JVM中只有一个Class实例,每个类的实例都会记住自己是有那个Class实例所生成。

使用Class.forName()加载类

Class的静态方法有两个版本,一个版本只需指定类名,而另一个版本可以指定类名称,加载类时是否运行静态区,指定类加载器:

static Class forName(String name, boolean initialize, ClassLoader loader)

在使用第二个版本时,可以将initialize设置为false,这样加载类时就不会运行静态区。

下面是测试类的代码:

package onlyfun.caterpillar;
    public class TestClass2 {
        static {
            System.out.println("[运行静态区]");
        }
    }
package onlyfun.caterpillar;

public class ForNameDemoV1 {
    public static void main(String[] args) {
        try {
            System.out.println("载入TestClass2");
            Class c = Class.forName("onlyfun.caterpillar.TestClass2"); //使用forName的第一个版本加载类
            
            System.out.println("使用TestClass2声明参考名称");
            TestClass2 test = null;
            
            System.out.println("使用TestClass2建立对象");
            test = new TestClass2();
        } catch (ClassNotFoundException e) {
            System.out.println("找不到指定的类");
        }
    }
}

运行结果:

从结果看,当使用forName方法第一个版本加载类时,静态区立即运行

下面是forName的第二个版本

package onlyfun.caterpillar;

public class ForNameDemoV2 {
    public static void main(String[] args) {
        try {
            System.out.println("载入TestClass2");
            //用forName的第二个版本加载类,initialize设置为false,不会运行静态区, 并指明了类加载器
            Class c = Class.forName("onlyfun.caterpillar.TestClass2", false, Thread.currentThread().getContextClassLoader());
            
            System.out.println("使用TestClass2声明参考变量");
            TestClass2 test = null;
            
            System.out.println("使用TestClass2建立对象");
            test = new TestClass2();
        } catch (ClassNotFoundException e) {
            System.out.println("找不到指定的类");
        }
    }
}

运行结果:

从结果看,用forName方法加载TestClass2时并没有运行静态区

类加载器

当运行java程序的时候,运行程序会找到JRE的安装目录,然后寻找jvm.dll(默认在JRE目录下bin\client目录中), 接着JVM启动并初始化,产生Bootstrap Loader, Bootstrap Loader加载Extended Loader,并设置Bootstrap Loader为其parent,然后Extended Loader(也就是ExtClassLoader)加载System Loader(也就是AppClassLoader),并设置Extended Loader为其parent。

Bootstrap Loader用C编写,其他两个Loader为java语言编写

Bootstrap Loader会搜索系统参数sun.boot.class.path中指定的类,默认是JRE所在目录的classes下的.class文件,或者lib目录下的.jar文件中的类(如rt.jar)并加载。可以通过System.getProperty("sun.boot.class.path")语句显示sun.boot.class.path中指定的路径。

Extended Loader会搜索系统参数java.ext.dirs中指定的类,默认是JRE目录下的lib/ext/classes目录下的.class文件,或者lib/ext目录下的.jar文件(如 rt.jar)中的类并加载。可以通过System.getProperty("java.ext.dirs")语句来显示java.ext.dirs中指定的路径。

System Loader会搜索系统参数java.class.path中指定的类,也就是Classpath所指定的路径,默认是当前工作路径下的.class文件。可以使用System.getProperty("java.class.path")来显示java.class.path中指定的路径。在java运行时,也可以通过-cp来覆盖原来的Classpath设置:java -cp ./classes someClass.

每个类加载器都会先将加载任务交给其parent,如果parent找不到,再由正自己负责,如果还找不到,就抛出NoClassDefFoundError。

类加载器在java中都已java.lang.ClassLoader类型存在,每一个加载的类都有一个Class实例代表,而每个Class实例都会记住自己是哪一个类加载器加载的。可以有Class的getClassLoader方法取得该类的ClassLoader,耳从ClassLoader的getParent方法可以取到自己的parent。

取得ClassLoader的实例后,可以使用loadClass方法来加载类。这个方法不会运行静态区。

package onlyfun.caterpillar;

public class ForNameDemoV3 {
    public static void main(String[] args) {
        try {
            System.out.println("使用类加载器加载类");
            ClassLoader loader = ForNameDemoV3.class.getClassLoader();//获取类加载器
            Class c = loader.loadClass("onlyfun.caterpillar.TestClass2");
        
            System.out.println("使用TestClass2声明参考名称");
            TestClass2 test = null;
        
            System.out.println("使用TestClass2建立对象");
            test = new TestClass2();
        } catch (ClassNotFoundException e) {
            System.out.println("找不到指定的类");
        }
    }
}

运行结果:

使用自己的ClassLoader

ExClassLoader和AppClassLoader都是java.net.URLClassLoader的子类,可以在使用java时用下列指令指定ExtClassLoader的所搜路径:

java -Djava.ext.dirs = c:\workspace\Yourclass

可以用-cp或者-classpath指定AppClassLoader的搜索路径:

java -cp c:\workspace\Yourclass

可以使用URLClassLoader来产生新的类加载器,它需要java.net.URL作其参数指定加载类时的搜索路径:

URL url = new URL("file:/d:/workspace/");
ClassLoader  urlClassLoader = new URLClassLoader(new URL[] {url});
Class c = urlClassLoader.loadClass("Someclass");

新建的ClassLoader会将其parent设置为AppClassLoader

由同一个类加载器加载的类文件,会只有一份Class实例,如果同一个类文件有两个不同的ClassLoader载入,就会有两份不同的Class实例

原文地址:https://www.cnblogs.com/chaoguo1234/p/2962072.html