jvm之类加载器

news2024/11/24 20:07:49

写在前面

当我们通过javac命令将java源代码编译为Java字节码后,必须通过类加载器将其加载到jvm中才能运行,所以类加载器是jvm中非常重要的一个组成部分,本文我们就一起来看下吧!

1:类的生命周期

类的生命周期如下图:

在这里插入图片描述

注意以上包括卸载在内的所有过程都是通过类加载器完成的。

1.1:加载

将class字节码文件加载到jvm中,具体就是通过类加载器来完成的。class字节码本身的二进制信息会被加载到jvm的方法区中,并在堆中创建一个对应的java.lang.Class对象实例(java中万物皆对象,class字节码也不例外!),参考下图:

在这里插入图片描述

1.2:验证

首先通过魔法数字验证这是否是一个class字节码文件,java的class字节码使用前4个字节来进行标记cafebabe,如下使用submit打开查看:

在这里插入图片描述

可以类比pdf文件的魔数是"%PDF",则其十六进制表示就是2550 4446,如下图:

在这里插入图片描述

除了验证魔数之外,还会验证版本号,即class字节码文件可运行的jdk版本,如下就是版本53,即jdk1.9(52就是1.8,51就是1.7,50就是1.6,以此类推)

在这里插入图片描述

除了对于魔数和版本号的验证之外,还包括对字节码文件本身格式的验证等。

1.3:准备

为类的静态变量在方法区中分配内存,并给零值,即基础数据类型的默认值,如整型为0,布尔为false,具体如下:

在这里插入图片描述

假定有如下类:

class A {
    static int num1 = 90;
    staic boolean isOk = true;
    
    ...
}

则经过准备阶段后如下图:

在这里插入图片描述

注意这个阶段并不会将num1赋值为程序中定义的90,isOK也不会赋值为程序中定义的true,而只是会给默认值,因为本步骤的主要是为静态变量们找到容身之所,具体的赋值完成要等在初始化阶段完成。

1.4:解析

将常量池中的符号引用解析为实际引用,即将将使用到的类名称,如com.xx.A,解析为A类在方法区中的内存地址,这样当需要A类时就可以直接找到。所以解析就是将一个代表了某个信息的文本字符串转换为方法区中的某个内存地址。

1.5:初始化

执行构造器代码,执行静态代码块,为类静态变量赋值(还记得在准备阶段我们已经将类静态变量分配到方法去中内存并给默认值了吗?这里会给程序设置的实际值)。如下代码就会在该阶段被执行:

class A {
    static {
       sout("静态代码块逻辑");
    }
    public A() {
        sout("构造函数逻辑");
    }
    static {
       sout("静态代码块逻辑");
    }
}

注意{}代码块不会被执行,因为代码块和普通类成员变量一样是属于类实例的,因此只有在创建类实例对象的时候才会执行。

2:类何时加载并初始化

2.1:运行main所在类

当我们通过main函数启动程序时,该main函数所在的会被加载并初始化,即如下操作:

在这里插入图片描述

2.2:new指令的目标类

当我们通过new关键字创建一个类实例对象时,该类对应的class字节码会被加载并初始化,如下:

在这里插入图片描述

2.3:invokestatic指令访问类静态方法

当调用某静态方法时,该静态方法所在的类会被初始化,如下:

在这里插入图片描述

2.4:getstatic指令访问静态类成员变量

当访问某静态变量时,该静态变量所在的类会被加载并初始化,如下:

在这里插入图片描述

2.5:子类的初始化会触发父类初始化

通过extends关键字继承的父类会随着本类的初始化而初始化,如下:

在这里插入图片描述

2.6:有default方法的接口子类初始化

当一个接口有default方法时,如果有其子类被初始化,则该接口也会被初始化,因为default方法是提供了具体实现的方法,是可以直接被子类使用的如下:

在这里插入图片描述

2.7:通过反射API创建某类实例时

这种情况通new指令,因为都是要创建对象实例,所以肯定是需要初始化的。

2.8:MethodHandle调用方法时

MethodHandle调用方法时,方法所在的类会被加载并初始化如下:

在这里插入图片描述

有些时候类会被加载但不会被初始化,接着来看下。

3:类何时加载但不初始化

3.1:通过子类引用父类的静态字段

