java main() 启动流程
1.在启动类的class 利用java的环境启动jvm,创建javaJVM虚拟机
2.创建jvn根类加载器底层由C++实现
3.由引导类加载器创建其他的类加载器(扩展类加载器,应用加载器,自定义加载器)
4.利用类加载器实现的loadClass 加载当前calss,执行main方法
5.执行完毕,销毁
类加载的过程(类加载器属于lazy,如果只是定义,并没有使用就不会加载 如 Demo demo = null)
-
加载
将磁盘或者网络加载到jvm内存中, -
验证
验证class字节码头信息,二进制是否符合java的字节码规范 -
准备
将静态变量赋值=(默认值) -
解析
将符合引用替换为直接引用。就是把代码转换为静态的常量自变量符合,静态链接过程。动态链接只有在运行是才会把代码符合找到对应的代码,如接口,多态等在执行时,才知道,这个过程就是动态链接 -
初始化
给静态变量赋真实的值 ,常量直接进行赋值 -
使用
-
卸载
类加载器分类
-
引导类加载器 加载自身自带的核心类,例如String.class
-
扩展类加载器 加载扩展库(ext包下的jar包) 如 DESKeyFactory.calss
-
应用类加载器 加载应用程序代码 如自己定义的 Demo.class
-
自定义加载器
双亲委派机制
是指加载某一个类时,委托自己父类加载器进行加载,若有则返回,没有继续向上委托,如果到引用加载器都没有找到,在由自己的加载器进行类的加载。
为什么要使用双亲委派机制
- 为了安全,防止本身项目使用jdk一样的包名和类名将jdk代码覆盖。
- 避免重复加载。父类加载了,子类不需要加载,确保类的唯一性
自定义类加载器示
-
基础ClassLoad
-
重写findClass方法。
创建测试类
package com.moon;
import lombok.NoArgsConstructor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @ClassName MyTestClassLoad
* @Author moon
* @Date 2022/8/8
* @Description 自义定classLoad
* @Version 1.0
*/
@NoArgsConstructor
public class MyTestClassLoad extends ClassLoader {
private String path;
public MyTestClassLoad(String path) {
this.path = path;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classBytes = loadName(name);
return defineClass(name, classBytes, 0, classBytes.length);
}
private byte[] loadName(String name) {
name = name.replaceAll("\\.", "/");
try (FileInputStream fileInputStream = new FileInputStream(path + "/" + name + ".class");) {
int available = fileInputStream.available();
byte[] bytes = new byte[available];
fileInputStream.read(bytes);
return bytes;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (!name.startsWith("user")) {
c = this.getParent().loadClass(name);
} else {
c = findClass(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
public static void main(String[] args) throws Exception {
MyTestClassLoad classLoad = new MyTestClassLoad("E:\\user1");
Class<?> mindao = classLoad.loadClass("com.moon.entity.User");
Object o = mindao.newInstance();
Method setControlId = mindao.getDeclaredMethod("setAge", int.class);
setControlId.invoke(o, 10);
System.out.println(mindao.getDeclaredMethod("getAge").invoke(o));
System.out.println(mindao.getClassLoader());
System.out.println("--------------------------");
System.out.println(mindao.getClassLoader());
classLoad = new MyTestClassLoad("E:\\user2");
mindao = classLoad.loadClass("com.moon.entity.User");
o = mindao.newInstance();
setControlId = mindao.getDeclaredMethod("setAge", int.class);
setControlId.invoke(o, 10);
System.out.println(mindao.getDeclaredMethod("getAge").invoke(o));
}
}
运行main():
1.当前项目中没有User.class
10
com.moon.MyTestClassLoad@74a14482
--------------------------
com.moon.MyTestClassLoad@74a14482
20
Process finished with exit code 0
2.该项目中存在User.class 会使用自定义加载器父类进行委托加载
20
sun.misc.Launcher$AppClassLoader@18b4aac2
--------------------------
sun.misc.Launcher$AppClassLoader@18b4aac2
20
如何破坏双亲委派:
在 MyTestClassLoad 中 重写 loadClass();不交给父类进行委派加载即可:
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
// try {
// if (parent != null) {
// c = parent.loadClass(name, false);
// } else {
// c = findBootstrapClassOrNull(name);
// }
// } catch (ClassNotFoundException e) {
// // ClassNotFoundException thrown if class not found
// // from the non-null parent class loader
// }
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
2个同包同类名的class从理论上是可以在一个项目中存在的,判断唯一除了判断同包,同路径还有看是否是同一个类加载器。例如新建一个项目和现在项目中创建一个同包同类名的类,,一个用自定义class进行加载,一个用默认的class进行加载。或者用自己写的打破双亲委派机制 就能加载 不同路径下的通包同类名的class ,如下图实例创建2个UserClass ,一个age设置就等于本身,user2目录下getAge 方法比为 age+10进行加载: