转载

Java基础之类加载器

概述

类加载器:

负责将.class文件加载到内存中,并为之生成对应的Class对象,也就是字节码文件对象。

问题:我们平时书写在eclipse中的Java程序是如何运行的呢?

1)首先将 .java 源文件编译为class类文件;

2)编译后的类文件是存在硬盘中的,那么我们运行需要在内存中看到效果,那么类文件是如何被加载到内存中的呢,就是jvm通过类加载器ClassLoader把硬盘中的class文件加载到内存中,这样就可以使用这个类中的成员变量和方法了。而被加载到内存中这个class文件就会变成一个Class类的对象。

常见的类加载器有三种,每个类加载器负责加载不同位置的类:

Java基础之类加载器

1)Bootstrap 根类加载器;

2)ExtClassLoader 扩展类加载器;

3)AppClassLoader 系统/应用类加载器;

那么这三种类加载器各有什么作用或者有什么区别呢?

他们三个加载的范围是不一样的。如下图所示:

Java基础之类加载器

说明:

1)Bootstrap是最顶级的类加载器。它加载类文件不是我们自己书写的,负责Java核心类的,比如System,String等。只有所有类加载到内存中,我们才可以使用。

2)ExtClassLoader 扩展类加载器,加载的是扩展类的,我们是用不到的,都是jdk内部自己使用的。

3)AppClassLoader 系统/应用类加载器,是用来加载ClassPath 指定的所有jar或目录,ClassPath表示存放类路径的,我们如果不配置ClassPath,那么就表示当前文件夹,在idea环境下的ClassPath是out目录。在out目录存放的都是我们书写好的class文件,也就是说 AppClassLoader 类加载器是用来加载我们书写的out目录下的class文件。

需求:演示类加载器的父子关系。

代码演示如下所示:

分析:如何获取一个类的类加载器呢?

如果想获得当前类的加载器,那么首先必须获得当前类的字节码文件对象,而这个字节码文件对象属于Class类型,我们可以使用 Class类中的getClassLoader()函数来获得类加载器:

ClassLoadergetClassLoader() 返回该类的类加载器

AppClassLoader:加载classPath中的所有的类,也就是我们自己写的那些类!

注意:类加载器,也是一个类,也需要被加载。一般类加载器都是被父类加载器加载的!

获取父类加载器的方法:使用ClassLoader 类中的getParent()返回委托的父类加载器 。

说明:AppClassLoader是被ExtClassLoader加载的!

ExtClassLoader肯定也是一个类,需要被父加载,它的父亲是BootStrap。

那么问题来了:如果这个类加载器也需要被人加载,那么就没有尽头了!因此,BootStrap是不需要被加载的。

因为它不是一个Java类。它是用C++实现的一段代码。

也就是说,jvm虚拟机一启动就会运行C++实现的这段代码,那么BootStrap类一旦被启动就会开始加载他下面的子类了。

注意:最顶级的类加载器不是Java类,而是C++实现的代码。

/*

* 演示类加载器的父子关系:

* 获取一个类加载器:使用Class类中的函数:ClassLoadergetClassLoader() 返回该类的类加载器

* 获取Class类的对象 :类名.class

* AppClassLoader:加载classPath中的所有的类,也就是我们自己写的那些类!

* 类加载器,也是一个类,也需要被加载。一般类加载器都是被父类加载器加载的!

* 获取父类加载器的方法:使用ClassLoader类中的函数:

*      ClassLoadergetParent()返回委托的父类加载器

*

* AppClassLoader类加载器是被ExtClassLoader类加载器加载的!

*

* ExtClassLoader肯定也是一个类,需要被父类加载器加载,它的父类是BootStrap类加载器。

* 而ExtClassLoader确实被他的父类BootStrap类加载器加载的,

* 那么问题来了:如果BootStrap类加载器也需要被人加载,那么就没有尽头了!因此,BootStrap类加载器是不需要被加载的。

* 因为它不是一个Java类。它是用C++语言实现的一段代码。

* 所以这里获取不到BootStrap类加载器,就是因为他是一段C++代码实现的。

*/