因为此时只要能够访问到父类的静态就行了,并不需要子类初始化,如下操作:

  • 定义类A,并定义静态变量
package yudaosourcecode.csdn.huohuo;

public class A {

    static {
        System.out.println("A 初始化了");
    }
    static int num1 = 90;
}
  • 定义A类的子类ASon
package yudaosourcecode.csdn.huohuo;

public class ASon extends A {
    static {
        System.out.println("ASon 初始化了");
    }
}
  • 定义B类通过ASon访问A的静态变量
package yudaosourcecode.csdn.huohuo;

public class B {
    public static void main(String[] args) {
        System.out.println(ASon.num1);
    }
}
  • 运行B输出如下

在这里插入图片描述

可以看到A类初始化了,ASon并没有初始化。

3.2:定义对象数组不会触发该类的初始化

因为此时只需要保证jvm中有该类的class即可,还没有使用,所以不会初始化,如下测试:

  • 定义类A
package yudaosourcecode.csdn.huohuo;

public class A {

    static {
        System.out.println("A 初始化了");
    }
    static int num1 = 90;
}
  • 定义类B,在其中定义A数组
package yudaosourcecode.csdn.huohuo;

public class B {
    public static void main(String[] args) {
        A[] as = {};
    }
}

运行,不会看到A 初始化了说明A并没有初始化:

在这里插入图片描述

3.3:应用某类的常量不会初始化该类

因为这种方式在编译时就是将常量值存入到常量池中,所以并不需要常量所在类初始化,如下:

  • 定义类A并定义常量
package yudaosourcecode.csdn.huohuo;

public class A {
    final static String name = "jack";

    static {
        System.out.println("A 初始化了");
    }
    static int num1 = 90;
}
  • 定义类B并访问A的常量
package yudaosourcecode.csdn.huohuo;

public class B {
    public static void main(String[] args) {
        System.out.println(A.name);
    }
}

运行,A并没有初始化,如下:

在这里插入图片描述

因为常量是不能在运行期被更改的,所以可以在编译器直接存入方法区中的常量池中,而不需要动态获取,但是普通静态变量不同,静态变量是可以程序运行的过程中被修改的,所以访问静态变量会导致静态变量所在类的初始化。

3.4:通过.class获取类的Class对象不会导致初始化

因为class字节码的Class对象在加载阶段就已经创建完毕并分配到堆上了,所以并不会进行初始化。如下:

  • 定义A类
package yudaosourcecode.csdn.huohuo;

public class A {
    final static String name = "jack";

    static {
        System.out.println("A 初始化了");
    }
    static int num1 = 90;
}
  • 定义B类,访问A类的class对象
package yudaosourcecode.csdn.huohuo;

public class B {
    public static void main(String[] args) {
        System.out.println(A.class);
    }
}

访问测试,A并没有初始化:

在这里插入图片描述

3.5:Class.forName设置initialize false不会初始化

initialize参数使用来告诉虚拟机是否要初始化指定类,false不初始化,true初始化,如下:

  • 定义A类
package yudaosourcecode.csdn.huohuo;

public class A {
    static {
        System.out.println("A 初始化了");
    }
    static int num1 = 90;
}
  • 定义B类,initialize设置为false
package yudaosourcecode.csdn.huohuo;

public class B {
    public static void main(String[] args) throws Exception {
        Class.forName("yudaosourcecode.csdn.huohuo.A", false, B.class.getClassLoader());
    }
}

运行:

在这里插入图片描述

可以看到A类并没有初始化。

如果使用 Class.forName("yudaosourcecode.csdn.huohuo.A");,此时initialize默认为true,则会初始化A类,如下:

在这里插入图片描述

3.6:通过ClassLoader的loadClass

通过ClassLoader的loadClass也是只会加载而不会初始化。如下:

  • 定义A类
package yudaosourcecode.csdn.huohuo;

public class A {
    static {
        System.out.println("A 初始化了");
    }
    static int num1 = 90;
}
  • 定义B类使用类加载器加载A类
package yudaosourcecode.csdn.huohuo;

public class B {
    public static void main(String[] args) throws Exception {
        B.class.getClassLoader().loadClass("yudaosourcecode.csdn.huohuo.A");
    }
}

运行:

在这里插入图片描述

