一、类加载流程
类加载指的是一个.class文件的加载,在Java中.class文件可能是一个类,也可能是一个接口。此处都叫做类加载。整个类加载的过程即:加载→验证→准备→解析→初始化。
在链接过程阶段,给对类变量(static修饰的)赋初始值(比如int型默认为0),在初始化阶段会给类变量赋值,执行静态代码块
类加载器,顾名思义就是用来加载类的,将类的字节码文件通过Io流从硬盘读到内存中,但是其作用不仅仅是加载类。因为对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器都拥有一个独立的类名称空间。
二、java类加载器分类:
1、 启动(Bootstrap)类加载器
启动类加载器主要加载的是JVM自身需要的类,这个类加载使用C++语言实现的,是虚拟机自身的一部分,它负责将 <JAVA_HOME>/lib
路径下的核心类库或-Xbootclasspath
参数指定的路径下的jar包加载到内存中,注意必由于虚拟机是按照文件名识别加载jar包的,如rt.jar,如果文件名不被虚拟机识别,即使把jar包丢到lib目录下也是没有作用的(出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类)。
2、 扩展(Extension)类加载器
扩展类加载器是指Sun公司(已被Oracle收购)实现的sun.misc.Launcher$ExtClassLoader
类,由Java语言实现的,是Launcher的静态内部类,它负责加载<JAVA_HOME>/lib/ext
目录下或者由系统变量-Djava.ext.dir指定位路径中的类库,开发者可以直接使用标准扩展类加载器。
3、系统(System)类加载器
也称应用程序加载器是指 Sun公司实现的sun.misc.Launcher$AppClassLoader
。它负责加载系统类路径java -classpath
或-D java.class.path
指定路径下的类库,也就是我们经常用到的classpath路径,开发者可以直接使用系统类加载器,一般情况下该类加载是程序中默认的类加载器,通过ClassLoader#getSystemClassLoader()
方法可以获取到该类加载器。
4、理解双亲委派模式
在Java的日常应用程序开发中,类的加载几乎是由上述3种类加载器相互配合执行的,在必要时,我们还可以自定义类加载器,需要注意的是,Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象,而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式即把请求交由父类处理,它一种任务委派模式,下面我们进一步了解它。
三、自定义类加载器
定义java类
public class ClassBean {
private int age;
private String name;
public ClassBean() {
}
public void createBean() {
this.age = 32;
this.name = "LiuPing";
System.out.println(this.toString());
}
@Override
public String toString() {
return "ClassBean{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
使用javac命令编译ClassBean.java为ClassBean.class,字节码文件的内容如下(字节码查看工具 Binary Viewer):
自定义类加载器
public class MyClassLoader extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
InputStream inputStream = null;
ByteArrayOutputStream outputStream = null;
try {
//将ClassBean.class文件读入输入流
inputStream = new FileInputStream("D:\\workplace\\java\\opensource\\YangApp\\src\\main\\java\\org\\cn\\fcw\\cls\\ClassBean.class");
outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while (((len = inputStream.read(buffer)) != -1)) {
outputStream.write(buffer, 0, len);
}
outputStream.flush();
byte[] bytes = outputStream.toByteArray();
///生成类文件
return defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(outputStream!=null){
outputStream.close();
}
}catch (IOException e){
e.printStackTrace();
}
try {
if(inputStream!=null){
inputStream.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
return null;
}
}
测试类
public static void main(String[] arr){
MyClassLoader myClassLoader=new MyClassLoader();
try {
///Class.forName("org.cn.fcw.ClassLoaderBean");
//生成ClassBean类,一定要带包名
Class<?> classLoaderBean = myClassLoader.findClass("org.cn.fcw.cls.ClassBean");
//通过反射调用默认构造器创建实例
Object o = classLoaderBean.getDeclaredConstructor().newInstance();
//获取ClassBean的createBean方法
Method method = classLoaderBean.getMethod("createBean");
//执行createBean方法
method.invoke(o);
} catch (Exception e) {
e.printStackTrace();
}
}