感谢某视频,这里就将自己学习后做的笔记截图上来了,因为用md写的好看一点,最后附上了文档内容。
接上
接上
接上
接上
接上
接上
接上
- 加载
- 将class文件读取到JVM内存中,采用的懒加载模式,只有使用到类时才加载,比如调用main方法,new对象时,加载阶段会在内存中生成Class对象,作为方法区这个类的各种数据访问入口。
- 验证
- 验证字节码文件的正确性。
- 准备
- 给类的静态变量分配内存,并赋予初始值。
- 解析
- 符号引用替换为直接引用,这就是静态链接过程,如果时运行期间将符号引用转化为直接引用,则是动态链接。
- 初始化
- 对类的静态变量初始化为指定的值,并执行静态代码块,这就是为什么静态代码块比构造方法先执行的原因。
- 引导类加载器
- 在JVM启动时自动加载的,是用C++写的程序,加载JRE-LIB文件夹下面的一些支撑Java程序运行的jar包,比如rt.jar;于此同时引导类加载器会自动创建launcher类,用于加载扩展类加载器和应用程序类加载器,且分清楚两者的上下级关系,上级是扩展类加载器,下级是应用程序类加载器。
- 扩展类加载器
- 加载JRE-LIB-EXT文件夹下面的一些扩展jar包,如sunjce_provider.jar。
- 应用程序类加载器
- 加载在类路径下的自己写的类
- 自定义类加载器
当加载某个类时,先交给应用程序类加载器,看是否已经加载过,如果没有加载过,交给扩展类加载器,看是否已经加载过,如果没有加载过,交给引导类加载器,看是否已经加载过,如果没有加载过,就尝试加载,如果加载失败,交给扩展类加载器,尝试加载,如果加载失败,交给应用程序类加载器,尝试加载,如果失败就失败了,如果成功就成功了。
//ClassLoader
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 {
//没有加载过,交给上级类加载器去加载
//parent表明某个加载器有个属性是parent,不要理解为这几个类加载器是继承关系
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//没有上级类加载器,扩展类加载器的parent就为null了,就直接交给引导类加载器处理,本地方法,无法查看
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();
//自己尝试加载,调用UrlClassLoader类中的findClass方法
//这是个空实现,扩展类加载器时,因为继承的是UrlClassloader的这个方法
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;
}
}
//应该被实现类重写这个方法,然后执行,这里也就是要执行UrlClassLoader的findClass,自定义类加载器的化就是重写这个方法即可!
//为什么是执行rlClassLoade的findClass呢?因为扩展类加载器没有findClass方法,而扩展类加载器是继承UrlClassLoader的,因此就是执行UrlClassLoader的findClass方法了
/**
* Finds the class with the specified binary name.
* This method should be overridden by class loader implementations that
* follow the delegation model for loading classes, and will be invoked by
* the {@link #loadClass loadClass} method after checking the
* parent class loader for the requested class. The default implementation
* throws a ClassNotFoundException.
*
* @param name
* The binary name of the class
*
* @return The resulting Class object
*
* @throws ClassNotFoundException
* If the class could not be found
*
* @since 1.2
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
//URLClassLoader类中
//就是找那个类,然后加载进JVM内存中
//就是在执行加载、验证、准备、解析、初始化的过程
protected Class<?> findClass(final String name)
throws ClassNotFoundException
{
final Class<?> result;
try {
result = AccessController.doPrivileged(
new PrivilegedExceptionAction<Class<?>>() {
public Class<?> run() throws ClassNotFoundException {
String path = name.replace('.', '/').concat(".class");
Resource res = ucp.getResource(path, false);
if (res != null) {
try {
//加载进JVM内存中
return defineClass(name, res);
} catch (IOException e) {
throw new ClassNotFoundException(name, e);
}
} else {
return null;
}
}
}, acc);
} catch (java.security.PrivilegedActionException pae) {
throw (ClassNotFoundException) pae.getException();
}
if (result == null) {
throw new ClassNotFoundException(name);
}
return result;
}
- 沙箱安全机制
- 假如自定义一个类和JDK的类的包名和类名完全一致,但是内部的方法不一样,这个时候运行程序就会报错,因为上级的类加载器会加载到这个类,但是没有自定义的方法。比如自定义一个java.lang.String,但是内部有一个main方法,并且运行,就会报错,因为这个类会被引导类加载器加载,但本身String类时没有main方法的。
- 好处:防止恶意串改JDK最基本的类,要不然你就可以自己写一个类,覆盖原始的类,别人运行就会报错的。
- 避免类的重复加载
- 很好理解,就是上级加载了,下级就不会加载了。
- 为什么要向上委托而不是向下委托
- 比较好的解释是,因为大部分程序的类都是自定义的,就可以做到大部分类由应用程序类加载器加载,要不然每次都先从上级开始,委派给下级,这样会造成很大的浪费。
- 为什么要一层层委托,然后从上层开始如果加载失败才交给下层加载,而不是下层开始能加载就加载,不能加载就再给上层加载
- 上面已经确认向上委托的好处,如果不向上委托,就是当需要加载某个类是,下级能加载,就加载,那不就破坏杀向安全机制了么,比如自定义一个java.lang.String,应用程序类加载器能加载,就加载了,就可以篡改JDK以下原始的类了,造成很大的风险。
package cn.cdqf.mydmsjportal.test;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
public class MyClassLoaderTest{
static class MyClassLoader extends ClassLoader{
private String classPath;
public MyClassLoader(String classPath){
this.classPath=classPath;
}
private byte[] loadByte(String name) throws IOException {
name=name.replaceAll("\\.","/");
FileInputStream fileInputStream = new FileInputStream(classPath + "/" + name+".class");
int len= fileInputStream.available();//获取文件大小
byte[] bytes = new byte[len];
fileInputStream.read(bytes);
fileInputStream.close();
return bytes;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] bytes = loadByte(name);
return defineClass(name,bytes,0,bytes.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
}
public static void main(String[] args) {
MyClassLoader myClassLoader = new MyClassLoader("D:\\qianfeng\\JavaEE2007\\self-study\\");
try {
//不要写成findClass!!!
Class<?> aClass = myClassLoader.loadClass("cn.cdqf.mydmsjportal.test.Test");
Object newInstance = aClass.newInstance();
Method method = aClass.getMethod("test");
method.invoke(newInstance,null);
//打印使用的类加载器
System.out.println(aClass.getClassLoader().getClass().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
建一个文件夹,放入Test.class,然后运行程序,得到的结果如下:
测试自定义类加载器
sun.misc.Launcher$AppClassLoader
这就是因为双亲委派机制,因为程序中的类路径下Test.class存在,就由父级加载器加载了,为什么自定义类加载器的父级类加载器是AppClassLoader?一方面是为了遵循双亲委派机制,另一方面从源码的角度来看:
//传入的就是系统类加载器,而系统类加载器默认的就是应用程序类加载器
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
private ClassLoader(Void unused, ClassLoader parent) {
//指定父级类加载器
this.parent = parent;
if (ParallelLoaders.isRegistered(this.getClass())) {
parallelLockMap = new ConcurrentHashMap<>();
package2certs = new ConcurrentHashMap<>();
domains =
Collections.synchronizedSet(new HashSet<ProtectionDomain>());
assertionLock = new Object();
} else {
// no finer-grained lock; lock on the classloader instance
parallelLockMap = null;
package2certs = new Hashtable<>();
domains = new HashSet<>();
assertionLock = this;
}
}
//获取系统类加载器,注意initSystemClassLoader();,初始化系统类加载器
@CallerSensitive
public static ClassLoader getSystemClassLoader() {
initSystemClassLoader();
if (scl == null) {
return null;
}
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(scl, Reflection.getCallerClass());
}
return scl;
}
//初始化系统类加载器,注意scl = l.getClassLoader();
private static synchronized void initSystemClassLoader() {
if (!sclSet) {
if (scl != null)
throw new IllegalStateException("recursive invocation");
sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
if (l != null) {
Throwable oops = null;
scl = l.getClassLoader();
try {
scl = AccessController.doPrivileged(
new SystemClassLoaderAction(scl));
} catch (PrivilegedActionException pae) {
oops = pae.getCause();
if (oops instanceof InvocationTargetException) {
oops = oops.getCause();
}
}
if (oops != null) {
if (oops instanceof Error) {
throw (Error) oops;
} else {
// wrap the exception
throw new Error(oops);
}
}
}
sclSet = true;
}
}
//获得类加载器
public ClassLoader getClassLoader() {
return this.loader;
}
//初始化会调用默认无参构造方法,loader赋值为AppClassLoader,搞定!
public Launcher() {
Launcher.ExtClassLoader var1;
try {
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
if (var2 != null) {
SecurityManager var3 = null;
if (!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
} catch (InstantiationException var6) {
} catch (ClassNotFoundException var7) {
} catch (ClassCastException var8) {
}
} else {
var3 = new SecurityManager();
}
if (var3 == null) {
throw new InternalError("Could not create SecurityManager: " + var2);
}
System.setSecurityManager(var3);
}
}
以上不让父级类加载器加载Test的方法有两个,一个是在类路径下删除,另外一个方法就是打破双亲委派机制,双亲委派机制的核心在loadClass方法中,因此重写即可!
package cn.cdqf.mydmsjportal.test;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
public class MyClassLoaderTest{
static class MyClassLoader extends ClassLoader{
private String classPath;
public MyClassLoader(String classPath){
this.classPath=classPath;
}
private byte[] loadByte(String name) throws IOException {
name=name.replaceAll("\\.","/");
FileInputStream fileInputStream = new FileInputStream(classPath + "/" + name+".class");
int len= fileInputStream.available();//获取文件大小
byte[] bytes = new byte[len];
fileInputStream.read(bytes);
fileInputStream.close();
return bytes;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] bytes = loadByte(name);
return defineClass(name,bytes,0,bytes.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
@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();
//注意
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
// 如果不是是要自己加载的,就让父级去加载
// 注意不能全部让自定义类加载器去加载,因为有些父级比如object是需要提前加载的
// 这些必须用父级加载器去加载
// 否则会报错:java.io.FileNotFoundException: D:\qianfeng\JavaEE2007\self-study\java\lang\Object.class (系统找不到指定的路径。)
if (!name.startsWith("cn.cdqf.mydmsjportal.test")){
c=this.getParent().loadClass(name);
}else {
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) {
MyClassLoader myClassLoader = new MyClassLoader("D:\\qianfeng\\JavaEE2007\\self-study\\");
try {
//不要写成findClass!!!
Class<?> aClass = myClassLoader.loadClass("cn.cdqf.mydmsjportal.test.Test");
Object newInstance = aClass.newInstance();
Method method = aClass.getMethod("test");
method.invoke(newInstance,null);
//打印使用的类加载器
System.out.println(aClass.getClassLoader().getClass().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
这样就是直接使用自定义类加载器啦
测试自定义类加载器
cn.cdqf.mydmsjportal.test.MyClassLoaderTest$MyClassLoader