4:3种类加载器

4.1:3种类加载器以及其特点

三种类加载器分别是启动类加载器BootstrapClassLoader,扩展类加载器ExtClassLoader,应用类加载器AppCLassLoader,其职责如下:

启动类加载器BootstrapClassLoader:负责加载jre核心类库,如jre/lib/rt.jar
扩展类加载器ExtClassLoader:负责加载lib/ext,或者java.ext.dirs指定目录的jar和class
应用类加载器AppClassLoader:负责加载-classpath,-cp,java.class.path属性指定的类路径

特点如下:

双亲委派:当需要根据类全限定名称加载某个class时,并不会自己直接加载,而是先找其父类加载器加载,当最终也没有加载类时则会报出ClassNotFoundException
负责依赖:当加载的类,依赖于其它类时,也会加载这些依赖的类
加载缓存:已经加载的类会进行缓存,避免重复加载

其中还有第四种类加载器,就是自定义的类加载器,如下图:

在这里插入图片描述

4.2:自定义类加载器

假定我们现在有这样的一个需求,有一个类机密程度非常高,其.class字节码文件不能暴露出去,因为一旦暴露出去就可以很容易的通过反编译工具,甚至是通过javap来读指令,获取源码信息,这个时候普通的类加载器就无法满足需求了,我们就需要来自定义类加载器,该类加载器可以加载一个.class加密后的字符串来加载类,简单起见,我们就用base64来模拟加密后的.class字节码,则假定我们有如下的类:

package yudaosourcecode.csdn.huohuo;

public class A {
    static {
        System.out.println("A 初始化了");
    }
}

接着我们执行如下操作,获取类对应字节码的加密结果这里是base64

bogon:huohuo xb$ javac -d . A.java
bogon:huohuo xb$ java yudaosourcecode/csdn/huohuo/A
A 初始化了
bogon:huohuo xb$ base64 yudaosourcecode/csdn/huohuo/A.class 
yv66vgAAADUAHgoABgAQCQARABIIABMKABQAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEACDxjbGluaXQ+AQAKU291cmNlRmlsZQEABkEuamF2YQwABwAIBwAYDAAZABoBAA5BIOWIneWni+WMluS6hgcAGwwAHAAdAQAdeXVkYW9zb3VyY2Vjb2RlL2NzZG4vaHVvaHVvL0EBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAADAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAAwAJAAsADAABAAkAAAAZAAAAAQAAAAGxAAAAAQAKAAAABgABAAAACQAIAA0ACAABAAkAAAAlAAIAAAAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAFAAgABgABAA4AAAACAA8=

这里的base64后的结果我们就可以在自定义类加载器中使用了,自定义类加载器如下:

package yudaosourcecode.csdn.huohuo;

import java.util.Base64;

public class ACLassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        String helloBase64 = "yv66vgAAADUAHgoABgAQCQARABIIABMKABQAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEACDxjbGluaXQ+AQAKU291cmNlRmlsZQEABkEuamF2YQwABwAIBwAYDAAZABoBAA5BIOWIneWni+WMluS6hgcAGwwAHAAdAQAdeXVkYW9zb3VyY2Vjb2RlL2NzZG4vaHVvaHVvL0EBABBqYXZhL2xhbmcvT2JqZWN0AQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3RyaW5nOylWACEABQAGAAAAAAADAAEABwAIAAEACQAAAB0AAQABAAAABSq3AAGxAAAAAQAKAAAABgABAAAAAwAJAAsADAABAAkAAAAZAAAAAQAAAAGxAAAAAQAKAAAABgABAAAACQAIAA0ACAABAAkAAAAlAAIAAAAAAAmyAAISA7YABLEAAAABAAoAAAAKAAIAAAAFAAgABgABAA4AAAACAA8=";
        byte[] bytes = decode(helloBase64);
        return defineClass(name,bytes,0,bytes.length);
    }

    public byte[] decode(String base64){
        return Base64.getDecoder().decode(base64);
    }
}

然后使用自定义类加载器加载我们的加密类:

package yudaosourcecode.csdn.huohuo;

public class B {
    public static void main(String[] args) throws Exception {
        new ACLassLoader().loadClass("yudaosourcecode.csdn.huohuo.A").newInstance();
    }
}

运行:

