类加载器一篇足以

GIS应用技巧之三招搞定重叠区域数据提取 在很多分析过程中,常常需要分析判断哪些图形发生了重叠,该重叠情况的发生在对应分析环境下具有特定意义。例如在缓冲区分析中,若两个受灾点周边的受灾面积(即缓冲区)有所重叠,分析重叠区域的受灾影响时,就需要考虑相互影响的因素。本例中先用【要素转面】工具提取出发生重叠的区域,其次用【空间连接】工具将原始图层信息连接到重叠数据的图层,分析重叠数据的属性表,即可得到发生重叠的原始图层数据。 Step1 提取重叠区属数据。打开【数据管理工具】|【要素】|【要素转面】工具,输入要素设置为“circles”,输出要素类设置为 阅读详情


前言

语雀地址:https://www.yuque.com/yangxiaofei-vquku/wmp1zm/dmz2gd

一、类加载器相关概念

1.Classloader的作用

Classloader是java的核心组件,所有的Class都是又Classloader进行加载的,Classloader负责通过各种方式将Class信息的二进制数据流读入JVM内部,转换为一个与目标类对应的java.lang.Class对象实例。然后交给java虚拟机进行连接、初始化等操作。因此,Classloader在整个装载阶段,只能影响到类的加载,而无法通过Classloader去改变类的连接和初始化行为,至于它是否可以运行,则有Execution Engine决定。

2.类加载的分类

类的加载可以分为显示加载vs隐式加载

  • 显示加载:是指在代码中显示使用Class.forName(),或者Classloader.loaderClass()加载class
  • 隐式加载:是指A类中引用了B类(import)则在A类加载时会隐式加载B类
    在这里插入图片描述

3.类加载器的必要性

一般情况下,java开发人员并不需要在程序中显示的使用类加载器,但是了解类加载器的加载机制去显得至关重要。

  • 避免开发中遇到java.lang.ClassNotFoundException异常或java.lang.NoClassDefFoundError异常时手足无措。只有了解类加载器的加载机制才能在出现异常时快速定位问题。
ClassNotfoundException和NoClassDefFoundError两者的本质区别就是:
ClassNotfoundException时在编译时JVM加载不到类或者找不到类导致的; 
而NoClassDefFoundError是在运行时JVM加载不到类或者找不到类。
NoClassDefFoundError出现举例:在父类加载器加载的A类中调用了子类加载器加载的B类,B类对父类在的器不可见,
此时会出现NoClassDefFoundError。
  • 需要支持类的动态加载或者需要对编译后的字节码进行加密解密操作是,就需要和类加载器打交道了
    注:源码之下无秘密,在java的类加载器层面上处理加密解密还是比较容易被破解的,建议使用openjdk重写defineClass1的native实现完成解密操作,这样相对于在java层面上更安全一些
  • 开发人员可以在程序编写自定义类加载器来重新定义加载规则

4.命名空间

命名空间总结:定义类加载器和类本身一同确定其在虚拟机中的唯一性。

①.何为类的唯一性

对于任意一个类,都需要由加载他的类加载器和这个类本身一同确认其在Java虚拟机中的唯一性。每一个类加载器,都拥有一个独立的命令空间:比较两个类是否相等,只有在这两个类是由同一个类加载器加载的前提下才有意义。否则,即便这两个类源自同一个Class文件,被同一个虚拟机加载,只要他们的类加载器不同,那这两个类就必定不相等。

②.命名空间
  • 每个类加载器都有自己的命名空间,命名空间由该加载器及其所有的父加载器的类组成
  • 同一命名空间中,不会出现类完整名字(包括类的包名)相同的两个类
    注:这必须是在双亲委派机制的前提下,例如:使用自定义类加载器调用findClass可以再次加载系统类加载器加载过的类,但是一个类加载器不能重复使用findClass加载一个类两次。不考虑双亲委派的话应该说同一个类加载器加载的类中不会出现完整名字相同的两个类。
  • 在不同的命名空间中有可能会出现类的完整名字相同的两个类
③.有关命名空间的例子
例子1:不同类加载器加载的同一个class文件会产生两个Class对象
public class MyClassloaderTest1 {
   final static String path="/Users/yangxiaofei/lago/learning-notes/JVM/jvmDemo/src/main/java/";
    public static void main(String[] args) throws Exception{
        MyClassloader myClassloader1=new MyClassloader(path);
        MyClassloader myClassloader2=new MyClassloader(path);
    // 主要此处不能用loadClass,使用loadClass就会由系统类加载器去加载target下的User.class了
        Class<?> userClass2 = myClassloader1.findClass("jvm.memory.classloader.User");
        Class<?> userClass1 = myClassloader2.findClass("jvm.memory.classloader.User");

        System.out.println(userClass2==userClass1);

        System.out.println("userClass1的类加载器是"+userClass1.getClassLoader());

        System.out.println("userClass2的类加载器是"+userClass2.getClassLoader());

    }

}
打印结果:
false
userClass1的类加载器是jvm.memory.classloader.MyClassloader@7c75222b
userClass2的类加载器是jvm.memory.classloader.MyClassloader@1cd072a9
例子2:打破双亲委派的情况下,MyClassloader可以加载系统类加载器已经加载过的类(定义类加载器和类本身一同确定其在虚拟机中的唯一性,下面情况由于打破了双亲委托致使定义类加载器不同,所以不足以确定类的唯一性)
/**
 * 打破双亲委派的情况下,MyClassloader可以加载系统类加载器已经加载过的类
 */
