1.类的初始化过程
2.触发类的初始化情况
3.双亲委派机制
Java向上委派加载,只有当上层找不到对应的class文件时,才会交给下一级类加载器加载
4.类加载的时序图
5.自定义类加载器
1.验证类加载顺序
package com.roger.classloader;
import com.sun.java.accessibility.util.EventID;
public class ClassLoaderMain {
public static void main(String[] args) {
System.out.println("java.lang.String-在%JAVA_HOME%\\lib\\目录下的jar包中");
System.out.print("使用的类加载器为-");
System.out.println(String.class.getClassLoader());
System.out.println("com.sun.java.accessibility.util.EventID-在%JAVA_HOME%\\lib\\ext\\目录下的jar包中");
System.out.print("使用的类加载器为-");
System.out.println(EventID.class.getClassLoader());
System.out.println("com.roger.classloader.ClassLoaderMain-在%CLASSPATH%目录下");
System.out.print("使用的类加载器为-");
System.out.println(ClassLoaderMain.class.getClassLoader());
}
}
运行结果:
java.lang.String-在%JAVA_HOME%\lib\目录下的jar包中
使用的类加载器为-null
com.sun.java.accessibility.util.EventID-在%JAVA_HOME%\lib\ext\目录下的jar包中
使用的类加载器为-sun.misc.Launcher$ExtClassLoader@45ee12a7
com.roger.classloader.ClassLoaderMain-在%CLASSPATH%目录下
使用的类加载器为-sun.misc.Launcher$AppClassLoader@18b4aac2
2.自定义类加载器
package com.roger.classloader;
import java.io.*;
/**
* 自定义类加载器的步骤
* 1.继承java.lang.ClassLoader类
* 2.重写findClass()方法 --- 找到要加载的class文件,并转换成字节流
* 3.调用defineClass方法 --- 把findClass()方法找到的字节流转换成jvm认识的Class类型
* 4.使用自定义的类加载器的方法是:
* 调用其父类的loadClass(String name)方法
*
* 如果想破换双亲委派机制
* 只需要重写 ClassLoader的loadClass(String name) 方法
*/
public class CustomClassLoader extends ClassLoader {
private static final String EXT = ".class";
//自定义类加载器的加载路径
private String classPath;
public CustomClassLoader(String classPath, ClassLoader parent) {
super(parent);
this.classPath = classPath;
}
public CustomClassLoader(String classPath) {
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
byte[] clazzBytes = loadClassBytes(className);
if (clazzBytes == null) {
throw new ClassNotFoundException("该类不存在,类名为" + className);
}
return super.defineClass(className, clazzBytes, 0, clazzBytes.length);
}
private byte[] loadClassBytes(String className) {
String classFilePath = getClassFileAllPath(className);
ByteArrayOutputStream bout = null;
InputStream fin = null;
try {
fin = new FileInputStream(classFilePath);
bout = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len = 0;
while ((len = fin.read(bytes)) != -1) {
bout.write(bytes, 0, len);
}
} catch (FileNotFoundException e) {
throw new RuntimeException("该类文件不存在,文件路径为:" + classPath);
} catch (IOException e) {
throw new RuntimeException("该类文件内容损坏,无法读取,文件路径为:" + classPath);
} finally {
try {
if (fin != null) {
fin.close();
}
if (bout != null) {
bout.close();
}
} catch (IOException e) {
}
}
return bout.toByteArray();
}
private String getClassFileAllPath(String className) {
String filePathName = className.replace(".", File.separator);
if (classPath.endsWith("/") || classPath.endsWith("\\")) {
return classPath + filePathName + EXT;
}
return classPath + File.separator + filePathName + EXT;
}
public static void main(String[] args) {
String classPath = "F:\\test";
CustomClassLoader customClassLoader = new CustomClassLoader(classPath);
try {
Class<?> StringClass = customClassLoader.loadClass("java.lang.String");
System.out.println("调用Java核心包的类-类加载器为:" + StringClass.getClassLoader());
Class<?> eventIdClass = customClassLoader.loadClass("com.sun.java.accessibility.util.EventID");
System.out.println("调用Java扩展包的类-类加载器为:" + eventIdClass.getClassLoader());
Class<?> classLoaderMainClass = customClassLoader.loadClass("com.roger.classloader.ClassLoaderMain");
System.out.println("调用Classpath路径下的类-类加载器为:" + classLoaderMainClass.getClassLoader());
Class<?> customArithClass = customClassLoader.loadClass("com.roger.classloader.Arith");
System.out.println("调用自定义的类加载路径-类加载器为:" + customArithClass.getClassLoader());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行路劲:
调用Java核心包的类-类加载器为:null
调用Java扩展包的类-类加载器为:sun.misc.Launcher$ExtClassLoader@45ee12a7
调用Classpath路径下的类-类加载器为:sun.misc.Launcher$AppClassLoader@18b4aac2
调用自定义的类加载路径-类加载器为:com.roger.classloader.CustomClassLoader@2503dbd3
在准备class文件的时候,遇到编码GBK不可映射字符:
通过下面命令解决:
javac -encoding utf-8 CountDownLatchMain.java