java使用类加载的好处_Java类加载机制(简要总结)

本文原标题是总结,但是呢,看了一下深入理解JVM内容后,还是too young too simple,改名为简要总结。

此文总结一下Java的类加载机制。

概述

总所周知,Java是跨平台的一种语言,究其原因是JVM虚拟机的跨平台。因此不论是何种语言,只要能编译成class字节码,就可以在JVM中运行。因此可以这样说,Java是跨平台的语言,而JVM是跨语言的平台。那么,就会有一个问题,JVM是如何加载class文件的呢(这里的class文件可以保存在任何地方,数据库,网络,内存等)?今天就来总结一下Java的类加载机制。

类的生命周期

简单总结一下类的生命周期,非常简单的总结,一眼见到底的那种

【图不会上传】

如图所示,class从加载到虚拟机开始到卸除内存位置,其生命周期包括加载加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)七个阶段,这其中验证、准备、解析统称为连接。

加载

将class字节码加载进内存

连接

验证

验证加载class字节码是否符合要求,比如是否语法错误,格式是否正确,有没有错误的重写父类的final方法等一系列复杂的验证

准备

这个阶段是为static变量赋默认值,直接给final常量赋最终值,也就是指定的值

解析

将符号引用转换为直接引用,直接引用就是指向一个内存地址

初始化

为static变量赋指定的值,loadClass不会触发初始化,classForName如果指定不初始化也不会进行初始化

一般造成初始化的操作

使用new关键字了,这时候肯定要进行初始化的嘛,因为都已经在使用中了

子类对象实例化 这个也很好理解,就是子类实例创建的时候肯定得先找到父类吧,要不怎么初始化,因为它包含父类的一些信息

使用该类的静态变量 看看初始化干了啥(当然,不仅仅是这些,还有其他操作),不就是将静态变量初始了

使用

正常使用,用来创建对象啥的

卸载

一般不卸载,除非该类不存任何实例

加载该类的ClassLoader已经被回收。

该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。

类加载器

概述(深入理解JVM原话):

Java虚拟机设计团队有意把类加载阶段中的“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需的类。实现这个动作的代码被称为“类加载器”(Class Loader)。

类加载器可以说是Java语言的一项创新,它是早期Java语言能够快速流行的重要原因之一。类加载器最初是为了满足Java Applet的需求而设计出来的,在今天用在浏览器上的Java Applet技术基本上已经被淘汰[1],但类加载器却在类层次划分、OSGi、程序热部署、代码加密等领域大放异彩,成为Java技术体系中一块重要的基石,可谓是失之桑榆,收之东隅。

其实我理解的就是加载类的一种东西或者说是工具,这说的也很明显-类加载器,意思就是加载类的工具

类与类加载器

对于任意的类,其在内存中是由这个本身以及加载他的类加载器组成。这里有个值得注意的的点,如果两个类来源于同一个class文件,但是

呢,不是由同一个类加载器加载,那么两个类就不相等

这里所指的“相等”,包括代表类的Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果,也包括了使用instanceof关键字做对象所属关系判定等各种情况。如果没有注意到类加载器的影响,在某些情况下可能会产生具有迷惑性的结果,代码清单7-8中演示了不同的类加载器对instanceof关键字运算的结果的影响。

双亲委派模型

【图不会上传】

如上图,Java中一共有三层类加载器,分别是启动类加载器,扩展类加载器以及应用类加载器,还有最后的自定义类加载器

启动类加载器负责加载\lib目录或者是被-Xbootclasspath参数所指定的路径中。而且可以是Java虚拟机能够识别的类库加载到虚拟机中。启动类加载器用户无法直接应用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器去处理,那直接使用null代替即可,代码清单7-9展示的就是java.lang.ClassLoader.getClassLoader()方法的代码片段,其中的注释和代码实现都明确地说明了以null值来代表引导类加载器的约定规则。

扩展类加载器(Extension Class Loader):这个类加载器是在类sun.misc.Launcher$ExtClassLoader中以Java代码的形式实现的。它负责加载\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中所有的类库。根据“扩展类加载器”这个名称,就可以推断出这是一种Java系统类库的扩展机制,JDK的开发团队允许用户将具有通用性的类库放置在ext目录里以扩展Java SE的功能,在JDK 9之后,这种扩展机制被模块化带来的天然的扩展能力所取代。由于扩展类加载器是由Java代码实现的,开发者可以直接在程序中使用扩展类加载器来加载Class文件。

应用程序类加载器(Application Class Loader):这个类加载器由sun.misc.Launcher$AppClassLoader来实现。由于应用程序类加载器是ClassLoader类中的getSystem-ClassLoader()方法的返回值,所以有些场合中也称它为“系统类加载器”。它负责加载用户类路径(ClassPath)上所有的类库,开发者同样可以直接在代码中使用这个类加载器。如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

自定义类加载器,只需要重写ClassLoader里的findClass()方法就可以了,这个可以实现class的加解密

源码展示:

protected synchronized Class> loadClass(String name, boolean resolve) throws ClassNotFoundException

{

// 首先,检查请求的类是否已经被加载过了

Class c = findLoadedClass(name);

if (c == null) {

try {

if (parent != null) {

c = parent.loadClass(name, false);

} else {

c = findBootstrapClassOrNull(name);

}

} catch (ClassNotFoundException e) {

// 如果父类加载器抛出ClassNotFoundException

// 说明父类加载器无法完成加载请求

}

if (c == null) {

// 在父类加载器无法加载时

// 再调用本身的findClass方法来进行类加载

c = findClass(name);

}

}

if (resolve) {

resolveClass(c);

}

return c;

}

类加载器的调用过程

先看一下缓存中有没有,也就是有没有加载过,要是加载过就直接返回了。如果没有就给父加载器,(这里的父加载器的意思不是继承二十合成复用,在类加载器内部里有个成员变量指向了父加载器)如果父类没有找到再给子类去加载。这种加载机制有点责任链的味道

双亲委派模型的好处

避免存在多个相同的类,例如类java.lang.Object,它存放在rt.jar之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都能够保证是同一个类。反之,如果没有使用双亲委派模型,都由各个类加载器自行去加载的话,如果用户自己也编写了一个名为java.lang.Object的类,并放在程序的ClassPath中,那系统中就会出现多个不同的Object类,Java类型体系中最基础的行为也就无从保证,应用程序将会变得一片混乱。

也就是使系统更安全

破坏双亲委派模型

有时候吧,双亲委派模型也不怎么好,比如tomcat,作为一个web容器,肯定可以部署很多的web项目,这时候呢就会有不同版本的jar包被引用,这个时候呢,就需要进行破坏了要不然只有一个类被加载了.

破坏双亲委派模型很简单,只需要重写loadClass()方法,因为双亲委派就在这里实现。

至此,总结完毕

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值