public class MyClassloaderTest2 {
    final static String path="/Users/yangxiaofei/lago/learning-notes/JVM/jvmDemo/target/classes/";
    public static void main(String[] args)throws Exception {
        Field classes = ClassLoader.class.getDeclaredField("classes");
        classes.setAccessible(true);
        MyClassloader myClassloader1=new MyClassloader(path);
        // 使用loadClass会遵循双亲委派机制最终由系统类加载器加载User类
        Class<?> userClass1 = myClassloader1.loadClass("jvm.memory.classloader.User");
        System.out.println("************打印系统类加载器加载的类******************");
        Vector a=(Vector)classes.get(myClassloader1.getParent());
        for (Object o : a) {
            System.out.println(o);
        }
        System.out.println("************打印MyClassloader加载器加载的类****************");

        Vector b=(Vector)classes.get(myClassloader1);
        for (Object o : b) {
            System.out.println(o);
        }

        Class<?> userClass2 = myClassloader1.findClass("jvm.memory.classloader.User");

        System.out.println(userClass2==userClass1);

        System.out.println("userClass1的类加载器是"+userClass1.getClassLoader());

        System.out.println("userClass2的类加载器是"+userClass2.getClassLoader());


        System.out.println("************再次打印系统类加载器加载的类******************");
        Vector aa=(Vector)classes.get(myClassloader1.getParent());
        for (Object o : aa) {
            System.out.println(o);
        }
        System.out.println("************再次打印MyClassloader加载器加载的类****************");

        Vector bb=(Vector)classes.get(myClassloader1);
        for (Object o : bb) {
            System.out.println(o);
        }

    }
}


打印结果:
************打印系统类加载器加载的类******************
class com.intellij.rt.execution.application.AppMainV2$Agent
class com.intellij.rt.execution.application.AppMainV2
class com.intellij.rt.execution.application.AppMainV2$1
class jvm.memory.classloader.MyClassloaderTest2
class jvm.memory.classloader.MyClassloader
class jvm.memory.classloader.User
************打印MyClassloader加载器加载的类****************
false
userClass1的类加载器是jdk.internal.loader.ClassLoaders$AppClassLoader@2c13da15
userClass2的类加载器是jvm.memory.classloader.MyClassloader@7c30a502
************再次打印系统类加载器加载的类******************
class com.intellij.rt.execution.application.AppMainV2$Agent
class com.intellij.rt.execution.application.AppMainV2
class com.intellij.rt.execution.application.AppMainV2$1
class jvm.memory.classloader.MyClassloaderTest2
class jvm.memory.classloader.MyClassloader
class jvm.memory.classloader.User
************再次打印MyClassloader加载器加载的类****************
class jvm.memory.classloader.User

例子3:即便打破双亲委派机制,同一个类加载器也不能加载同一个类两次
/**
 * 即便打破双亲委派机制,同一个类加载器也不能加载同一个类两次
 */
public class MyClassloaderTest3 {
    final static String path="/Users/yangxiaofei/lago/learning-notes/JVM/jvmDemo/src/main/java/";
    public static void main(String[] args) throws Exception{


        MyClassloader myClassloader1=new MyClassloader(path);

        // 主要此处不能用loadClass,使用loadClass会遵循双亲委派机制userClass2不会真正加载而是直接被赋予userClass1的引用
        Class<?> userClass1 = myClassloader1.findClass("jvm.memory.classloader.User");
        Class<?> userClass2 = myClassloader1.findClass("jvm.memory.classloader.User");

    }
}


打印结果(出现LinkageError异常):
Exception in thread "main" java.lang.LinkageError: loader jvm.memory.classloader.MyClassloader @1cd072a9 (instance of jvm.memory.classloader.MyClassloader, child of 'app' jdk.internal.loader.ClassLoaders$AppClassLoader) attempted duplicate class definition for jvm.memory.classloader.User.
	at java.base/java.lang.ClassLoader.defineClass1(Native Method)
	at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
	at jvm.memory.classloader.MyClassloader.findClass(MyClassloader.java:47)
	at jvm.memory.classloader.MyClassloaderTest3.main(MyClassloaderTest3.java:15)

5.类加载机制的基本特征

  • 双亲委派模型。但是不是所有类加载都遵守这个模型,有时候,启动类加载器所加载的类型,是可能需要加载用户代码的比如SIP机制,具体如JDBC的驱动发现等,在这种情况下就不会用双亲委派模型去加载了,而是利用线程上下文类加载器去打破它(默认的线程上下文类加载器就是系统类加载器)。
  • 可见性,子类加载器可以访问父类加载器加载的类型,但是反过来是不被允许的
  • 单一性,由于父加载器加载的类对于子类加载器是可见的,所以父加载器中加载过的类型,就不会再子加载器中重复加载。但是类加载器"邻居"间(MyClassloader的两个实例),同一类型可以被多次加载,因为互相并不可见。

二、类加载器分类(java8)

JVM支持两种类型的类加载器,分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器。
从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是java虚拟机规范却没有这么定义,而是将所有派生与抽象类CLassLoader的类加载器都划分为自定义类加载器所有扩展类加载器和系统类加载器都算自定义类加载器。无论类加载器的类型如何划分,在程序中我们最常见的类加载器结构主要情况如下:
在这里插入图片描述

1.引导类加载器

  • 这个类是由C/C++语言实现的,嵌套在JVM内部。
  • 它用加载java的核心类库(JAVA_HOME/jre/lib/rt.jar或sun.boot.class.path路径下的内容),用于提供JVM自身需要的类。
  • 并不继承自java.lang.ClassLoader,没有父类加载器
  • 出于安全考虑,Bootstrap启动类加载器只能加载包名为java、javax、sun等开头的类
  • 加载扩展类加载器和应用程序类加载器,并指定为他们的父类加载器
  • 在java代码中没有具体实现,通常用null代表

2.扩展类加载器

  • JAVA语言编写,由sun.misc.Launcher$ExtClassLoader实现
  • 继承与ClassLoader类
  • 父类加载器为Bootstrap启动类加载器
  • 从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库,如果用户创建的jar放在此目录下,也可以由扩展类加载器加载到

3.系统类加载器

  • java语言编写,由sun.misc.launcher$AppClassLoader实现
  • 继承与ClassLoader类
  • 父类加载器是启动类加载器
  • 它负责加载环境变量classpath或系统属性java.class.path指定路径下的类库
  • 应用程序中的类加载器默认是系统类加载器
  • 它是用户自定义类加载器的默认父加载器
  • 通过ClassLoader.getSystemClassLoader()方法可以获取该类加载器

4.数组的类加载器(补充)

数组的Class对象,不是由类加载器去创建的,而是java运行期JVM根据需要自动创建的,数组类的Class.getClassLoader()返回的类加载器与数组中元素类型的类加载器是一样的,如果数组中是基本数据类型则没有类加载器,如果是String、Integer、Long等引用类型则为Bootstrap类加载器,如果是User等自定义的引用类型则为系统类加载器。
在这里插入图片描述

注:基本数据类型由虚拟机预先定义不需要类加载,不需要类加载器;引用数据类型则需要进行类加载,需要类加载器

5.初始类加载器和定义类加载器(补充)

不说废话直接上代码

public class User {
    static {
        Dog dog=new Dog();
        System.out.println("我有一只小狗,他的初始类加载器是"+User.class.getClassLoader()+",他的定义类加载器是"+dog.getClass().getClassLoader());
    }
}
/**
 * 初始类加载器和定义类加载器
 */
public class MyClassloaderTest4 {
    final static String path="/Users/yangxiaofei/lago/learning-notes/JVM/jvmDemo/target/classes/";
    public static void main(String[] args) throws Exception {
        MyClassloader myClassloader=new MyClassloader(path);
        Class<?> aClass = myClassloader.findClass("jvm.memory.classloader.User");
        Object o = aClass.newInstance();
    }
}

打印结果:我有一只小狗,他的初始类加载器是jvm.memory.classloader.MyClassloader@1cd072a9,他的定义类加载器是jdk.internal.loader.ClassLoaders$AppClassLoader@2c13da15

因为Dog在User中被引用触发隐式加载所以User的类加载器是Dog的初始类加载器,有由于双亲委派机制的存在最终加载Dog的类是系统类加载器,所以系统类加载器是Dog类的定义类加载器。

三、双亲委派机制

  首先这里的"双亲"并不是指真正的父类,子类加载器和父类加载器直接不是继承关系,而且子类加载器里面有个父类加载器的引用存放于parent变量中,从而子类加载器可以间接访问父类加载器加载的类以及父类加载器的属性和方法。

1.定义与本质

一个类加载器在接到一个类加载请求时,它首先不会自己尝试去加载这个类,而是把这个请求任务委托给父类加载器去完成,依次递归,如果父类加载器可以完成这个加载任务就成功返回,只有父类加载器不能完成加载任务时,才会自己去加载。

2.优势

  • 避免类的重复加载,确保一个类的全局唯一性
  • 保护程序安全,防止核心API被随意修改
    在双亲委派机制下java的核心类库始终由Bootstrap启动类加载器加载,不会被系统类加载器加载,避免了全类名冲突恶意覆盖行为。
①代码支持(见loaderClass源码分析)
  • 先在当前类加载器的缓存中查找有无目标类,如果有直接返回。
  • 判断当前类加载器的父类加载器是否为空,如果不为空则递归调用parent.loadClass()进行加载
  • 相反,如果当前类加载器为空,则调用findBootstrapClassOrNull()方法让启动类加载器进行加载
  • 如果以上3条路径都未加载成功,则调用findClass进行加载,改方法最后调用definClass系列的native方法加载目标java类
