类创建的方式
- 隐式加载:不通过在代码里调用ClassLoader来加载需要的类,而是通过JVM来自动加载需要的类到内存,例如:当类中继承或者引用某个类时,JVM在解析当前这个类不在内存中时,就会自动将这些类加载到内存中。
- 显示加载:在代码中通过ClassLoader类来加载一个类,例如调用this.getClass.getClassLoader().loadClass()或者Class.forName()。
JDK 默认提供了如下几种ClassLoader
-
Bootstrp loader
Bootstrp加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载%JAVA_HOME%/jre/lib
,-Xbootclasspath
参数指定的路径以及%JAVA_HOME%/jre/classes
中的类。
-
ExtClassLoader
Bootstrp loader加载ExtClassLoader,并且将ExtClassLoader的父加载器设置为Bootstrp loader.ExtClassLoader是用Java写的,具体来说就是 sun.misc.Launcher$ExtClassLoader,ExtClassLoader主要加载%JAVA_HOME%/jre/lib/ext
,此路径下的所有classes目录以及java.ext.dirs
系统变量指定的路径中类库。 -
AppClassLoader
Bootstrp loader加载完ExtClassLoader后,就会加载AppClassLoader,并且将AppClassLoader的父加载器指定为 ExtClassLoader。AppClassLoader也是用Java写成的,它的实现类是 sun.misc.Launcher$AppClassLoader,另外我们知道ClassLoader中有个getSystemClassLoader
方法,此方法返回的正是AppclassLoader.AppClassLoader主要负责加载classpath所指定的位置的类或者是jar文档,它也是Java程序默认的类加载器。
ClassLoader加载类的过程
- 找到.class文件并把这个文件加载到内存中
- 字节码验证,Class类数据结构分析,内存分配和符号表的链接
- 类中静态属性和初始化赋值以及静态代码块的执行
为什么要使用双亲委托这种模型呢?
因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义的类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以用户自定义的ClassLoader永远也无法加载一个自己写的String,除非你改变JDK中ClassLoader搜索类的默认算法。
自定义ClassLoader
package com.asiainfo.crm.engine.multi;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
*
*
* @ClassName: PathClassLoader
* @Description: java类作用描述
* @Author: 孤独的main()函数
* @CreateDate: 2019/2/26 18:46
* @UpdateUser: 更新者
* @UpdateDate: 2019/2/26 18:46
* @UpdateRemark: 更新说明
* @Version: 1.0
*/
public class PathClassLoader extends ClassLoader{
public static final String drive = "d://";
public static final String fileType = ".class";
public static void main(String[] args) throws Exception
{
PathClassLoader loader = new PathClassLoader();
Class<?> objClass = loader.loadClass("CommonServiceMultiController", true);
Object obj = objClass.newInstance();
System.out.println(objClass.getName());
System.out.println(objClass.getClassLoader());
System.out.println(obj.getClass().toString());
}
@Override
public Class<?> findClass(String name)
{
byte[] data = loadClassData(name);
return defineClass(name, data, 0, data.length);// 将一个 byte 数组转换为 Class// 类的实例
}
public byte[] loadClassData(String name)
{
FileInputStream fis = null;
byte[] data = null;
try
{
fis = new FileInputStream(new File(drive + name + fileType));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int ch = 0;
while ((ch = fis.read()) != -1)
{
baos.write(ch);
}
data = baos.toByteArray();
} catch (IOException e)
{
e.printStackTrace();
}
return data;
}
}