在Java虚拟机中,类在JVM中的生命周期为:加载、验证、准备、解析、初始化、使用和卸载几个阶段。一般类的主动加载会有以下情况:遇到new、getstatic、putstatic、或invokestatic这4条字节码(一般为new对象时,读取设置类静态字段时,调用类的静态方法时);使用java.lang,reflect包的方法进行反射时;初始化某个类,其父类会首先被初始化;拥有main方法所在的类会被首先初始化。
Java中的类加载动作是通过“类加载器”在JVM外部实现的,因此应用程序可以自由决定如何获取所需要的类。类加载器是通过类的全限定名获取此类的二进制字节流,然后分析二进制流中的字节码,最后将类载入虚拟机中。
Java中的类加载器一般是通过ClassLoader类来实现的,同时一个类和它的类加载器共同确定了这个类的唯一性。即是说,每个类加载器都拥有独立的名称空间,要确定两个类相等,需要保证相同的类加载器和相同的类。
import java.io.InputStream;
public class ClassLoaderTest {
/**
* 类和它的类加载器共同确定这个类的唯一性
* */
public static void main(String[] args) throws Exception {
ClassLoader loader = new ClassLoader() {
public Class<?> loadClass(String name)
throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf('.') + 1) + ".class";
InputStream is = getClass().getResourceAsStream(fileName);
if (is == null) {
return super.loadClass(name);
}
byte[] b = new byte[is.available()];
is.read(b);
return defineClass(name, b, 0, b.length);
} catch (Exception e) {
throw new ClassNotFoundException(name);
}
}
};
String className = ClassLoaderTest.class.getCanonicalName();
Object obj = loader.loadClass(className).newInstance();
System.out.println(obj.getClass());// 从类名上看obj是ClassLoaderTest类型
System.out.println(ClassLoaderTest.class);
System.out.println(obj instanceof ClassLoaderTest);// 会返回false
}
}
双亲委派模型
从Java虚拟机的角度来讲,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),它使用C++实现,是虚拟机自身的一部分;另一种就是所有的其他的类加载器,这些类加载器都由Java语言实现,独立于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader。
启动类加载器(Bootstrap ClassLoader):负责将存放在<JAVA_HOME>\lib下的或者-Xbootclasspath参数所指定的路径中,并且是虚拟机可识别的(按照文件名识别,否则不会被加载,如rt.jar)类库加载到虚拟机内存中。如果需要把加载请求委派给启动类加载器,可以使用null来替代。
扩展类加载器(Extension ClassLoader):由sun.misc.Launcher$ExtClassLoader实现,负责将<JAVA_HOME>\lib\ext目录中的或者java.ext.dirs系统变量指定的路径中的库加载到系统中。
应用类加载器(Application ClassLoader):由sun. misc.Launcher$AppClassLoader实现,负责加载用户路径(ClassPath)上所指定的类库。它是ClassLoader中getSystemClassLoader()的返回值,如果应用中没有自定义过自己的类加载器,一般它就是程序默认的类加载器。
自定义类加载器(User ClassLoader):就是用户通过继承自java.lang.ClassLoader实现的类加载器,一般要求必须实现findClass()或者loadClass()方法从而实现类加载。
类加载器的双亲委派模型要求,除了启动类加载器之外,所有的其他类加载器都应该有自己的父类加载器(通过组合实现)。双亲委派模型的工作过程是:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每个层次的类加载器都是如此,因此所有的加载请求最终都应该传送给顶层启动类加载器中,只有当父类加载器反馈自己无法完成整个请求时,子类加载器才会尝试自己加载。
双亲委派模型使得类加载器具有一种优先级的层次关系,即父类加载器的加载过程优先于其下层的类加载器,这样可以保证系统的安全性和稳定性。如java.lang.Object类无论如何设定加载过程,都会被委派给最顶端的启动类加载器,这样就保证了整个系统中只有唯一一个Object类。