②思考

如果在自定义类加载器中重新loaderClass()或直接用findClass()加载等方式打破双亲委派机制,此时来加载自定义的java.lang.String类能否加载成功破坏核心类库呢?
答案是否定的,因为JDK自定义的类加载器在加载类的最后一不都会调用defineClass(String,byte[],int,int,ProtectionDomain)方法,该方法会执行**preDefineClass()**方法,此方法中对核心类库进行了安全检查。

3.劣势

双亲委派机制的最大缺点在于检查类是否加载的过程是单向的,这个方式虽然从结构上比较清晰,使各个ClassLoader的职责非常明确,但是使得顶层的ClassLoader无法访问底层ClassLoader加载的类。

4.破坏双亲委托机制

①JDK1.2之前

双亲委派模型是在jdk1.2之后引入的,之前自然不存在。

②线程上下文类加载器

对SPI不了解的可以看下:Java的SPI机制
在SPI服务机制中就要求JDK核心类库rt.jar中的代码去调用classpath下面代码,而rt.jar中的代码的定义类加载器是Bootstrap启动类加载器,是绝对加载不到classpath中的类的,此时为了打破这个僵局推出了线程上下文类加载器,这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果线程创建时未设置它将会从父线程中继承一个,如果在应用程序的全局范围内都未设置过的话,那么这个类加载器的默认就是应用程序类加载器。
在这里插入图片描述

③代码热替换、模块热部署

想要实现代码的热替换必须要打破双亲委派模型使用自定义的类加载器才行,比较成熟的技术有OSGI有兴趣可以自行百度一下,下面我举一个比较简单代码热替换例子。
在这里插入图片描述

王厨师做一道回锅肉做完后发现忘记放肉了,然后抓紧将肉加进去。直接上代码

/**
 * 王厨师
 */
public class Cook_wang implements Cook {

    @Override
    public void cooking() {
        StringBuffer stringBuffer=new StringBuffer();
        stringBuffer.append("这是一道回锅肉材料有:").append("葱、").append("辣椒、").append("醋、").append("锅");
        System.out.println(stringBuffer.toString());
    }
}
/**
 * 厨师接口
 */
public interface Cook {
    void cooking();
}

/**
 * 自定义类加载器
 */
public class MyClassloader extends ClassLoader{
    private String path;

   public MyClassloader(String path){
       this.path=path;
   }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] bytes1 = new byte[0];
        FileInputStream fileInputStream=null;
        ByteArrayOutputStream byteArrayOutputStream=null;
        try {
            String classFilePath=path+name.replace(".","/")+".class";
            fileInputStream=new FileInputStream(new File(classFilePath));
            byteArrayOutputStream=new ByteArrayOutputStream();
            byte[] bytes=new byte[1024];
            int len;
            while ((len=fileInputStream.read(bytes))!=-1){
                byteArrayOutputStream.write(bytes,0,len);
            }
            bytes1 = byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            throw new ClassNotFoundException(e.getMessage());
        } finally {
            try {
                if(fileInputStream!=null){
                    fileInputStream.close();
                }
                if(byteArrayOutputStream!=null){
                    byteArrayOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return defineClass(null,bytes1,0,bytes1.length,null);
    }
}


/**
 * 工厂容器
 */
public class SpringBeanFactory {
    private final static String path="/Users/yangxiaofei/lago/learning-notes/JVM/jvmDemo/target/classes/";
    private static Map<Class,Object> beanList=new HashMap<>();

    public SpringBeanFactory(){
        Thread thread=new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                try {
                    Thread.sleep(1000);
                    MyClassloader myClassloader=new MyClassloader(path);
                    // 这里就暂且只加载王厨师了,实战是应加载全部类
                    Class<?> aClass = myClassloader.findClass("jvm.memory.classloader.hotdeploy.Cook_wang");
                    beanList.put(Cook.class,aClass.newInstance());
                } catch (Exception e) {
                    e.printStackTrace();
                } }
            }
        });
        thread.setDaemon(true);
        thread.start();
    }




  public  static<T> T  getBean(Class<T> beanClass){
      Object object = beanList.get(beanClass);
      T cast = beanClass.cast(object);
      return cast;
  }


}


/**
 * 热部署测试类
 */
public class HotDeployTest {
    public static void main(String[] args) throws Exception{
        // 初始化容器
        SpringBeanFactory springBeanFactory=new SpringBeanFactory();

        while (true){
            Thread.sleep(2000);
            Cook bean = springBeanFactory.getBean(Cook.class);
            bean.cooking();
        }

    }
}

运行程序打印:
这是一道回锅肉材料有:葱、辣椒、醋、锅
这是一道回锅肉材料有:葱、辣椒、醋、锅
这是一道回锅肉材料有:葱、辣椒、醋、锅

王厨师添加了肉后

/**
 * 王厨师
 */
public class Cook_wang implements Cook {

