一道面试题

你能自己写一个叫java.lang.System的班吗?

答:通常不会,但你可以用别的方法满足这个需求。

说明:为了避免我们写系统类,班级加载采用委托机制,保证爸爸们优先,爸爸们能找到的班级,儿子没有机会加载。 另一方面,System类由Bootstrap加载程序加载,即使您自己重写,也总是使用Java系统提供的System,而您编写的System类没有机会加载。

但是,我们可以自己定义类加载器来实现这个目的。 为了避免父母委托机制,这个类加载器也必须是特殊的。 系统中的三个类加载器加载特定目录下的类,因此如果将自己的类加载器放在特殊目录中,则无法加载系统加载器。 也就是说,最终将由自己的加载器加载。

双亲委派模式优势

避免重复加载 + 避免核心类篡改

采用父母代理模式的好处是,Java类与类加载器具有优先级层次关系。 这种层次关系可以避免类的重复加载。 如果父亲已经加载了类,则子类加载器不需要再加载一次。 其次,考虑到安全性,java核心api中定义的类型不会被任意替换。 假设通过网络传递一个名为java.lang.Integer的类,并在父母委托的模式下传递给启动类加载器,而启动类加载器位于核心java中

API发现具有此名称的类,发现该类已加载,不重新加载网络传递的java.lang.Integer,而是直接返回加载的Integer.class,从而使核心API库获胜

JVM预定义的三种类型类加载器:

启动(Bootstrap )类加载器)是用本地代码实现的类加载器,负责

Java_Runtime_Home/lib下的类库将加载到rt.jar等内存中。 由于引导类加载器包含虚拟机的本地实现详细信息,因此开发人员无法直接从引用中操作,因为无法直接获取对引导类加载器的引用。

标准扩展类加载器Sun的

由ext class loader (sun.misc.launcher $ ext class loader )实现。 它负责

Java_Runtime_Home /lib/ext或系统变量

位于java.ext.dir指定位置的类库将加载到内存中。 开发人员可以直接使用标准的扩展类加载器。

系统(系统)类加载器: Sun的

app class loader (sun.misc.launcher $ app class loader )实现了。 将“系统类别路径”(CLASSPATH )指定的类库加载到内存中。 开发人员可以直接使用系统类加载器。

双亲委派模型工作工程:

当Application ClassLoader收到加载类的请求时,他不会首先尝试自己加载该类,而是将该请求委托给父类的加载器Extension ClassLoader完成。

当Extension ClassLoader收到加载类的请求时,他不会首先尝试自己加载类,而是将请求委托给父类的加载器Bootstrap

类加载器完成。

如果Bootstrap ClassLoader加载失败(如果在JAVA_HOME\lib中找不到所需的类),请让扩展类loader尝试加载。

如果扩展类加载器也加载失败,它将使用应用程序类加载器加载。

如果应用程序类加载器也加载失败,则尝试使用自定义加载器进行加载。

这就涉及到了类的具体加载过程, 如下图, 类的加载过程被从左到右划分为 3 大阶段

1 .加载

在此阶段,找到要加载的类的二进制class文件,并将其作为bytecode加载到虚拟机中。 在此过程中,JVM为此类分配了基本的内存结构,但方法、变量域和引用的其他类在此阶段尚未处理。 也就是说,这个类在当前阶段不可用

2 .链接(链接) )。

这个步骤还可以细分为三个阶段

字节码验证

确保字节码正确,并准备了符合规范的类字节码类

定义此类所需的数据结构,以表示成员变量域、方法和实现的接口等分析

加载此类锁引用的所有其他类后,将按以下方式进行引用: 3 .初始化(Initializing ),继承由实现接口域变量的方法定义方法定义的局部变量。

缺省情况下,执行类中定义的静态代码块并初始化静态变量

隐式加载vs显示加载

从上面类加载的详细步骤中可以看出,类以两种方式加载

显式加载

程序主动调用以下类型的方法:类class loader.load class (classname ) class name )隐式加载

显式加载的类可能被其他类引用如下: 由实现接口域变量的方法定义方法定义的局部变量被引用的类会被动地一并加载至虚拟机, 这种加载方式属于隐式加载原文: https://blog.csdn.net/leng Xiao 1993/article/details