一:类加载
类加载通常需要三个步骤:将class文件加载到内存中,并建立class对象是类加载的最终状态。
1.加载:由类加载器进行加载,jvm提供类加载器,也可以通过继承classLoader实现自定义的类加载器。
2.连接:生成对应的class对象后就进入连接状态把类的二进制数据加载到jre中。
(1)验证:当前字节码文件是否符合虚拟机要求(魔数(用来标记字节码文件的类型),版本号…);
(2)准备:给类变量在方法区中开辟内存,赋类型的初始值;
(3)解析:解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
3.初始化:正常的类初始化,执行顺序。
二:类加载机制
Java 虚拟机一般使用 Java 类的流程:首先将开发者编写的 Java 源代码(.java 文件)编译生成 Java 字节码文件 (.class 文件),然后类加载器会读取字节码文件,并转换成 java.lang.Class 对象。有了该 Class 对象后,Java 虚拟机 可以利用反射方法创建其真正的对象了。 Java 提供的类加载器绝大多数都继承自 ClassLoader 类,它们被用来加载不 同来源的字节码文件。
三:类加载器
class对象的唯一标识:(类名,包名,加载器名)
JDK 提供的三种类加载器,分别是 BootstrapClassLoader(启动类加载器),ExtClassLoader(扩展类加 载器),AppClassLoader(应用类加载器)。每一种类加载器都有其指定的类加载路径。
(1) BootstrapClassLoader(启动类加载器又称为根加载器/引导类加载器)主要加载 JAVA_HOME/jre/lib 里的 jar 包,该目录下的所有 jar 包都是运行 JVM 时所必需的 jar 包。注意:类加载器其实自身也是一个 Java 类,因此, 自身类加载器需要被其他类加载器进行加载后方可使用,显然必须有一个类加载器的顶级父类(也就是 Bootstrap ClassLoader,该类加载器是由 C 语言代码进行开发的)是其他类加载器的父类。关键点在于,如果一个类的类加载器 是 BootstrapClassLoader,那么该类的 getClassLoader()方法返回 null。
(2) ExtClassLoader 主要加载 Java 核心扩展类,即 JAVA_HOME/jre/ext 目录下的 jar 文件。
(3) AppClassLoader 主要加载的是开发者在应用程序中编写的类,即 CLASSPATH 路径下所有的 jar 文件。
这三种类加载器之间的关系:
双亲委派模型:
双亲委派模型工作过程:
(1)当前类加载器从自己已经加载的类中查询是否此类已经加载,如果已经加载则返回原来已经加载的类。 (2)如果没有找到,就去委托父类加载器去加载。父类加载器也会采用同样的策略,查看自己已经加载过的 类中是否包含这个类,有就返回,没有就委托其父类去加载,直到委托到启动类加载器为止。因为如果父类加载器 为空了,就代表使用启动类加载器作为父加载器去加载该类。(也就是看到的 String 类加载器为 null)
(3)如果启动类加载器加载失败,就会使用扩展类加载器来尝试加载,继续失败则会使用 AppClassLoader 来 加载,继续失败就会抛出一个异常 ClassNotFoundException。
双亲委派模型的优点:
(1)安全性,避免用户自己编写的类动态替换 Java 的一些核心类。
(2)避免类的重复加载。
四:自定义类加载器
如果用户想加载一个特定目录下的类时,就要用到用户自定义的类加载器。除了启动类加载器之外,所有的类 加载器都是 ClassLoader 的子类。
自定义类加载器MyClassLoader要继承自ClassLoader类,有三个方法较为关键:loadClass 方法、findClass 方法和 defineClass 方法。 。
(1)loadClass:负责以双亲委托方式去加载类 ;
(2)loadClass:负责以双亲委托方式去加载类 ;
(3)defineClass:负责从 class 字节码中加载 Class 对象,然后 Class 对象通过反射机制生成对象。
例:
**
* 一、ClassLoader加载类的顺序
* 1.调用 findLoadedClass(String) 来检查是否已经加载类。
* 2.在父类加载器上调用 loadClass 方法。如果父类加载器为 null,则使用虚拟机的内置类加载器。
* 3.调用 findClass(String) 方法查找类。
* 二、实现自己的类加载器
* 1.获取类的class文件的字节数组
* 2.将字节数组转换为Class类的实例
* @author lei 2011-9-1
*/
public class ClassLoaderTest {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
//新建一个类加载器
MyClassLoader cl = new MyClassLoader("myClassLoader");
//加载类,得到Class对象
Class<?> clazz = cl.loadClass("classloader.Animal");
//得到类的实例
Animal animal=(Animal) clazz.newInstance();
animal.say();
}
}
class Animal{
public void say(){
System.out.println("hello world!");
}
}
class MyClassLoader extends ClassLoader {
//类加载器的名称
private String name;
//类存放的路径
private String path = "E:\\workspace\\Algorithm\\src";
MyClassLoader(String name) {
this.name = name;
}
MyClassLoader(ClassLoader parent, String name) {
super(parent);
this.name = name;
}
/**
* 重写findClass方法
*/
@Override
public Class<?> findClass(String name) {
byte[] data = loadClassData(name);
return this.defineClass(name, data, 0, data.length);
}
public byte[] loadClassData(String name) {
try {
name = name.replace(".", "//");
FileInputStream is = new FileInputStream(new File(path + name + ".class"));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int b = 0;
while ((b = is.read()) != -1) {
baos.write(b);
}
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
自定义类加载器代码实现:
class MyClassLoader extends ClassLoader{
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
String Dir="D:";//用户自定义路径进行拼接
String classPath=Dir+File.separatorChar+name.replace('.', File.separatorChar)+".class";
File file = new File(classPath);
byte[] buff = null;
InputStream in;
try {
in = new FileInputStream(file);
buff = new byte[in.available()];
in.read(buff);
in.close();
Class<?> c = defineClass(name, buff, 0, buff.length);
return c;
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return super.findClass(classPath);
}
}