三个步骤:
1.加载--将字节码加载到内存中,并将这些静态数据转换成方法区运行时的数据结构
在堆中生成一个代表这个类的java.Class对象,作为方法区类数据的访问入口
在堆中生成一个代表这个类的java.Class对象,作为方法区类数据的访问入口
2.链接--验证--加载的类是否符合JVM规范
--准备--正式为类变量分配内存并设置类变量初始值阶段,这些内存都将在方法区中进
行分配
--解析--虚拟机常量池中的符号引用替换为直接引用的过程
--准备--正式为类变量分配内存并设置类变量初始值阶段,这些内存都将在方法区中进
行分配
--解析--虚拟机常量池中的符号引用替换为直接引用的过程
3.初始化--执行类构造器<clinit>()方法的过程,类构造器<clinit>()方法
是由编译器自动收集列中的所有类变量的赋值动作和静态语句块中的语句合并而成
当初始化一个类时,如果其父类还没有进行初始化,则需要先初始化其父类
虚拟机会保证一个类<clinit>()方法在多线程环境中被正确加锁和同步
当访问一个java静态域时,只有真正声明这个域的类才会被初始化
双亲委派机制:
即优先父类加载,如果父类不能加载,再由子类加载,若自定义类加载也无法加载就会报错。
引导类加载(bootstrap):加载java核心库,并不继承自java.lang,ClassLoader,由c实现
扩展类加载(extensions):加载java扩展库,
引用程序类加载(application)
引用程序类加载(application)
自定义类加载
实例(自定义文件系统类加载器):
package ClassloadingDemo;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
* 自定义文件系统类加载器
* --继承ClassLoader类
* @author LSD
*
* 2018年4月14日
*/
public class FileSystemClassLoaderDemo extends ClassLoader {
private String rootDir;
public FileSystemClassLoaderDemo () {
}
public FileSystemClassLoaderDemo (String rootDir) {
this.rootDir = rootDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
//应该先查询有没有加载过这个类,如果已经加载,则直接返回已经加载的类,如果没有就加载
if(c != null) {
return c;
}else {
//让父类加载
ClassLoader parent = this.getParent();
try {
c = parent.loadClass(name);//委托给父类加载
}catch(Exception e){
//e.printStackTrace();
}
if(c != null) {
return c;
}else {
byte [] classData = getClassData(name);
if(classData == null) {
throw new ClassNotFoundException();
}else {
c = defineClass(name,classData,0,classData.length);
}
}
}
return c;
}
private byte [] getClassData(String className) {
//比如传的是FileSystemClassLoaderDemo这个类,那会在f:/myjava(某个盘某个文件夹)/ClassloadingDemoFile/SystemClassLoaderDemo找这个类
String path = rootDir + "/" + className.replace(".", "/") + ".class";
//IOUtils,可以使用它将流中的数据转换成字节数组
InputStream is =null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
is = new FileInputStream(path);
byte [] buffer = new byte [1024];
int temp = 0;
while((temp = is.read(buffer)) != -1) {
baos.write(buffer, 0, temp);
}
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
return null;
}finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package ClassloadingDemo;
/**
* 测试自定义的FileSystemClassLoaderDemo
*
*
* @author LSD
*
* 2018年4月14日
*/
public class MainTest {
public static void main(String[] args) throws ClassNotFoundException {
FileSystemClassLoaderDemo loader1 = new FileSystemClassLoaderDemo("f:/myjava");
FileSystemClassLoaderDemo loader2 = new FileSystemClassLoaderDemo("f:/myjava");
//f盘myjava文件夹中的hello类
Class<?> c1 = loader1.loadClass("hello");
Class<?> c2 = loader1.loadClass("hello");
Class<?> c3 = loader2.loadClass("hello");
//同一个类被不同的类加载器加载,JVM也认为不是相同的类
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
Class<?> c4 = loader2.loadClass("java.lang.String");
Class<?> c5 = loader2.loadClass("hello");
Class<?> c6 = loader2.loadClass("ClassloadingDemo.MainTest");
System.out.println(c4.getClassLoader());//引导类加载器
System.out.println(c5.getClassLoader());//自定义加载器
System.out.println(c6.getClassLoader());//系统自动的类加载器
}
}