    @Override
    public void cooking() {
        // 急忙补上肉
        StringBuffer stringBuffer=new StringBuffer();
        stringBuffer.append("这是一道回锅肉材料有:").append("葱、").append("辣椒、").append("醋、").append("锅、").append("肉");
        System.out.println(stringBuffer.toString());
    }
}

此时程序继续运行打印:
这是一道回锅肉材料有:葱、辣椒、醋、锅、肉
这是一道回锅肉材料有:葱、辣椒、醋、锅肉

代码热替换成功

四、Classloader源码解析

1.loaderClass

public Class<?> loadClass(String name) throws ClassNotFoundException {
    	// 第二个参数是是否进行解析的意思,默认不进行解析
        return loadClass(name, false);
}


protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 检查看该类是否已经被加载过,如果已经被加载过则直接返回(优先检查定义类加载器是否加载过)
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                // 如果该类从未加载过,则判断当前类加载器的父类加载器是否为null
                    if (parent != null) {
                  // 如果父类加载器不为null,则直接委托父类加载器来加载,递归调用loadClass()
                        c = parent.loadClass(name, false);
                    } else {
                   // 如果父类加载器为null,说明该加载器为ExtClassLoader扩展类加载器,此时调用Bootstrap启动类加载器进行加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // 父类加载器也不能加载,此时调用findClass自己进行加载
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

2.findClass

findClass方法在自定义类加载器是官方推荐的重新方法,此方法主要完成的工作是根据类的全限定名通过各种途径获取改类对于class文件的数据流并输出位byte[]字节数组,最后调用defineClass系列重载方法加载改类。

3.defineClass

defineClass系列重载方法最后都会调用defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain)

protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                                         ProtectionDomain protectionDomain)
        throws ClassFormatError
    {	
        protectionDomain = preDefineClass(name, protectionDomain);
        String source = defineClassSourceLocation(protectionDomain);
        Class<?> c = defineClass1(this, name, b, off, len, protectionDomain, source);
        postDefineClass(c, protectionDomain);
        return c;
    }

最后会调用本地方法defineClass1来加载该类获取Class实例,想要继续深入JVM源码探究的可以看下类加载时JVM在干什么,源码分析。

4.preDefineClass

对要加载的class的全限定名进行检查,以java开头的类会直接抛出异常,防止核心API被篡改。

 private ProtectionDomain preDefineClass(String name,
                                            ProtectionDomain pd)
    {
        if (!checkName(name))
            throw new NoClassDefFoundError("IllegalName: " + name);

        // Note:  Checking logic in java.lang.invoke.MemberName.checkForTypeAlias
        // relies on the fact that spoofing is impossible if a class has a name
        // of the form "java.*"
     // 检查是否已java开头
        if ((name != null) && name.startsWith("java.")
                && this != getBuiltinPlatformClassLoader()) {
            throw new SecurityException
                ("Prohibited package name: " +
                 name.substring(0, name.lastIndexOf('.')));
        }
        if (pd == null) {
            pd = defaultDomain;
        }

        if (name != null) {
            checkCerts(name, pd.getCodeSource());
        }

        return pd;
    }

五、自定义类加载器

在没有特殊情况下自定义类加载器官方建议保留双亲委派机制,重写findClass方法即可。

/**
 * 自定义类加载器
 */
public class MyClassloader extends ClassLoader{
    private String path;

