一、类加载过程
将多个java文件编译打包成可运行jar包,通过java命令运行某个主类的main函数启动程序,此时类加载器会首先把该主类加载到jvm(已经使用到),jar包中其他类,在使用到时才开始执行加载流程
加载流程:
1)加载:通过io流读入字节码文件,使用到类时才会进行加载,比如main函数,new对象等
2)验证:校验字节码文件的正确性
3)准备:将静态变量分配内存,并赋默认值(如int a = 0)
4)解析:将符号引用替换成直接引用,比如将静态变量(main方法)替换成所存内存的指针或句柄,这个过程叫静态链接,在类加载时完成。另外一种为动态链接,在程序运行期间完成该过程
5)初始化:将静态变量初始化为指定的值(如int a = 6),执行静态代码块
二、类加载器与双亲委派机制
类的加载流程由类加载器完成,类加载器分为以下几种:
1)引导类加载器(bootstrap class loader),加载java 核心jar包,jre/lib/.jar等
2)扩展类加载器(ExtClassLoader),加载jre/ext/.jar
3)系统类加载器,也就是应用程序类加载器(AppClassLoader),加载ClassPath下的类包,也就是应用程序自己写的类
4)自定义加载器:加载自定义路径下的类包
类加载器示例
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(String.class.getClassLoader());
System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName());
System.out.println(TestJDKClassLoad.class.getClassLoader().getClass().getName());
System.out.println(ClassLoader.getSystemClassLoader().getClass().getName());
}
输出:
null
sun.misc.Launcher
E
x
t
C
l
a
s
s
L
o
a
d
e
r
s
u
n
.
m
i
s
c
.
L
a
u
n
c
h
e
r
ExtClassLoader sun.misc.Launcher
ExtClassLoadersun.misc.LauncherAppClassLoader
sun.misc.Launcher$AppClassLoader
自定义加载器实现:
继承ClassLoader类即可,主要有2个核心方法
1)loadClass(String,Boolean),实现了双亲委派机制,
2)findClass(String name),默认是抛出异常,自定义的话重写该方法即可
private byte[] loadByte(String name) throws IOException {
name = name.replaceAll("\\.","/");
FileInputStream fileInputStream = new FileInputStream(classPath+"/"+name+".class");
final int len = fileInputStream.available();
byte [] data = new byte[len];
fileInputStream.read(data);
fileInputStream.close();
return data;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
// 读取硬盘上的字节码文件
final byte[] data = loadByte(name);
// 根据字节码文件创建class对象
return defineClass(name,data,0,data.length);
} catch (IOException e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
双亲委派机制
大致流程:
1)判断指定的类是否已经加载过,如果已经加载 则直接返回
2)如果该类没有加载过,则判断有没有父加载器,有的话则使用父加载器进行加载,或者调用bootstrap加载器进行加载
3)当父加载器或者bootstrap加载器都找不到该类,则调用当前类的加载器findClass完成加载
双亲委派机制好处
- 安全,始终由父加载器优先加载,保证核心类库不被篡改,且不能以java开头命名类包
- 唯一性,保证jvm中只存在一个相同类