在这里插入图片描述

5:利用类加载器帮我们排查问题

5.1:找不到jar包

在工作中经常遇到这样问题,明明jar包已经加到classpath中,但就是找不到,这个时候我们能不能明确的获取各种类加载器加载了哪些jar包和class,从而定位问题呢?是可以的,定义如下类:

package yudaosourcecode.csdn.huohuo;

import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;

public class A {
    public static void main(String[] args) {
        // 启动类加载器
        URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        System.out.println("启动类加载器");
        for (URL url : urls) {
            System.out.println(" ==> " + url.toExternalForm());
        }
        // 扩展类加载器
        printClassLoader("扩展类加载器", A.class.getClassLoader().getParent());
        // 应用类加载器
        printClassLoader("应用类加载器", A.class.getClassLoader());
    }

    public static void printClassLoader(String name, ClassLoader CL) {
        if (CL != null) {
            System.out.println(name + " ClassLoader ‐> " + CL.toString());
            printURLForClassLoader(CL);
        } else {
            System.out.println(name + " ClassLoader ‐> null");
        }
    }

    public static void printURLForClassLoader(ClassLoader CL) {
        Object ucp = insightField(CL, "ucp");
        Object path = insightField(ucp, "path");
        ArrayList ps = (ArrayList) path;
        for (Object p : ps) {
            System.out.println(" ==> " + p.toString());
        }
    }

    private static Object insightField(Object obj, String fName) {
        try {
            Field f = null;
            if (obj instanceof URLClassLoader) {
                f = URLClassLoader.class.getDeclaredField(fName);
            } else {
                f = obj.getClass().getDeclaredField(fName);
            }
            f.setAccessible(true);
            return f.get(obj);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

运行,注意使用jdk8:

bogon:huohuo xb$ /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/bin/javac -d . A.java 
A.java:11: 警告: Launcher是内部专用 API, 可能会在未来发行版中删除
        URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
                             ^
1 个警告
bogon:huohuo xb$ /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/bin/java yudaosourcecode/csdn/huohuo/A
启动类加载器
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/resources.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/sunrsasign.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jsse.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jce.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/charsets.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jfr.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/classes
扩展类加载器 ClassLoader ‐> sun.misc.Launcher$ExtClassLoader@7852e922
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/sunec.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/nashorn.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/cldrdata.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/jfxrt.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/dnsns.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/localedata.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/jaccess.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/zipfs.jar
 ==> file:/System/Library/Java/Extensions/MRJToolkit.jar
应用类加载器 ClassLoader ‐> sun.misc.Launcher$AppClassLoader@2a139a55
 ==> file:/Users/xb/Desktop/D/dongsir-dev/java-life-current/java-life/src/main/java/yudaosourcecode/csdn/huohuo/

可以很清晰的看到启动类加载器,扩展类加载器,应用类加载器都加载了哪些jar和class。

5.2:NoSuchMethodError

如果是通过5.1:找不到jar包确定jar包肯定加载了,则就要看下是不是加载了不同版本的jar,即jar冲突了,一般是这个原因。

5.3:如何看类加载顺序

5.2:NoSuchMethodError如果是因为冲突造成的,则可以通过这里的方法来进一步定位问题,需要在java命令中增加‐XX:+TraceClassLoading 或者 ‐verbose 即可,如下:

bogon:huohuo xb$ /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/bin/java -XX:+TraceClassLoading yudaosourcecode/csdn/huohuo/A
[Opened /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Object from /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.io.Serializable from /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.String from /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar]
[Loaded java.lang.reflect.AnnotatedElement from /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar]
...

5.4:怎么修改启动类加载器和扩展类加载器加载内容

正常的,我们只需要rt.jar就行了,但默认的启动类加载器会加载很多其它的jar,此时可以通过‐Dsun.boot.class.path来指定要加载的jar,如下:

bogon:huohuo xb$ /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/bin/java -Dsun.boot.class.path="/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar" yudaosourcecode/csdn/huohuo/A
启动类加载器
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar
扩展类加载器 ClassLoader ‐> sun.misc.Launcher$ExtClassLoader@7852e922
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/ext/sunec.jar
 ...

可以看到此时启动类加载器只加载了rt.jar,对于扩展类加载器,正常其默认加载的jar包我们都是使用不到的,所以可以通过‐Djava.ext.dirs将其设置为空,即不加载,如下:

bogon:huohuo xb$ /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/bin/java -Djava.ext.dirs="" yudaosourcecode/csdn/huohuo/A
启动类加载器
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/resources.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/sunrsasign.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jsse.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jce.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/charsets.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jfr.jar
 ==> file:/Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/classes
扩展类加载器 ClassLoader ‐> sun.misc.Launcher$ExtClassLoader@15db9742
应用类加载器 ClassLoader ‐> sun.misc.Launcher$AppClassLoader@73d16e93
 ==> file:/Users/xb/Desktop/D/dongsir-dev/java-life-current/java-life/src/main/java/yudaosourcecode/csdn/huohuo/

此时扩展类加载器就什么都不加载了。

5.5:运行时动态加载类

有两种方式,第一种是通过自定义类加载器,第二种是通过URLClassLoader的addURL方法动态添加应用类加载器加载类时的扫描路径,分别看下。

5.5.1:自定义类加载器

  • 定义要动态加载的类A
package yudaosourcecode.csdn.huohuo;

public class A {
    static {
        System.out.println("A初始化了!");
    }
    public static void main(String[] args) {
    }

}
  • 定义自定义类类加载器
package yudaosourcecode.csdn.huohuo;

import sun.misc.BASE64Encoder;

import java.io.File;
import java.io.FileInputStream;
import java.util.Base64;

public class ACLassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // 这里的路径注意修改为自己的!!!
        String path = "/Users/xb/Desktop/D/dongsir-dev/java-life-current/java-life/src/main/java/yudaosourcecode/csdn/huohuo/yudaosourcecode/csdn/huohuo/A.class";
        File file = new File(path);
        byte[] byte1 = new byte[0];
        try (FileInputStream fis = new FileInputStream(file)) {
            byte1 = new byte[fis.available()];
            fis.read(byte1);
        } catch (Exception e) {}
        BASE64Encoder encoder = new BASE64Encoder();
        String helloBase64 = encoder.encode(byte1);
        helloBase64 = helloBase64.replaceAll("\n", "");
        byte[] bytes = decode(helloBase64);
        return defineClass(name,bytes,0,bytes.length);
    }

    public byte[] decode(String base64){
        return Base64.getDecoder().decode(base64);
    }
}
  • 定义测试类
package yudaosourcecode.csdn.huohuo;

public class LoadDynamic {