   public MyClassloader(String path){
       this.path=path;
   }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] bytes1 = new byte[0];
        FileInputStream fileInputStream=null;
        ByteArrayOutputStream byteArrayOutputStream=null;
        try {
            // 将.换成/
            String classFilePath=path+name.replace(".","/")+".class";
            // 获取.class文件的输入流
            fileInputStream=new FileInputStream(new File(classFilePath));
            byteArrayOutputStream=new ByteArrayOutputStream();
            byte[] bytes=new byte[1024];
            int len;
            while ((len=fileInputStream.read(bytes))!=-1){
                byteArrayOutputStream.write(bytes,0,len);
            }
            // 获取.class文件的字节数组
            bytes1 = byteArrayOutputStream.toByteArray();
        } catch (IOException e) {
            throw new ClassNotFoundException(e.getMessage());
        } finally {
            try {
                if(fileInputStream!=null){
                    fileInputStream.close();
                }
                if(byteArrayOutputStream!=null){
                    byteArrayOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 调用defineClass加载该类
        return defineClass(name,bytes1,0,bytes1.length,null);
    }
}

六、java9有关类加载器的变化

  1. 在jdk9中移除了扩展类加载器,新增了平台类加载器代替原先扩展类加载器的位置。
  2. 启动类加载器新增了对应的java实现类BootClassLoader,但为了向前兼容在获取启动加载器时还是会返回null。
  3. 系统类加载器的实现有原先的sun.misc.launcher A p p C l a s s L o a d e r 换 成 了 j d k . i n t e r n a l . l o a d e r . C l a s s L o a d e r s AppClassLoader换成了jdk.internal.loader.ClassLoaders AppClassLoader换成了jdk.internal.loader.ClassLoadersAppClassLoader
  4. 移除了URLCLassLoader,所以之前版本自定义类加载器是如果直接继承了URLClassLoader的话在新的JDK9的环境下无法运行
  5. 双亲委派机制在,委派给父类之前,先根据包名判断该类是否能够确定归属与某个类加载器,如果定义这个归属关系则直接由归属类加载器加载。
    在这里插入图片描述
MATLAB算法实战应用案例精讲-【数模应用】装箱问题 装箱问题可以根据不同的维度进行分类。以下是一些常见的装箱问题二维装箱问题:二维装箱问题假设所有物品都是平面上的矩形,并且需要将它们放入平面上的容器中。这个问题的目标是找到一种装箱方案,使得物品不重叠并占据的总面积最小。三维装箱问题:三维装箱问题考虑到物品可以具有高度,并且需要将它们放入三维容器(如箱子或货柜)中。目标是找到一种装箱方案,使得物品不重叠且总体积最小。多维装箱问题:多维装箱问题是指在更高维度空间中进行装箱的问题。除了长度和宽度之外,物品可能具有其他属性,如高度、重量、形状等。 阅读详情

相关推荐

微信中调起支付宝支付

微信中调用支付宝支付,首先我们知道微信是不能直接调用支付宝的,所以我们需要使用一个中间页提示用户在浏览器中打开,然后进行网页支付或打开app支付。以下为详细唤起支付宝的步骤、业务代码以及相关页面图。 如有问题欢迎评论留言,看到定回复哦^_^\(^o^)/~ 一、微信中唤起支付宝,有几个步骤: 1、中间页引导用户在浏览器中打开去唤起支付宝。 2、浏览器中打开支付宝或app中打开支付宝。 3...

han_calligrapher的博客 2万+

Java中类的初始化及类加载器的介绍

要加载一个类.class,从低层级到高层级一级一级委派,先由应用层加载器委派给扩展类加载器,再由扩展类委派给启动类加载器;启动类加载器无法加载,再由扩展类加载器载入,扩展类加载器无法加载,最后由应用类加载器载入,如果应用类加载器也找不到,就会触发findclass,抛出classNotFoundException。1.通过委派的方式,可以避免类的重复加载,父加载器已经加载过某一个类时,子加载器就不会再重新加载这个类。Java类的初始化--虚拟机会保证这个类的 构造方法 线程安全,类其实就是懒加载。

m0_57640408的博客 517

昇腾Ascend310b部署deepseek-r1-7b的学习记录

9.鲲鹏服务器+昇腾卡(Atlas 300I pro)搭建DeepSeek-R1-Distill-Qwen-7B(自己存档详细版)5.如何使用昇腾Ascend 300I Pro 310P芯片 单卡运行DeepSeek-R1-Distilled-Qwen-7B。6.鲲鹏服务器+昇腾卡(Atlas 300I pro)搭建DeepSeek-R1-Distill-Qwen-7B。7.鲲鹏+昇腾部署DeepSeek-R1-Distill-Qwen-32B+Open-webui。

maken 2367

JVM - 类加载,连接和初始化

JVM - 类加载,连接和初始化

汤键的博客 704

对极几何 —两相机相对位置关系与相机姿态估计方法(基础矩阵,本征矩阵,单应矩阵梳理)附c++源码

对极几何 —两相机相对位置关系与相机姿态估计方法(基础矩阵,本质矩阵,单应矩阵梳理)附c++源码对极几何是什么,它有什么用(整体流程理解)对极约束(描述两相机关系)基础矩阵特性与求解方法本质矩阵特性与求解方法相机姿态恢复方法怎么又冒出了个单应矩阵H? 对极几何是什么,它有什么用(整体流程理解) 如图,当空间上有一个点分别在相机1和相机2上进行成像,这两个点存在什么变换关系呢?再扩展一下,真实世界下的场景分别用两个相机进行拍摄,获得两张照片,两张照片有什么变换关系呢?那么对极几何就是用来描述这种变换关系的。

qq_37764141的博客 5103

Xilinx FPGA平台GTX简易使用教程(汇总篇)

Xilinx平台GTX简易使用教程,先“知其然”,慢慢再研究“所以然”。

数字IC/FPGA杂货铺,找一找,或许有你想要的~ 3万+

Java三种默认类加载器_三种默认类加载器【AppClassLoader/ExtClassLoader/BootstrapClassLoader】...

那么JVM什么时候加载.class文件到内存中呢?当执行new操作时候,隐式调用类加载其加载对应的类到JVM中,该种方式为隐式装载当执行Class.forName(“包路径 + 类名”) Class.forName(“包路径 + 类名”, ClassLoader) ClassLoader.loadClass(“包路径 + 类名”),显式加载需要的类,该种方式为显式装载以上情况都会触发类加载器去类加...

weixin_35761085的博客 1681

搜索神器Perplexity的详细使用方法(持续更新)

本文主要介绍了ChatGPT的互补工具Perplexity的详细使用方法,希望对新手有所帮助。需要说明的是,Perplexity不需要上网工具,很方便新手和小白上手使用。Perplexity的官网链接为:https://www.perplexity.ai/ 。 文章目录 1. 为什么要讲Perplexity? 2. 详细使用方法 2.1 使用要点一 2.2 使用要点二 2.3 使用要点三

herosunly的博客 8万+

Java ClassLoader加载class过程

当我们尝试加载Hello类时,发现父类Object没有加载,所以会先加载父类(这也是class加载过程中的一个点)。由于defineClass是final的,所以如果你用自定义加载器也没法加载这样的类。由于我们没有遵循双亲委派,并且jvm内部内部已经加载的java/lang/Object的classloader也不是ClzLoaderSimp,所以没有找到对应父类。至于defineClass和剩下的resolve都是native方法,并且resolve不是在load的是否必须的。以下是我认为的一些要点。..

wuxiaopengnihao1的博客 1415

基于STM32的浴室环境智能控制系统设计

摘要:本文设计了一款基于STM32F103的智能卫浴系统,通过多传感器协同检测浴室环境参数。系统采用DS18B20检测环境与卫浴温度,DHT11监测湿度,MQ-135检测空气质量,光敏电阻测量光照强度,D203S人体热释电模块检测人员存在。当检测到异常参数时,系统自动启动相应调节功能:温度异常时控制加热/制冷继电器,湿度过高时启动通风除湿,空气质量差时进行换气,光照不足且有人时自动开灯。系统支持按键、蓝牙APP和语音三种控制方式,可手动设置参数阈值、切换防雾模式、控制音乐播放等功能。实验结果表明,该系统能有

3254968421的博客 482

【自适应滤波】自适应UKF,基于新息自适应调节Q和R,场景为雷达跟踪与滤波、MATLAB实现

本文提出了一种基于新息协方差匹配的自适应UKF(CM-AUKF)算法,用于解决噪声统计未知或时变场景下的目标跟踪问题。通过实时调整过程噪声协方差Q和观测噪声协方差R,CM-AUKF显著提升了跟踪精度和稳定性。仿真结果表明,相比传统UKF,CM-AUKF在位置和速度估计误差上分别降低了约50%和30%,且无需人工调参。该算法适用于无人机导航、雷达跟踪等动态环境,MATLAB代码可直接运行复现结果。自适应曲线显示算法能有效收敛到真实噪声参数,验证了其自适应能力。

985工科博士毕业,专攻定位、导航和滤波等算法研究。从业10年,主要使用MATLAB 329

JVM-类加载相关

加载阶段-->链接阶段(包含验证、准备、解析三个小阶段)-->初始化阶段类加载器子系统负责从文件系统或者网络中加载Class文件,任何语言只要生成的class文件符合规范,都可以在Java虚拟机上执行,加载的类信息会存放进方法区注意:ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine(执行引擎)决定Java中类加载是使用双亲委派机制进行加载的,我们也可以自定义自己的类加载器,但是一般不会打破双亲委派机制JVM支持两种类型的类加载器。分别为。

weixin_44165892的博客 979

VXWorks6.9 + Workbench3.3 开发环境部署

VxWorks6.9 + Workbench3.3的安装部署,以及简单示例项目的创建、编译和运行

Mr.Sugarcane 5452

Java_类加载器

对Java类加载器的讲解分析,包含程序段

AI+全栈,AI+应用,实操落地,前沿技术 2258

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

JVM类加载器

文章目录一、类加载器二、类与类加载器三、双亲委派模型四、破坏双亲委派模型4.1、Tomcat4.1.1、WebApp类加载器4.1.2、Shared类加载器4.1.3、Catalina类加载器4.1.4、Common类加载器4.1.5、Jsp类加载器4.2、JDBC 一、类加载器 从Java虚拟机的角度来讲,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),这个类加载器使用C++语言实现,是虚拟机自身的一部分;另一种就是所有其他的类加载器,这些类加载器都由Java语

