java类加载器

来源:互联网 发布:苹果手机健身软件 编辑:程序博客网 时间:2024/06/11 13:58

类加载器

java虚拟机代码存储在.class结尾的类文件当中,但是运行程序的机器并不能识别class文件,这就需要一种机制将class文件解释为机器能够识别的代码,这就是类加载器

类加载器的分类

类加载器分为以下三类:

  • BootStrap ClassLoader(引导类加载器或者叫启动类加载器)
  • ExtClassLoader(扩展类加载器)
  • AppClassLoader(系统类加载器或者叫应用类加载器)

    BootStrap ClassLoader是虚拟机的一部分,它是由C语言实现的,通常从rt.jar文件中加载(也即java.开头的类文件)。BootStrap ClassLoader没有对应的ClassLoder对象,比如以下方法:
String.class.getClassLoader()

该方法执行后会返回null。
ExtClassLoader从jre/lib/ext目录加载“标准的扩展”,如果将jar文件放入该目录,即时没有任何类路径,ExtClassLoder也可以找到其中的各个类。AppClassLoader用于加载应用类,它对CLASSPATH环境变量或者-classpath指定的目录或者jar文件进行加载。

每个线程都有一个对类加载器的引用,称为上下文类加载器。主线程的上下文类加载器是AppClassLoader,当新线程创建时,它的上下文类加载器被设置为创建它的线程的上下文类加载器。比如以下方法:

public static void main(String[] args) throws InterruptedException {        System.out.println(Thread.currentThread().getContextClassLoader());        new Thread(new Runnable() {            public void run() {                System.out.println(Thread.currentThread().getContextClassLoader());            }        }).start();    }

该方法执行后会输出:

sun.misc.Launcher$AppClassLoader@14dad5dcsun.misc.Launcher$AppClassLoader@14dad5dc

类的加载

类的加载有三种方式:

  • 启动应用时由jvm进行加载;
  • 使用Class.forName进行动态加载;
  • 使用ClassLoader的loadClass方法进行动态加载。


    第一种方式如前所述,不进行赘述。Class.forName方法和ClassLoader的loadClass方法有一些区别,主要如下:

  • Class.forName有两个,一个是Class.forName(String name), 一个是Class.forName(String name, boolean initialize, ClassLoader loader), 执行Class.forName(className)等同于Class.forName(className, true, currentLoader), 会对类进行初始化;

  • ClassLoader的loadClass方法单纯进行类加载,不进行初始化操作,只有在调用newInstance方法时才会进行初始化操作。

    我们定义一个具有static块的类对上述逻辑进行测试,
public class StaticTest {    static {        System.out.println("static test");    }}

使用以上测试类执行以下代码:

public static void main(String[] args) throws InterruptedException, ClassNotFoundException,            IllegalAccessException, InstantiationException {        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();        Class a = classLoader.loadClass("com.cmcc.hy.StaticTest"); //此处不会触发static语句块执行        a.newInstance();//此处会触发static语句块执行        Class b = Class.forName("com.cmcc.hy.StaticTest");//此处不会触发static语句块执行        b.newInstance();//此处不会触发static语句块执行        Class.forName("com.cmcc.hy.StaticTest", true, classLoader);//此处不会触发static语句块执行    }

执行之后,会输出一次“static test”,ClassLoader的loadClass的方法不会进行初始化, newInstance方法执行时如果Class已经初始化,则不进行初始化操作,否则进行初始化,那么a.newInstance会进行初始化操作,从而触发static语句块执行,Class.forName原本会进行初始化,但是根据类的加载原则,所有的Class只会加载一次,所以b本质上和a是相同的引用,那么b.newInstance也不会进行初始化操作,最后一句也和此相同,不会进行初始化操作。

原创粉丝点击