public class ClassLoaderDemo1 {

public static void main(String[] args) {

// 获取当前类的加载器

ClassLoader loader = ClassLoaderDemo1.class.getClassLoader();

//输出当前类的类加载器

System.out.println(loader);//sun.misc.Launcher$AppClassLoader@b0014f0

//获取AppClassLoader类加载器的父类

ClassLoader parent = loader.getParent();

//输出AppClassLoader类加载器的父类加载器

System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@325e9e34

//获取ExtClassLoader类加载器的父类

ClassLoader grandpa = parent.getParent();

//输出ExtClassLoader类加载器的父类加载器

System.out.println(grandpa);//null

}

}

注意:

通过上述学习我们发现三种类加载器都有自己要加载的类文件,各司其职,不能乱加载,比如Bootstrap类加载器只能加载sun公司定义好的类,他就不能加载自定义的类文件。所有的自定义类文件都是由AppClassLoader类加载器加载。

使用类加载器加载配置文件的方式

需求:用类加载器加载配置文件。

代码步骤:

1)新建一个类ClassLoaderDemo2 ,并书写main函数;

2)在当前项目下新建一个stu.ini文件,并在其中输入:

name=zhangsan

age=19

3)创建Properties 类的集合对象p;

4)创建字节输入流FileInputStream对象关联项目根目录文件stu.ini;

5)使用集合对象p调用load函数加载配置文件中的内容,并使用输出语句输出内容即可;

使用之前的FileInputStream方式加载配置文件方式加载文件内容:

//使用之前的加载配置文件方式加载文件内容

public void method_1() throws Exception{

// 创建Properties类的对象

Properties p = new Properties();

//调用函数加载配置文件中内容

p.load(new FileInputStream("stu.ini"));

//输出内容

System.out.println(p);

}

说明:直接使用FileInputStream的相对路径,是相对于项目。

注意:我们在开发中配置文件stu.ini不仅可以存放到项目的根目录,有时我们还会存放到src下面:

Java基础之类加载器

这样我们使用new FileInputStream("stu.ini")在读取就会出现如下问题:

错误原因:new FileInputStream("stu.ini")是去项目的根目录去查找stu.ini文件,而由于我们已经将该文件移动到src下面,所以会报找不到指定文件的异常。

既然使用之前的加载方式加载src下面的配置文件有问题,那么我们可以使用新的加载方式,就是用类加载器加载。

分析步骤:

1)使用当前类ClassLoaderDemo2获得Class对象并调用Class类中的getClassLoader()函数:

ClassLoaderloader = ClassLoaderDemo2. class .getClassLoader();

2)使用类加载器对象loader 调用ClassLoader 类中的InputStream getResourceAsStream(String name)返回读取指定资源的输入流

InputStream in =loader.getResourceAsStream("stu.ini");

​ 说明:这里的name是文件的路径:这个路径如果使用相对路径,相对的是src目录。

代码演示如下所示:

//使用之前的加载配置文件方式加载文件内容

public void method_1() throws Exception{

// 创建Properties类的对象

Properties p = new Properties();

//获取当前类的加载器

ClassLoader loader = ClassLoaderDemo2.class.getClassLoader();

//使用类加载器对象调用函数获取加载配置文件的字节输入流

InputStream in = loader.getResourceAsStream("stu.ini");

//调用函数加载配置文件中内容

//      p.load(new FileInputStream("stu.ini"));

p.load(in);

//输出内容

System.out.println(p);

}

总结:这两种加载文件的区别在于加载的相对位置不一样,第一种方式相对的是当前项目,而第二种方式相对的是src。

原文  https://segmentfault.com/a/1190000020766811
正文到此结束
Loading...