Java实现类加载机制简介

类的加载机制

两次运行的Java程序为什么不能共享数据?

当我们调用Java命令运行某个Java程序时,该命令将会启动一条Java虚拟机进程,不管该Java程序有多么复杂,该程序启动了多少个线程,它们都处于该Java虚拟机进程里。

同一个JVM的所有线程、所有变量都处于同一个进程里,它们都使用该JVM进程的内存区。

两次运行Java程序处于两个不同的JVM进程中,两个JVM之间并不会共享数据。

类加载机制

当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载、连接、初始化三个步骤来对该类进行初始化,如果没有意外,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或类初始化。

类加载

类加载指的是将类的class文件读入内存,并为之创建一一个 java.lang.Class对象,也就是说当程序中使用任何类时,系统都会为之建立-一个java.lang.Class对象

类是某一类对象的抽象,是概念层次的东西。

类的连接

当类被加载之后,系统为之生成一-个对应的Class对象,接着将会进入连接阶段,连接阶段将会负责把类的二进制数据合并到JRE中。类连接又可分为如下三个阶段:
验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一-致。
准备:类准备阶段则负责为类的静态属性分配内存,并设置默认初始值。.
解析:将类的二进制数据中的符号引用替换成直接引用。

类的初始化

在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对静态属性进行初始化。

在Java类中对静态属性指定初始值有两种方式:

(1)声明静态属性时指定初始值;

(2)使用静态初始化块为静态属性指定初始值。

类的初始化时机(什么时候会进行类的初始化)?

当Java程序首次通过下面6种方式来使用某个类或接口时,系统就会初始化该类或接口:
创建类的实例。为某个类创建实例的方式包括使用new操作符来创建实例,通过反射来创建实例,通过反序列化的方式来创建实例。
调用某个类的静态方法。
访问某个类或接 口的静态属性,或为该静态属性赋值。
使用反射方式来强制创建某个类或接口对应的ja.lang.Class对象。例如代码:Class.forName(“Person”),如果系统还未初始化Person类,则这行代码将会导致该Person类被初始化,并返回Person对应的java.lang.Class对象。
初始化某个类的子类, 当初始化某个类的子类时,该子类的所有父类都会被初始化。
直接使用 java.exe命令来运行某个主类,当运行某个主类时,程序会先初始化该主类。

除此之外,下面有几种情形需要特别指出:
对于一个final型的静态属性,如果该属性可以在编译时就得到属性值,则可认为该属性可被当成编译时常量。当程序使用编译时常量时,系统会认为这是对该类的被动使用,所以不会导致该类的初始化。

使用final修饰符修饰的成员变量,在类调用时,是否初始化此类?

当某个静态属性使用final修饰,而且它的值可以在编译时得到,那么程序其他地方使用该静态属性时,实际上并不会使用该静态属性,而是相当于使用常量。

例如:

static final String str = "Java学习";

str的值在编译器就确定是"Java学习",所以使用该静态属性,不会初始化此类。

反之,如果final类型的静态属性的值不能在编译时得到,必须等到运行时才可以确定该属性的值**,如果通过该类来访问该静态属性,则可以认为是主动访问使用该类,将会导致该类被初始化。

例如:

static final String str = "现在的系统时间是:" + System.currentTimeMillis();

str的值在编译期无法确定,因为要在运行时获取当前系统时间,所以使用该静态属性,会初始化此类。

注意:

当使用ClassLoader类loadClass()方法来加载某个类时,该方法只是加载该类,并不会执行该类的初始化。当使用ClassforName()静态方法才会导致强制初始化该类

图解类加载过程

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。

在这里插入图片描述

类加载器

类装载器负责加载所有的类,系统为所有被载入内存中的类生成一个java.lang.Class 实例。

当JVM启动时,会形成由三个类加载器组成的初始类加载器层次结构:
Bootstrap Classl oader:根类(引导或原始)加载器。它负责加载Java的核心类。根类加载器非常特殊,它并不是java.lang.ClassLoader的子类,而是由JVM自身实现的。
Extension ClassL oader:扩展类加载器。它负贵加载JRE的扩展月录(JAVA_ HOME/jre/lib/ext或者山java.ext.dirs 系统属性指定的几录)中JAR的类包。通过这种方式,我们就可以为Java护展核心类以外的新功能,只要我们把自己开发的类打包成JAR文件,然后放入JAVA_ HOME/jre/lib/ext 路径即可。
System ClassLoader:系统类加载器。它负责在JVM启动时,加载来自命令java中的Classpath选项或java.class path系统属性,或CLASSPATH环境变量所指定的JAR包和类路径。

程序可以通过ClassLoader静 态方法getSystemClassLoader()获取 该类加载器。如果没有特别指定,则用户自定义的类加载器都以该类加载器作为它的父加载器。

思考:一个类被载入JVM中,同一个类就不会被再次载入了。怎么样才算“同一个类”?

在JVM中,一个类用其全限定类名和其类加载器作为其唯一标识。

类加载器的加载机制

JVM的类加载机制主要有如下三种机制:

  1. 全盘负责:所谓全盘负责,就是说当一个类加载器负责加载某个Class的时候,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显式使用另外一个类加载器来载入。
  2. 父类委托:所谓父类委托则是先让parent (父)类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。
  3. 缓存机制:缓存机制将会保证所有被加载过的Class 都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存中搜寻该Class,只有当缓存中不存在该Class对象时,系统才会重读取该类对应的二进制数据,并将其转换成Class对象,并存入cache。这就是为什么我们修改了Class 后,程序必须重新启动JVM,程序所作的修改才会生效的原因。
思考:为什么我们修改了Class 后,程序必须重新启动JVM,程序所作的修改才会生效?

缓存机制将会保证所有被加载过的Class 都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存中搜寻该Class,只有当缓存中不存在该Class对象时,系统才会重读取该类对应的二进制数据,并将其转换成Class对象,并存入cache。

类加载器的作用

将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

自定义类加载器

JVM中除根加载器之外的所有炎加载器都是ClassLoader 子类的实例,开发者可以通过扩展ClassLoader的子类,并重写该ClassLoader所包含的方法来实现自定义的类加载器。

如何实现,暂略。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值