java动态类加载学习

java动态类加载学习

静态代码块加载

整一个Person类,里面放静态代码块和动态代码块等等,看不同操作下,他们的调用情况

实例化一个类对象

new Person();

在这里插入图片描述

调用静态方法

Person.staticAction();

调用了静态代码块和静态方法
在这里插入图片描述

赋值

Person.id = 1;

这里只调用了静态代码块
在这里插入图片描述

所以静态代码块在类初始化的时候就调用了,而其他的在类实例化的时候才调用(比如构造代码块,无参构造函数)

Class c = Person.class;

这里只加载不初始化,所以不调用静态代码块

使用Class.forName

Class.forName("cc3.Person");

这里进行了初始化,调用了静态代码块
在这里插入图片描述

双亲委派

java有三种类加载器
1、启动类加载器(Bootstrap ClassLoader)
它是属于虚拟机自身的一部分,用C++实现的,主要负责加载<JAVA_HOME>\lib目录中或被-Xbootclasspath指定的路径中的并且文件名是被虚拟机识别的文件。加载String java.lang这些
2、扩展类加载器(Extension ClassLoader)
它是Java实现的,独立于虚拟机,主要负责加载<JAVA_HOME>\lib\ext目录中或被java.ext.dirs系统变量所指定的路径的类库。加载扩展包 ext
3、应用程序类加载器(Application ClassLoader)
它是Java实现的,独立于虚拟机。主要负责加载用户类路径(classPath)上的类库,如果我们没有实现自定义的类加载器那这玩意就是我们程序中的默认加载器。加载用户自定义类

在这里插入图片描述

双亲委派的意思是如果一个类加载器需要加载类,那么首先它会把这个类请求委派给父类加载器去完成,每一层都是如此。一直递归到顶层,当父加载器无法完成这个请求时,子类才会尝试去加载。这里的双亲其实就指的是父类,没有mother。父类也不是我们平日所说的那种继承关系,只是调用逻辑是这样。

理解:加载一个类时,先一直委派到顶层,从最顶层开始,去相应的文件夹中找相应的class文件,没有就往子类中找,其中第三层Application ClassLoader就加载我们用户自己写的类

类的继承关系

ctrl+aalt+u查看类的继承关系

在这里插入图片描述

ClassLoader-->SecureClassLoader-->URLClassLoader-->AppClassLoader
loadClass-->findClass(重写的方法)-->defineClass(从字节码加载类)

加载类测试

注意,其中第二步加载类的时候不进行初始化

ClassLoader cl = ClassLoader.getSystemClassLoader();
Class<?> c = cl.loadClass("cc3.Person");//  不进行初始化
c.newInstance();

URLClassLoader任意类加载

先整一个Hello.java

import java.io.IOException;

public class Hello {
    static {
        System.out.println("hello sk1y");
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

然后javac编译,将Hello.class文件移走,然后将源码中的Hello.java删除

file协议

注意第二行加载类的时候不进行初始化

URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///D:\\project\\java\\classes\\")});
Class<?> c = urlClassLoader.loadClass("Hello");
c.newInstance();

http协议

本地起一个http服务

        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://127.0.0.1:9999/")});
        Class<?> c = urlClassLoader.loadClass("Hello");
        c.newInstance();

在这里插入图片描述

调用成功
所以如果我们可以控制URLClassLoader类的参数,那么我们就可以进行任意类的调用

jar协议

将Hello.class打包为jar包

jar cf Hello.jar Hello.class

jar协议和http协议也可以配合着使用

        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("jar:file:///D:\\project\\java\\classes\\Hello.jar!/")});
        Class<?> c = urlClassLoader.loadClass("Hello");
        c.newInstance();

ClassLoader类的defineClass任意方法调用

这个方法是protected属性的,需要通过反射调用

在这里插入图片描述

        ClassLoader cl = ClassLoader.getSystemClassLoader();
        Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
        defineClass.setAccessible(true);
        //这里要指定加载的类
        byte[] code = Files.readAllBytes(Paths.get("D:\\project\\java\\classes\\Hello.class"));
        Class c = (Class) defineClass.invoke(cl,"Hello",code,0,code.length);
        c.newInstance();

运行结果
在这里插入图片描述

Unsafe类的defineClass

不能直接获取类对象,会有一个安全检查

但是可以通过theUnsafe成员变量来得到类对象
在这里插入图片描述

使用Unsafe类的defineClass方法

        ClassLoader cl = ClassLoader.getSystemClassLoader();
        Class c = Unsafe.class;
        Field theUnsafeField = c.getDeclaredField("theUnsafe");
        theUnsafeField.setAccessible(true);
        //获取Unsafe类对象
        Unsafe o = (Unsafe) theUnsafeField.get(null);
        byte[] code = Files.readAllBytes(Paths.get("D:\\project\\java\\classes\\Hello.class"));
        //加载
        Class<?> hello = o.defineClass("Hello", code, 0, code.length, cl, null);
        hello.newInstance();

运行结果
在这里插入图片描述

(在Spring里面Unsafe类对象可以直接生成)

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值