rockvine的博客 3484

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

通过这一篇文章,可以把Java中的类加载器了解的七七八八了

对于每个开发人员来说,java.lang.ClassNotFoundExcetpion这个异常几乎都遇到过,而追求其该异常的来源的话,就免不了谈一谈Java的类加载器了。本文就基于启动类加载器、扩展类加载器、系统类加载器和自定义类加载器来为大家补充一下这方面的知识。 类加载器简介 Java程序被编译器编译之后成为字节码文件(.class文件),当程序需要某个类时,虚拟机便会将对应的class文件进行加载,创建出对应的Class对象。而这个将class文件加载到虚拟机内存的过程,便是类加载。 类加载器负责在

程序新视界 1308

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

java 类加载器有几种_一篇文章读懂Java类加载器

原标题:一篇文章读懂Java类加载器 Java类加载器算是一个老生常谈的问题,大多Java工程师也都对其中的知识点倒背如流,最近在看源码的时候发现有一些细节的地方理解还是比较模糊,正好写一篇文章梳理一下。关于Java类加载器的知识,网上一搜一大片,我自己也看过很多文档,博客。资料虽然很多,但还是希望通过本文尽量写出一些自己的理解,自己的东西。如果只是重复别人写的内容那就失去写作的意义了。 类加载器...

