类加载器,ClassLoader

命名空间:

•不同的类加载器具有不同的命名空间

•同一命名空间内,类的名称具有惟一性

•同一命名空间内,类之间可以直接交互

•不同命名空间之间,除非显示的提供交互的机制,是不能交互的

 

类加载器:

•启动类加载器(Bootstrap classloader)

•扩展类加载器(Extended classloader)

•系统类加载器(Application classloader)

•Bootstrap加载Extended和Application,Extended的父类为Bootstrap,由于Bootstrap是由C++语言编写,所以Extended的父类为null,Bootstrap在加载Application的时候,将其父类设置为Extended。

 

类加载器的委派模型:

•类加载器总是委托其父类去加载所需要的文件

•如果一个文件A引用另一个文件B,在没有自定义加载B的加载器的情况下,B类只能由A类的加载器或者其父类加载

 

类加载器各自搜索的目录

•每个类加载器所加载文件的目录:

Ø可以通过方法:System.getProperty(String str);方法获得

ØBootstrap:   sun.boot.class.path

ØExtended:    java.ext.dirs

ØApplication:java.class.path

1.Bootstrap Loader(启动类加载器):加载System.getProperty("sun.boot.class.path")所指定的路径或jar。通过System.out.println(System.getProperty("sun.boot.class.path"));打印,发现主要是“D:\Program Files\Java\jdk1.6.0_10\jre\lib”中的jar包。启动类加载器主要加载的是JVM自身需要的类,这个类加载使用C++语言实现的,是虚拟机自身的一部分,它负责将 <JAVA_HOME>/lib路径下的核心类库或-Xbootclasspath参数指定的路径下的jar包加载到内存中,注意必由于虚拟机是按照文件名识别加载jar包的,如rt.jar,如果文件名不被虚拟机识别,即使把jar包丢到lib目录下也是没有作用的(出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类)。

 

2.Extended Loader(标准扩展类加载器ExtClassLoader):加载System.getProperty("java.ext.dirs")所指定的路径或jar。在使用Java运行程序时,也可以指定其搜索路径,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld。扩展类加载器是指Sun公司(已被Oracle收购)实现的sun.misc.Launcher$ExtClassLoader类,由Java语言实现的,是Launcher的静态内部类,它负责加载<JAVA_HOME>/lib/ext目录下或者由系统变量-Djava.ext.dir指定位路径中的类库,开发者可以直接使用标准扩展类加载器。

通过打印System.out.println(System.getProperty("java.ext.dirs"));,可以发现主要加载目录为:

“D:\Program Files\Java\jdk1.6.0_10\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext

 

3、AppClass Loader(系统类加载器AppClassLoader):加载System.getProperty("java.class.path")所指定的路径或jar。在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如: java -cp ./lavasoft/classes HelloWorld。也称应用程序加载器是指 Sun公司实现的sun.misc.Launcher$AppClassLoader。它负责加载系统类路径java -classpath-D java.class.path 指定路径下的类库,也就是我们经常用到的classpath路径,开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器,通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器。
  在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,需要注意的是,Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象,而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式即把请求交由父类处理,它一种任务委派模式,下面我们进一步了解它。

对于eclipse,就是“.classpath”中的jar包。

 

ExtClassLoader和AppClassLoader在JVM启动后,会在JVM中保存一份,并且在程序运行中无法改变其搜索路径。如果想在运行时从其他搜索路径加载类,就要产生新的类加载器。

 

加载类的方法

1. 使用Class静态方法 Class.forName  

    Class cls = Class.forName("com.rain.B"); 
    B b = (B)cls.newInstance(); 

2. 使用ClassLoader  
    /* Step 1. Get ClassLoader */ 
    ClassLoader cl; // 如何获得ClassLoader参考本文最后

    /* Step 2. Load the class */ 
    Class cls = cl.loadClass("com.rain.B"); // 使用第一步得到的ClassLoader来载入B 
     
    /* Step 3. new instance */ 
    B b = (B)cls.newInstance(); // 有B的类得到一个B的实例 

3. 直接new  
    B b = new B();

 

使用这段代码打印出来的

StringBuilder sb  = new StringBuilder(100);
        sb.append("java.class.path: ");
        sb.append(System.getProperty("java.class.path"));
        sb.append("\n\n");
        sb.append("java.boot.class.path: ");
        sb.append(System.getProperty("java.boot.class.path"));
     
        textView.setText(sb.toString());

每个进程对应一个虚拟机实例,而每个实例并不是对应不同的BootClassLoader、ExtClassLoader和SystemClassLoader,因为从ClassLoader类中的代码可以知道都是一个单例。所以说系统总共就只有一个。因为如果每个虚拟机对应一个的话,而不同加载器有独立的命名空间,那么每个进程都会去重新加载系统所有的jar包,那显然是不合理的。而每个加载器的搜索路径是不可以在运行时改变的,那么SystemClassLoader是如何变化搜索路径的呢,因为SystemClassLoader需要加载应用的字节码文件,即java.class.path中是写了什么路径。答案是'.',即一个点,表示当前路径,当前路径的绝对路径随着进程环境因上下文不同而不同。系统应用的apk保存在system/app和system/priv-app,而普通应用保存在/data/app。

 

BootStrap ClassLoader是C++写的,是虚拟机程序一部分。而ExtClassLoader和AppClassLoader是sun公司用java写的,独立于虚拟机程序。 进程之间是不能共享变量的,即使是静态变量也是每个进程都会有一份的。ExtClassLoader和AppClassLoader也是每个虚拟机实例都会有一个的,而每个ClassLoader实例都会有自己独立的命名空间。而在一个进程中,类加载器的关系是BootStrapClassLoader ->Extension ClassLoader -> Application ClassLoader。ExtClassLoader和AppClassLoader是java类,所以进程之间必定不能共享,所以这个两个加载器加载的类是每个进程都可以单独加载一个份的,而BootStrapClassLoader是C++的,这个加载器会不会被所有进程共用就不得而知了。反正每个进程都是独立加载类的,所以别的进程也无法篡改 公用的类,因为即使A进程加载了一个全类名和公用类一样的类,别的进程也会因为BootStrapClassLoader不一样而找不到这个加载过的类,又去把这个公用类加载到自己的内存中。

自定义ClassLoader,不复写ClassLoader#loadClass,复写findClass,因为loadClass是维护委托机制的,而find让某个ClassLoader去完成加载的。复写时,一般只需让ClassLoader#getClassLoader()作为父加载器就行了。

 

深入理解Java类加载器(ClassLoader)

深入探讨 Java 类加载器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值