Java类加载机制-笔记3(类加载器的分类)

在Java类生命周期中,只有加载步骤中的读取二进制流与初始化部分,能够被上层开发者,也就是大部分的Java程序员控制。而剩下的所有步骤,都是由JVM掌控,其中细节由JVM的开发人员处理,对上层开发者来说是个黑盒。
为什么要这么做呢?
这是一种面向对象中开闭原则和封装思想的设计。JVM 将类加载内部复杂的实现封装了起来,拒绝上层开发者修改,只提供了一个扩展接口,用于class文件二进制流的读取。而就是这么一点点的活动空间,也成了上层开发者施展拳脚的舞台,比如说基于该特性实现的 动态代理、热部署等等功能。

Java的类加载器

类加载器的分类属于JVM规范,是一种抽象的概念,各个不同的JVM实现的方式是不一定一样的,JVM规范中类加载器分为两大类

  • 启动类加载器
  • 非启动类加载器
以HotSpot虚拟机实现为例

在HotSpot中,Bootstrap ClassLoader是采用C/C++来实现的,是嵌套在了JVM内部,无法作为对象来被程序所引用,它主要用来加载Java的核心类库,比如说 <JAVA_HOME>/lib 路径下的jar包,或者是由启动参数来指定路径下的核心类库,此外,为了安全性Bootstrap ClassLoader只加载了包名在白名单中的文件,比如说由java,javax,sun开头的包,非Bootstrap ClassLoader又分为三类。分别为:

  • Extension ClassLoader
  • Application ClassLoader
  • User ClassLoader

在 HotSpot中,非Bootstrap ClassLoader都采用java来实现,他们都继承自 java.lang.ClassLoader 这个类,他们可以作为对象被引用,那么接下来我们来介绍这三种类加载器的区别:

  • Extension ClassLoader 是由Launcher 的内部类ExtClassLoader来实现的,主要来加载<JAVA_HOME>/lib/ext 目录下,或者是由系统变量指定的路径中的类库。Extension ClassLoader希望加载的是javaApi的扩展,是对java类库的一些补充能力。
  • Application ClassLoader 是由Launcher 的内部类AppClassLoader来实现的,主要来加载环境变脸classpath或者是系统属性指定路径下的类库,Application ClassLoader 希望加载的是上层程序员编写的代码以及一些第三方的类库,我们平时编写的代码基本上都是由Application ClassLoader 来加载的。

问题:我们可不可以用Extension ClassLoader 来加载我们自己写的代码呢?

可以这么做,但是完全没有必要,这种操作不符合规范,工程项目是讲究分层和抽象的,就好像你可以把一个项目的代码都写在一个文件里,但是是不推荐这么做的。

  • User ClassLoader 用户自己编写的类加载器,上面提及到的几种类加载器,都只能从本地文件中获取字节码来进行加载。而User ClassLoader可以让用户能够获取任何来源的字节码,并对他们进行加载,这就印证了在java类加载机制中允许用户从各个渠道获取class文件的二进制流来进行加载的结论。如果要编写一个User ClassLoader,其实非常简单,我们前面说到在HotSpot中,非Bootstrap ClassLoader都是采用java来实现的,他们都继承自java.lang.ClassLoader这个类,用户自定义的类加载器,其实只需要继承java.lang.ClassLoader ,然后单独实现获取二进制流的逻辑,而后续的步骤必须让java.lang.ClassLoader作为内置的逻辑来处理,用户无权进行重写和干涉。

问题:

  1. 不同的类加载器,除了读取二进制流的动作和范围不一样,后续的加载器逻辑是否也不一样?
  2. 遇到限定名一样的类,这么多类加载器会不会产生混乱?

JVM规范中,规定:每个类加载器都有属于自己的命名空间
即使你用不同的类加载器加载了同一个限定名的类,那么JVM也会认为这是两个不同的类,深入理解JVM这本书中,有这样的一个例子:

image.png
public class Main {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        ClassLoader myLoader = new ClassLoader() {
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                try {
                    String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                    InputStream is = getClass().getResourceAsStream(fileName);
                    if (is == null) {
                        return super.loadClass(name);
                    }
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name, b, 0, b.length);
                } catch (IOException e) {
                    e.printStackTrace();
                    throw new ClassNotFoundException(name);
                }
            }
        };
        Object obj = myLoader.loadClass("com.example.demo0413.test.A").newInstance();
        System.out.println(obj.getClass());
        System.out.println(obj instanceof A);
    }
}

可以看到我们首先使用匿名类的方式实现了一个User ClassLoader ,我们之前所说的,在HotSpot中所有非Bootstrap ClassLoader 都是java.lang.ClassLoader的子类,那么我们这里就重写了它的loadClass 方法,loadClass方法的内容呢就是直接从文件中读取class文件,然后调用父类的defineClass来进行后续的加载操作,最后使用加载输出的 class 来进行对象的实例化,并且对产生的对象进行类型判断,我们可以看到对象的类限定名是一样的,但是instanceof 的结果却输出了false,看上去很奇怪,这是因为我们自定义的这个类分别被默认的Application ClassLoader 和我们自定义的User ClassLoader加载了,在最后的判断中,JVM认为这是两个完全不同的Class,这就印证了我们上面提到的每个类加载器都属于自己的命名空间这一点。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容