weixin_36205186的博客 224

解读 JVM 类加载器-一篇文章简单易懂

首先来看一张图 1.引导类加载器(bootstrap class loader): 它用来加载 Java 的核心库(jre/lib/rt.jar),是用原生C++代码来实现的,并不继承自java.lang.ClassLoader。加载扩展类和应用程序类加载器,并指定他们的父类加载器,在java中获取不到。 2.扩展类加载器(extensions class loader): 它用来加载 Java 的扩展库(jre/ext/*.jar)。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找

凌波漫步 1831

java类加载器 架构 设计_一篇文章读懂Java类加载器

Java类加载器算是一个老生常谈的问题,大多Java工程师也都对其中的知识点倒背如流,最近在看源码的时候发现有一些细节的地方理解还是比较模糊,正好写一篇文章梳理一下。关于Java类加载器的知识,网上一搜一大片,我自己也看过很多文档,博客。资料虽然很多,但还是希望通过本文尽量写出一些自己的理解,自己的东西。如果只是重复别人写的内容那就失去写作的意义了。类加载器结构类加载器结构名称解释:根类加载器,也...

weixin_32540087的博客 175

一篇图文彻底弄懂类加载器与双亲委派机制

无论你是跟同事、同学、上下级、同行、或者面试官讨论技术问题的时候,很容易卷入JVM大型撕逼现场。为了能够让大家从大型撕逼现场中脱颖而出,最近我苦思冥想如何把知识点尽可能呈现的容易理解,方便记忆。于是就开启了这一系列文章的编写。为了让JVM相关知识点能够形成一个体系,arthinking将编写整理一系列的专题,以尽量以图片的方式描述相关知识点,并且最终把所有相关知识点串成了一张图。持续更新中,欢迎...

peng_zhanxuan的技术博客 1707

什么是类加载器,类加载器有哪些

类加载器(Class Loader)是Java虚拟机(JVM)的一部分,负责将Java类的二进制代码加载到内存中,并转换为可执行的Java字节码。它是Java语言的重要特性之一,为Java应用程序提供了动态加载和运行时扩展的能力。类加载器的主要职责是根据类的全限定名(Fully Qualified Name)在运行时定位并读取类的字节码。类加载器使用一种称为双亲委派模型(Parent Delegation Model)的机制来加载类。

Flying_Fish_roe的博客 2292

JVM类加载器详解

前言 在上一篇中,通过下面这幅图大致了JVM整体的内部运行结构图,在JVM的结构中,类加载子系统作为连接外部class文件与真正将class文件加载到运行时数据区,承担着重要的作用 类加载器是什么?有什么作用? 1、负责从文件或者网络加载class文件字节流 2、读取字节码中的信息,并在运行时存储到JVM的内存区域 3、检查并确保加载的class文件信息符合JVM字节码规范 ...

congge 6356

类加载器

类加载器(ClassLoader)是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术,类加载器只参与加载过程中的字节码获取并加载到内存这一部分。 类加载器虽然只用于实现类的加载动作,但它在Java程序中起到的作用却远超类加载阶段。对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。这句话可以表达得更通俗一些:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来

m0_62943934的博客 1218

了解类加载器吗?类加载器的类型有哪些?

类加载器(ClassLoader)是JVM的核心组件,负责将.class文件加载到内存并生成Class对象。JVM内置三类加载器:Bootstrap(核心类库)、Extension(扩展类)、Application(应用类),支持自定义类加载器。类加载采用双亲委派机制,优先委派父加载器处理,确保核心类安全并避免重复加载。自定义类加载器通过继承ClassLoader实现,常用于热部署、插件系统等场景。线程上下文类加载器(如SPI)可突破双亲委派限制。理解类加载机制对Java模块化开发和安全部署至关重要。

笑衬人心的博客 1537

类加载器的作用

深入探讨 Java 类加载器 成 富, 软件工程师, IBM 中国软件开发中心 成富任职于 IBM 中国软件开发中心,目前在 Lotus 部门从事 IBM Mashup Center 的开发工作。他毕业于北京大学信息科学技术学院,获得计算机软件与理论专业硕士学位。他的个人网站是 http://www.cheng-fu.com。 简介: 类加载器(class loader)

zhouren1314的专栏 1943

类加载器详解

类加载器从 JDK 1.0 就出现了,最初只是为了满足 Java Applet(已经被淘汰) 的需要。后来,慢慢成为 Java 程序中的一个重要组成部分,赋予了 Java 类可以被动态加载到 JVM 中并执行的能力。根据官方 API 文档的介绍:类加载器是一个负责加载类的对象。是一个抽象类。给定类的二进制名称,类加载器应尝试定位或生成构成类定义的数据。典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件”。每个 Java 类都有一个引用指向加载它的。不过,数组类不是通过。

m0_74267125的博客 818
上一篇: java类加载子系统之类的卸载
下一篇: java的SPI机制(jdk1.8)
躺平程序猿
博客等级 码龄8年 4870粉丝 71原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

躺平程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值