    public static void main(String[] args) throws Exception {
        new ACLassLoader().loadClass("yudaosourcecode.csdn.huohuo.A").newInstance();
    }
}
  • 运行注意用java8
bogon:huohuo xb$ /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/bin/javac -d . A.java 
bogon:huohuo xb$ /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/bin/javac -d . ACLassLoader.java 
ACLassLoader.java:3: 警告: BASE64Encoder是内部专用 API, 可能会在未来发行版中删除
import sun.misc.BASE64Encoder;
               ^
ACLassLoader.java:19: 警告: BASE64Encoder是内部专用 API, 可能会在未来发行版中删除
        BASE64Encoder encoder = new BASE64Encoder();
        ^
ACLassLoader.java:19: 警告: BASE64Encoder是内部专用 API, 可能会在未来发行版中删除
        BASE64Encoder encoder = new BASE64Encoder();
                                    ^
3 个警告
bogon:huohuo xb$ /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/bin/javac -d . LoadDynamic.java 
bogon:huohuo xb$ /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/bin/java yudaosourcecode/csdn/huohuo/LoadDynamic
A初始化了!

可以看到A类初始化了。

5.5.2:URLClassLoader的addUrl方法

定义如下类:

package yudaosourcecode.csdn.huohuo;

import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

public class B {
    public static void main(String[] args) throws Exception {
        // 注意修改为自己本地的路径
        String appPath = "file:/Users/xb/Desktop/D/dongsir-dev/java-life-current/java-life/src/main/java/yudaosourcecode/csdn/huohuo/yudaosourcecode/csdn/huohuo";
        URLClassLoader urlClassLoader = (URLClassLoader) B.class.getClassLoader();
        try {
            Method addURL = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
            // 暴力访问
            addURL.setAccessible(true);
            URL url = new URL(appPath);
            addURL.invoke(urlClassLoader, url);
            // 动态加载A类
            Class.forName("yudaosourcecode.csdn.huohuo.A");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行:

...
[INFO] --- exec-maven-plugin:3.0.0:exec (default-cli) @ java ---
A初始化了!
[INFO] 
...

写在后面

详解java类的生命周期 。

文件类型识别----魔数 。

一个类的生命周期 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/478676.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode刷题之回文链表and最长回文子串

234.回文链表 方法一:找中间结点,断开链表,后一段链表进行反转 思路:①找中间结点:使用快慢指针fast,slow,fast每次走两个,slow每次走一个; 如果链表的个数是奇数个,那么最后slow指向中间节点 如果链表的个数是偶数个,那么最后slow指向中间两个节点的后一个 ②使用prev指针保…

文心一言 VS chatgpt (13)-- 算法导论3.1 8题 3.2 1题

八、可以扩展我们的记号到有两个参数n和m的情形&#xff0c;其中的n和m可以按不同速率独立地趋于无穷。对于给定的函数g(n, m)&#xff0c;用O(g(n, m))来表示以下函数集&#xff1a; O(g(n, m)) { f(n, m): 存在正常量c、和&#xff0c;使得对所有n>n0或m>m0&#xff…

unity 性能优化之合批和剔除

批次对渲染的性能影响是比较大的&#xff0c;批次过多会导致cpu提交的次数过多&#xff0c;导致每帧渲染时间过长&#xff0c;所以我们需要对其优化&#xff0c;减少Bathches数量和SetPassCall次数。 批次合并的方法有多种&#xff0c;下面一一列出&#xff1a; 手动合批 将相…

CRM系统多少钱一套?盘点主流各大CRM系统价格

阅读本文你将了解&#xff1a;1.CRM定价规则&#xff1b;2.各大CRM系统报价&#xff08;CRM系统多少钱一套&#xff09;;3.CRM系统费用构成。 一、CRM定价规则 很多企业都寻求使用CRM系统来管理客户关系&#xff0c;从而优化管理流程&#xff0c;提升业绩。 对于企业而言&…

1985-2021年全国31省一二三产业就业人数/各省分产业就业人数数据(无缺失)

1985-2021年全国31省一二三产业就业人数/各省分产业就业人数数据&#xff08;无缺失&#xff09; 1、时间&#xff1a;1985-2021年 2、范围&#xff1a;包括全国31省 3、来源&#xff1a;各省NJ、社会统计NJ、人口和就业NJ 4、指标包括&#xff1a;省第一产业就业人数、省第…

Baumer工业相机堡盟相机如何使用PixelTransformation像素转换功能(像素转换功能的使用和优点以及行业应用)(C#)

项目场景 Baumer工业相机堡盟相机是一种高性能、高质量的工业相机&#xff0c;可用于各种应用场景&#xff0c;如物体检测、计数和识别、运动分析和图像处理。 Baumer的万兆网相机拥有出色的图像处理性能&#xff0c;可以实时传输高分辨率图像。此外&#xff0c;该相机还具…

2023年,web前端工程师20道Vue面试题及解析

本文章列出了20道关于Vue.js的面试题&#xff0c;包括基础和进阶问题&#xff0c;并提供了问题的答案及解析&#xff0c;以帮助读者更好地准备面试。从指令、生命周期函数到组件间通信和路由等各方面都作了涉及。 1. 什么是Vue.js&#xff1f;它有哪些特点&#xff1f; 答案&…

Git服务器集成(一)

本篇文章旨在分享本人在学习Git时的随笔记&#x1f929; 文章目录 Git 远程服务器1、下载 Git 软件&#xff08;linux版本&#xff09;2、安装 Git 软件2.1 解压 Git2.2 安装依赖2.3 删除旧版 Git2.4 编译、安装 Git2.5 配置环境变量2.6 建立链接文件2.7 测试安装 3、创建 Git …

使用Selenium控制Chrome浏览器 --工作自动化

使用Selenium控制Chrome浏览器 --工作自动化 背景&#xff1a; 最近朋友在用秒账做帐时&#xff0c;由于销售单量很大&#xff0c;重复录入工作一天一录就近五个小时&#xff0c;寻求帮助&#xff0c;问能不能把这重复劳动减少些&#xff0c;看后分析&#xff0c;使用web自动…

为网站的中文和英文使用不同的字体

CSS为网站的中文和英文使用不同的字体 前言 最近我在搭建个人网站&#xff0c;在这个过程中遇到很多的问题&#xff0c;不过chatGPT3.5帮助了很多&#xff0c;这是我使用的咒语&#xff1a; 我正在搭建一个个人网站&#xff0c;使用python Flask框架&#xff0c;CSS采用Boots…

linux网络设置与维护命令

文章目录 一、linux网络设置与维护命令总结 一、linux网络设置与维护命令 Linux ifconfig命令:配置或显示网络接口信息 Linux netstat命令:显示网络状态 Linux ip命令:执行网络管理任务 Linux ping命令:测试主机间网络连通性 Linux wall命令:发送广播 Linux finger命令:查找并…

ShardingJDBC的实核心流程和商户商家订单的分片实现

一、ShardingJDBC的核心流程 ShardingJDBC的核心流程主要分成六个步骤&#xff0c;分别是&#xff1a;SQL解析->SQL优化->SQL路由->SQL改写->SQL执行->结果归并&#xff0c;流程图如下: 4.1、SQL解析 分为词法解析和语法解析。 先通过词法解析器将SQL拆分为一…

基于ESP32 蓝牙游戏手柄设计

使用 ESP32 并通过 BLE 通信的 DIY 手持游戏手柄 这个项目中使用的东西 硬件组件 esp32 1 ws2812b 6 操纵杆 2 角度按钮 2 按钮 8 18560电池和电池座 2 3路拨动开关 1 TP4056带保…

【从0到1了解Libarchive】带你了解Libarchive Libarchive的用途意义以及成功入门Libarchive

目录 0 如果你还不知道Libarchive是什么请一定要先看一下 1 简介 1.1 为什么实现Libarchive 1.2 到底都有谁在用呢&#xff1f; 1.3 Libarchive都有哪些功能 1.4 我们可以通过这些获取更多信息 1.5 如何贡献 2 Libarchive归档与压缩 3 Libarchive编译 4 Libarchive简…

ApplicationContext 和 BeanFactory 的区别

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录 ApplicationContext 和 BeanFactory 的区别 ApplicationContext 和 BeanFactory 的区别 那么这 2 种获取 Spring 上下文对象…

搭建electron-vue上

electron-vue 准备工作修改package.jsonappveyor.yml.travis.yml.gitignore.eslintrc.js.eslintignore.babelrcsrc/renderer/main.jssrc/renderer/App.vuesrc/renderer/store/index.jssrc/renderer/store/modules/Counter.jssrc/renderer/store/modules/Counter.jssrc/renderer…

渗透测试 | 目录扫描

0x00 免责声明 本文仅限于学习讨论与技术知识的分享&#xff0c;不得违反当地国家的法律法规。对于传播、利用文章中提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;本文作者不为此承担任何责任&#xff0c;一旦造成后果请自行承担…

BUUCTF ciscn_2019_n_8

小白垃圾做题笔记而已&#xff0c;不建议阅读观看。 看到保护后我………………傻眼了。这保护全看根本无从下手嘛。 看到源程序后我依然有点迷茫。 我是小白&#xff0c;直到看到了大佬的wp&#xff0c;我才有有点头绪。 这里&#xff0c;var[13]里的不能为0 并且里边存储的还…

Java 基础入门篇(六)——— String 类详解

文章目录 一、String 类概述二、String 创建对象的方式2.1 创建对象的两种方式2.2 面试&#xff1a;两种方式的区别 ★2.3 常见面试题 ★ 三、String 类常用方法3.1 字符串内容比较3.2 常用 API&#xff1a;遍历、截取、替换、分割 一、String 类概述 java.lang.String 类代表…

R语言 | 日期和时间的处理

目录 一、日期的设定与使用 1.1 as.Date()函数 1.2 weekdays()函数 1.3 months()函数 1.4 quarters()函数 1.5 Sys.localeconv()函数 1.6 Sys.Date()函数 1.7 再谈seq()函数 1.8 使用不同格式表示日期 二、时间的设定与使用 2.1 Sys.time()函数 2.2 as.POSIXct()函数…