亲宝软件园·资讯

展开

Java类的加载过程与ClassLoader的理解及测试

"H" 人气:2

先了解下在程序准备运行某个类,但是该类还没被加载到内存中,会经过以下三个步骤:

类的加载(Load)→类的连接(Link)→类的初始化(Initialize)

  • 加载:类经过javac.exe编译的.class字节码文件读入内存(将静态数据转换成堆中方法区的运行时数据结构),并为之创建一个java.lang.Class对象作为方法区中类数据的访问入口(引用的地址),需要访问和使用类数据只能通过这个Class对象;此过程由类的加载器完成;
  • 链接:将java类的二进制代码合并到JVM的运行状态中的过程;
    • 验证:确保加载的类符合JVM规范;
    • 准备:正式为类变量(static)分配内存并设置变量默认初始值(非任何显示赋值),这些内存都在方法区中分配;
    • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
  • 初始化:JVM负责对类进行初始化;
    • 执行类构造器
    • 如其父类为进行初始化,则初始化操作从先从父类进行;
    • 虚拟机会保证一个类的

      类加载器ClassLoader的作用:

      除了上面提到的作用,还有一个类缓存机制:一旦某个类被加载到内存中,将位置加载(缓存)一段时间,相当于一个缓存了一个Class对象,无论此类创建多少个实例,都是从这唯一的结构中获取信息;GC也可以回收这些Class对象;
      JVM规范定义的类的加载器类型如下:

加载器关系测试:

@Test
    public void test1() {
        //1.获取一个系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        //2.获取系统类加载器的父类加载器,即扩展类加载器
        ClassLoader extensionClassLoader = systemClassLoader.getParent();
        System.out.println(extensionClassLoader);
        //3.获取扩展类加载器的父类加载器,即引导类加载器
        ClassLoader bootstapClassLoader = extensionClassLoader.getParent();
        //引导类加载器用于加载java核心库,无法直接获取,故输出null
        System.out.println(bootstapClassLoader);
        //4.测试当前类由哪个类加载器进行加载
        ClassLoader classLoader = null;
        try {
            classLoader = Class.forName("Reflection.ClassLoaderTest").getClassLoader();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(classLoader);//结果为系统类加载器
        //5.测试JDK提供的Object类由哪个类加载器完成
        ClassLoader objClassLoader = null;
        try {
            objClassLoader = Class.forName("java.lang.Object").getClassLoader();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(objClassLoader);//结果为null(说明是用的引导类加载器,我们无法获取)
        //6.关于类加载器的一个主要方法:getResourceAsStream(String str):获取路径下的指定文件的输入流
        InputStream is = null;
        is = this.getClass().getClassLoader().getResourceAsStream("Reflection\\test.properties");
        System.out.println(is);
        //可用于读取配置文件,下面单独拿来测试
    }

读取.properties配置文件:

    @Test
    public void test2(){
        Properties properties = new Properties();//表示一个持久的属性集,可保存在流中或从流中加载
//        //1.获取输入流
//        //方式一:(此时的文件默认路径在Module下)
//        FileInputStream fis = null;
//        try {
//            fis = new FileInputStream("test.properties");
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        }
        //方式二:使用ClassLoader方式(此时的文件默认路径在当前Module的src下)
        //获取当前类的Class实例对象-获取类加载器-获取指定指定路径下的文件输入流
        InputStream is = this.getClass().getClassLoader().getResourceAsStream("test1.properties");

        //2.读取配置文件
        try {
            //从输入流中读取属性列表(键和元素对)
            properties.load(is);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //匹配对应key的属性,获取key对应的元素值
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");
        System.out.println("user = " + user + " , password = " + password);
    }

加载全部内容

相关教程
猜你喜欢
用户评论