类加载器
类加载器作用:将class文件字节码内容加载到内存中,并将这些静态数据转换为方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口
一个类的生命周期:加载,连接,初始化。连接中分验证,准备,解析
类加载到内存中需要使用类加载器
根类加载器 Bootstrap
扩展类加载器Extension 继承根类加载器
系统类加载System(应用类加载器) 继承扩展类加载器
自定义类加载器ClassLoader 继承系统类加载器
双亲委派机制:
如果需要classLoader加载一个类时,该classLoader先委托自己的父加载器先去加载这个类,若父加载器能够加载,则由父加载器加载,否则才又classLoader自己加载这个类。真正加载类的加载器我们叫作启动类加载器。双亲委派机制的父子关系并非面向对象程序设计中的继承关系,而是通过组合模式来复用父类加载器代码
双亲委派机制的好处:
可以避免类的重读加载,当父类加载器已经加载了该类,就没有必要子ClassLoader再加载一次。
考虑到安全因素,Java核心API中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Object的类,通过双亲委派模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递过来java.lang.Object,而直接返回以加载过的Object.class,这样可以防止核心API库被随意篡改
所有的加载器 除了根类加载器都必须继承java.lang.ClassLoader。它是一个抽象类,
主要方法loadClass,findClass自定义类加载器时,一般覆盖这个方法,defineClass,resolveClass
URLClassLoader
自定义类加载器
- 继承ClassLoader
- 覆盖findClass方法
自定义文件类加载器
public class MyFileClassLoader extends ClassLoader {
private String directory;//被加载类所在的目录
public MyFileClassLoader(String directory) {
this.directory = directory;
}
public MyFileClassLoader(ClassLoader parent, String directory) {
super(parent);
this.directory = directory;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException{
//把类名转化为目录
//D:/com/
String file = directory+ File.separator+name.replace(".",File.separator)+".class";
//构建输入流
InputStream in = null;
try {
in = new FileInputStream(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//构建字节输出流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len=0;
while(true){
try {
if (!((len=in.read(buf))!=-1)) break;
} catch (IOException e) {
e.printStackTrace();
}
//得到字节码的二进制数据
byteArrayOutputStream.write(buf, 0, len);
}
byte data[] = byteArrayOutputStream.toByteArray();
try {
in.close();
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return defineClass(name, data, 0, data.length);
}
public static void main(String[] args) throws Exception{
MyFileClassLoader myFileClassLoader = new MyFileClassLoader("F:\\JavaPractice\\Day1129");
Class clazz = myFileClassLoader.loadClass("com.etime.Test01");
clazz.newInstance();
}
}
自定义网络类加载器
public class MyURLClassLoader extends ClassLoader{
private String url;
public MyURLClassLoader(String url) {//父类加载器还是系统类加载器
this.url = url;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String path = url+"/"+name.replace(".","/")+".class";
URL url = null;
try {
url = new URL(path);
} catch (MalformedURLException e) {
e.printStackTrace();
}
InputStream in = null;
try {
in = url.openStream();
} catch (IOException e) {
e.printStackTrace();
}
//构建字节输出流
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len=0;
while(true){
try {
if (!((len=in.read(buf))!=-1)) break;
} catch (IOException e) {
e.printStackTrace();
}
//得到字节码的二进制数据
byteArrayOutputStream.write(buf, 0, len);
}
byte data[] = byteArrayOutputStream.toByteArray();
try {
in.close();
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return defineClass(name, data, 0, data.length);
}
public static void main(String[] args) {
MyURLClassLoader myURLClassLoader = new MyURLClassLoader("http://localhost:8080/examples");
Class clazz = null;
try {
clazz = myURLClassLoader.loadClass("com.etime.Test01");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
热部署类加载器
让同一个class文件被不同的类加载器重复加载即可。但是不能调用loadClass方法,而应该调用findClass方法,避开双亲委派机制,从而实现同一个类被多次加载,实现热部署
我李炳橙今天要吃屎
本文详细解读Java类加载器的工作原理,包括Bootstrap、Extension和System加载器,双亲委派机制的应用,以及自定义File和URL类加载器实例。重点讲解如何实现热部署并通过自定义类加载器进行文件和网络类的加载。

453

被折叠的 条评论
为什么被折叠?



