1.类加载器简述
(1).类加载器功能
把类从静态的.class文件中,经过虚拟机过滤而装入主存而成为字节码文件!
(2).类加载器载入方式
1).预先载入
基础类库是一次性载入的,因为基础库中包含java程序执行所必需的类,只是在起始是需要时间!
2).需求载入
自己编写的类只会在用到的时候才载入,仅仅声明一个类型是不会被加载的,通过实例化或者这四种方式>>才会加载进主存。如果该类有父类,载入该类之前应确保父类加载,即父类是在子类之前加载,同样遵循委托机制!
2.类加载器详述
(1).类加载器的加载过程
虚拟机启动后,会完成一些初始化动作,在这过程中产生第一个类加载器Bootstrap Loader,Bootstrap Loader是由C++编写的,这个类加载器还会加载其它的类加载器,即sun.msic.Launcher.java中的ExtClassLoader(内部类)编译成Launcher$ExtClassLoader.class,并设定其parent为null,代表其父类加载器为Bootstrap Loader。然后Bootstrap Loader还会加载该java文件Launcher.java中的AppClassLoader编译成Launcher$AppClassLoader.class,并设定其parent为之前产生的ExtClassLoader实例。
注意:Launcher$ExtClassLoader.class与Launcher$AppClassLoader.class都是由Bootstrap Loader所载入,所以parent和由那个类加载器载入没有关系,可以通过下面代码检验!
Person p = new Person(1);
System.out.println(p.getClass().getClassLoader());//sun.misc.Launcher$AppClassLoader@fb56b1
System.out.println(p.getClass().getClassLoader().getClass().getClassLoader());//null
/*
* 得到验证AppClassLoader加载器的加载器是null即Bootstrap Loader,parent不能真实反映
* 只是AppClassLoader在加载后的Class对象中,Bootstrap Loader修改了该parent值为ExtClassLoader
*/
(2).类加载器的类别
jvm中默认的classloader有Bootstrap ClassLoader、Extension ClassLoader、App ClassLoader
1).Bootstrap ClassLoader
用C++编写的,在Java中看不到它,是null,是JVM自带的类装载器,负责加载java基础类,即目录/lib/rt.jar,可以通过System.getProperty("sun.boot.class.path")
2).Extension ClassLoader
负责加载java扩展类,即目录/lib/ext下的jar和class,可以通过System.getProperty("java.ext.dirs")来取得
3).App ClassLoader
负责加载当前java应用的classpath中的所有类和JAR,可以通过 System.getProperty("java.class.path")来取得,其加载该加载器的加载器是Bootstrap ClassLoader,但是Bootstrap ClassLoader修改了该parent值
注意:AppClassLoader和Bootstrap Loader只会搜索上述指定路径下的文件,因为AppClassLoader和ExtClassLoader在整个虚拟机中只存在一份,一旦建立了,其内部所参考的搜索路径将不再改变,即使我们在程序里利用System.setProperty()来改变系统参数的内容,仍然无法更改搜索路径。
3.类加载器的委托机制
(1).首先是当前线程的类加载器去加载线程中的第一个类。
(2).如果类A中引用了类B,Java虚拟机将使用加载类A的类加载器来加载类B。
(3).还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
java.lang.System在BootStrap中最先加载,但是可以写一个类加载器来加载我们自己写的java.lang.System类,即该加载器 loadClass方法不委托。
4.自定义类加载器
(1).准备的class
MyLoader,自定义的类加载器,继承了ClassLoader
InterTest接口,用于供Worker实现,抽象了Worker的方法(注意重要性,在Test中得到class是用于强转)
Worker类,需要自定义的类加载器加载
Test,用于测试类加载器
(2).自定义类加载器实现过程
1).调用 findLoadedClass(String) 来检查是否已经加载类。
2).在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。
3).调用 findClass(String) 方法查找类。
(3).代码实现
/*
* @author strawberry2013
* @fie Test.java
* 测试类加载器
*/
public class Test {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class clazz = new MyLoader().loadClass("Worker"); //通过类加载器加载任一路径下的正确编译的class文件
InterTest test = (InterTest)clazz.newInstance(); //接口强转的重要
/*
* 此处强转应该使用该类的父类或者接口,其中clazz得到的是Worker的字节码文件
* Worker test = (Worker)clazz.newInstance(); 强转的Worker默认加载不存在该字节码文件,故发生错误
*/
test.work();
}
}
/*
* 类加载器
* @file MyLoader.java
*/
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class MyLoader extends ClassLoader{
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String path = "E:/Android/Project/Test/"+name+".class";//欲加载一个任一路径下的class文件
FileInputStream fi = null;
ByteArrayOutputStream bt = null;
try {
fi = new FileInputStream(path);
bt = new ByteArrayOutputStream();
int p = 1;
while((p=fi.read()) != -1){
bt.write(p); //将输入流写入内存流
}
} catch (Exception e) {
e.printStackTrace();
} finally{
try {
fi.close();
bt.close();
} catch (IOException e) {
e.printStackTrace();
}
}
byte[] bytes = bt.toByteArray(); //将内存中的流转换为byte
return defineClass(bytes, 0, bytes.length);//生成内存中的字节码
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
return super.loadClass(name); //如果不想使用委托机制,可以修改这个函数
}
}
/*
* @file InterTest.java
* 供Worker实现,覆写方法
*/
public interface InterTest {
public void work();
}
/*
* @file Worker.java
*/
public class Worker implements InterTest{
public void work(){
System.out.println("work........~~..");
}
}