类加载器,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中保存一份,并且在程序运行中无法改变其搜索路径。如果想在运行时从其他搜索路径加载类,就要产生新的类加载器。
加载类的方法
- 使用Class静态方法 Class.forName
Class cls = Class.forName(“com.rain.B”);
B b = (B)cls.newInstance();
- 使用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的实例
- 直接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()作为父加载器就行了。
还没有评论,来说两句吧...