一、类加载全过程
JVM把class文件加载到内存,并对数据进行校验、解析和初始化,最终形成JVM可以直接使用的java类型的过程
1、加载:JVM把class文件加载到内存,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口。这个过程需要类加载器参与
2、链接:(包括 :验证、准备、解析)将java类的二进制代码合并到JVM的运行状态之中的过程
验证:确保加载的类信息符合JVM的规范,没有安全方面的问题
准备:正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配
解析:JVM常量池内的符号引用替换为直接引用的过程
3、初始化:初始化阶段是执行类构造器<clinit>()方法的过程,类构造器方法是由编译器自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的;当初始化一个类的时候,发现其父类没有进行初始化,则需要先初始化其父类;JVM会保证类构造器方法在多线程中被正确加锁和同步;
二、类的初始化——静态成员和静态块的初始化
(1)、类的主动引用(一定会发生类的初始化——静态成员和静态块)
1、new 一个新对象
2、调用类的静态成员(final常量除外)和静态方法
3、调用java.lang.Reflect包对用进行反射调用
4、当虚拟机启动,调用命令 java HelloWorld ,则一定会初始化HelloWorld类
5、当初始化一个类时,如果父类没有被初始化,会先初始化其父类
(2)、类的被动引用(不会发生类的初始化——静态成员和静态块)
1、当访问一个静态变量时,只有真正声明这个静态变量的类才会被初始化,通过子类调用父类的静态变量,子类不会被初始化
2、通过数组定义类的引用,不会触发类的初始化
3、调用常量不会触发类的初始化,常量在编译阶段就存入了调用类的
三、类加载器
1、引导类加载器(bootstrap class loader)
用来加载java的核心库(JAVA_HOME/jre/lib/rt.jar 或 sun.boot.class.path路径下的内容)
System.getProperty("sun.boot.class.path")
是用c实现的,并不继承java.lang.ClassLoader
2、扩展类加载器 (extensions class loader)
用来加载java的扩展库(JAVA_HOME/jre/lib/ext/*.jar 或 java.ext.dirs路径下的内容)
System.getProperty("java.ext.dirs")
JVM的实现会提供一个扩展库目录,该类加载器在此目录查找并加载java类
由sun.misc.Launcher$ExtClassLoader实现
3、应用程序类加载器(application class loader)
根据java的应用路径(classpath 或 java.class.path路径)加载类,一般来说,java应用的类,都是由它完成加载的
System.getProperty("java.class.path")
由sun.misc.Launcher$AppClassLoader实现
4、自定义类加载器
开发人员可以通过继承 java.lang.ClassLoader 来自定义一些类加载器,以满足一些特殊的功能
四、代理模式
交给其他类加载器来加载指定的类
1、双亲委托机制:jvm默认的类加载机制,代理模式的一种
就是某个特定的类加载器在接收到加载类的请求时,首先将加载任务委托给父类加载器进行,依次追溯,直到最高级的父类,如果父类加载器可以完成类加载任务,则成功返回;只有父类无法完成加载任务时,才自己去加载。
双亲委托机制是为了保证java核心库的类型安全,这种机制就保证了不会出现用户自己定义的java.lang.String类的情况,就是程序员能定义这样的类,但类加载机制不会加载这个类
2、tomcat服务器类加载器模式也是使用代理模式,但不是双亲委托机制,它是某个类加载器在接收到加载类请求时,先自己去加载,如果加载失败,在委托给父类加载器处理,依次追溯,刚好与双亲委托机制相反
五、自定义类加载器
不同的类加载器加载同一个类,JVM不认为是相同的类
package customize.classLoader.demo;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
/*
* class文件位置:D:/code
* com.demo.HelloWorld.class
*/
public class CustomizeClassLoader extends ClassLoader {
private String directory;// D:/code
private static CustomizeClassLoader customizeClassLoader = null;
private CustomizeClassLoader(String directory) {
this.directory = directory;
}
public synchronized static CustomizeClassLoader getInstance(String directory) {
if (customizeClassLoader == null) {
customizeClassLoader = new CustomizeClassLoader(directory);
}
return customizeClassLoader;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// com.demo.HelloWorld
Class<?> clz = findLoadedClass(name);
if (clz == null) {
// 调用父类的类加载器
try {
ClassLoader classLoader = this.getParent();
clz = classLoader.loadClass(name);
} catch (Exception e) {
e.printStackTrace();
}
if (clz == null) {
byte[] bytes = getClassData(name);
clz = defineClass(name, bytes, 0, bytes.length);
}
}
return clz;
}
/*
* 自己实现类加载器的实现
*/
private byte[] getClassData(String name) {
directory = directory.endsWith("/") ? directory : directory + "/";
String path = directory + name.replace(".", "/") + ".class";
try {
InputStream inputStream = new FileInputStream(path);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int count = -1;
while ((count = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, count);
}
inputStream.close();
outputStream.close();
return outputStream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) throws Exception {
CustomizeClassLoader loader1 = CustomizeClassLoader.getInstance("d:/code");
CustomizeClassLoader loader2 = CustomizeClassLoader.getInstance("d:/code");
Class<?> clz1 = loader1.findClass("com.demo.HelloWorld");
Class<?> clz2 = loader2.findClass("com.demo.HelloWorld");
System.out.println(clz1.hashCode());
System.out.println(clz2.hashCode());
System.out.println(clz1.getClassLoader());
System.out.println(clz2.getClassLoader());
System.out.println(CustomizeClassLoader.class.getClassLoader());
}
}