类加载全过程
1.编译: 源码经过编译器编译,形成字节码文件(.class)
2. 加载:类加载器(ClassLoader)加载字节码到内存中
3. 链接:将java类的二进制代码合并到JVM运行的状态之中
验证:确保加载的类信息符合JVM规范,没有安全方面的问题
准备:正式为类变量(static变量)分配内存并设置默认值,这些内存都在方法区(堆)中分配
解析:虚拟机常量池中的符号引用替换为直接引用的过程
4. 初始化:初始化阶段执行类构造器(Clinit)方法,类构造器方法是编译器自动收集所有类变量的赋值动作和静态语句块(static块)中的语句合并产生的。当初始化一个类的时候,如果发现其父类还没有被初始化,则先初始化其父类
/**
* 测试类变量和静态块加载
* @author L J
*/
public class StaticDemo {
public static void main(String[] args) throws Exception {
//类的主动引用(一定会初始化类)
//1、用new关键字创建对象
//new A();
//2、调用类的静态成员
//System.out.println(A.width);
//3、对类进行反射调用
//Class.forName("com.belief.jvm.A");
//类的被动引用(不会初始化类)
//1、调用类的常量
//System.out.println(A.MAX);
//2、通过数组定义类引用,不会触发此类的初始化
//A[] a = new A[1];
//3、访问一个静态域时,只有真正声明这个域的类才会被初始化
System.out.println(B.width); //类A被初始化
}
static {
System.out.println("静态初始化StaticDemo");
}
}
class B extends A {
static {
System.out.println("静态初始化B");
}
}
class A extends A_Father {
public static int width = 100;
public static final int MAX = 200;
static {
System.out.println("静态初始化类A");
width = 300;
}
public A() {
System.out.println("创建A类的对象");
}
}
class A_Father {
static {
System.out.println("静态初始化A_Father");
}
}
类加载器
类加载器的层次结构:
/**
* 了解类加载器的层次结构
* @author L J
*/
public class ClassLoaderDemo {
public static void main(String[] args) {
//获取AppClassLoader
System.out.println(ClassLoader.getSystemClassLoader());
//获取ExtClassLoader
System.out.println(ClassLoader.getSystemClassLoader().getParent());
//java获取不了bootstrap class loader,返回null
System.out.println(ClassLoader.getSystemClassLoader().getParent().getParent());
System.out.println(System.getProperty("java.class.path"));
}
}
双亲委托机制:
- 类加载采用双亲委托机制(只要有父加载器,就交给父加载器先加载,父加载器不能加载,才自己加载)
- 为了保证java核心库的类型安全,这种机制保证不会出现用户自定义java.lang.Object类的情况
自定义类加载器
/**
* 文件系统类加载器
* @author L J
*/
public class FileSystemClassLoader extends ClassLoader{
//要加载类的根目录
private String rootDir;
public FileSystemClassLoader(String rootDir) {
this.rootDir = rootDir;
}
/**
* 重写父类的findClass方法
*/
@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;
}
/**
* 获取要加载类的字节数组
* @param className 类名
* @return 要加载类的字节数组
*/
private byte[] getClassData(String className) {
//拼接路径
String path = rootDir + "/" + className.replace(".", "/") + ".class";
//输入流
InputStream is = null;
//输出流
ByteArrayOutputStream baos = null;
try {
is = new FileInputStream(path);
baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = 0;
//文件读取
while((len = is.read(buf)) != -1) {
baos.write(buf, 0, len);
}
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
return null;
}finally{
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
测试类:
/**
* 测试自定义的FileSystemClassLoader类
* @author L J
*/
public class FileSystemClassLoaderTest {
public static void main(String[] args) throws Exception {
FileSystemClassLoader loader = new FileSystemClassLoader("F:/DB");
FileSystemClassLoader loader2 = new FileSystemClassLoader("F:/DB");
Class<?> c = loader.findClass("Hello");
Class<?> c1 = loader.findClass("Hello");
Class<?> c2 = loader2.findClass("Hello");
Class<?> c3 = loader.findClass("java.lang.String");
//同一个类,被不同的类加载器加载,JVM认为是两个不同的类
System.out.println(c == c1); //true
System.out.println(c == c2); //false
//FileSystemClassLoader加载
System.out.println(c.getClassLoader());
//由bootstrap class loader加载,返回null
System.out.println(c3.getClassLoader());
}
}
/**
* 网络类加载器
* @author L J
*/
public class NetClassLoader extends ClassLoader{
//要加载类的根目录
private String rootURL;
public NetClassLoader(String rootURL) {
this.rootURL = rootURL;
}
/**
* 重写父类的findClass方法
*/
@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;
}
/**
* 获取要加载类的字节数组
* @param className 类名
* @return 要加载类的字节数组
*/
private byte[] getClassData(String className) {
//拼接路径
String path = rootURL + "/" + className.replace(".", "/") + ".class";
//输入流
InputStream is = null;
//输出流
ByteArrayOutputStream baos = null;
try {
URL url = new URL(path);
is = url.openStream();
baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = 0;
//文件读取
while((len = is.read(buf)) != -1) {
baos.write(buf, 0, len);
}
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
return null;
}finally{
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
解密类加载器:
/**
* 加密(取反)工具类
* @author L J
*/
@SuppressWarnings("all")
public class EncryptUtil {
/**
* 加密文件
* @param src //源文件
* @param dest //目标文件
*/
public static void encrypt(File src, File dest) {
//文件输入流
FileInputStream fis = null;
//文件输出流
FileOutputStream fos = null;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(dest);
int temp = -1;
while((temp = fis.read()) != -1) {
fos.write(temp^0xff); //取反操作
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
//关闭输出流
if(fos != null) {
fos.close();
}
//关闭输入流
if(fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 解密类加载器
* @author L J
*/
public class DecryptClassLoader extends ClassLoader {
//要加载类的根目录
private String rootDir;
public DecryptClassLoader(String rootDir) {
this.rootDir = rootDir;
}
/**
* 重写父类的findClass方法
*/
@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;
}
/**
* 获取要加载类的字节数组
* @param className 类名
* @return 要加载类的字节数组
*/
private byte[] getClassData(String className) {
//拼接路径
String path = rootDir + "/" + className.replace(".", "/") + ".class";
//输入流
InputStream is = null;
//输出流
ByteArrayOutputStream baos = null;
try {
is = new FileInputStream(path);
baos = new ByteArrayOutputStream();
int temp = -1;
while((temp = is.read()) != -1) {
baos.write(temp^0xff); //取反操作
}
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
return null;
}finally{
try {
if(is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 测试加密解密工具类
* @author L J
*/
public class EncryptDecryptTest {
public static void main(String[] args) throws Exception {
//加密
EncryptUtil.encrypt(new File("F:/DB/Hello.class"), new File("F:/DB/temp/Hello.class"));
//解密并加载类
DecryptClassLoader loader = new DecryptClassLoader("F:/DB/temp");
Class<?> c = loader.loadClass("Hello");
System.out.println(c);
}
}