一篇文章带你深入了解Java类加载
作者:Serendipity sn 发布时间:2023-06-11 22:12:00
1.类加载
<1>.父子类执行的顺序
1.父类的静态变量和静态代码块(书写顺序)
2.子类的静态变量和静态代码块(书写顺序)
3.父类的实例代码块(书写顺序)
4.父类的成员变量和构造方法
5.子类的实例代码块
6.子类的成员变量和构造方法
<2>类加载的时机
如果类没有进行初始化,则需要先进行初始化,虚拟机规范则是严格规定有且只有5种情况必须先对类进行初始化(而加载,验证,准备要在这个之前开始)
1.创建类的实例(new的方式),访问某个类的静态变量,或者对该静态变量赋值,调用类的静态方法
2.反射的方式
3.初始化某个类的子类,则其父类也会被初始化
4.java虚拟机启动时被标记为启动类的类,直接使用java.exe来运行的某个主类(如main类)
5.使用jdk1.7的动态语言支持时
<3>类的生命周期
七个阶段:加载,验证,准备,解析,初始化,使用和卸载。其中验证,准备和解析三个部分被称为连接
解析阶段在某些情况下可以在初始化阶段之后再进行,这是为了支持java语言的运行时绑定(动态绑定)
<4>类加载的过程
接下来我们详细讲解一下Java虚拟机中类加载的全过程,也就是加载、验证、准备、解析和初始化这5个阶段所执行的具体动作。
1.加载
<1>通过一个类的全限定名来获取定义此类的二进制字节流。
<2>将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
<3>在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
2.验证
这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
3.准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
假设一个类变量的定义为:
public static int value=123;
那变量value在准备阶段过后的初始值为0而不是123,因为这时候尚未开始执行任何Java方法,而把value赋值为123的putstatic指令是程序被编译后,存放于类构造器()方法之中,所以把value赋值为123的动作将在初始化阶段才会执行。
4.解析
虚拟机将常量池内的符号引用替换为直接引用的过程。
符号引用:符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到内存中。
直接引用:直接引用是和虚拟机实现的内存布局相关的。如果有了直接引用,那引用的目标必定已经在内存中存在。
5.初始化
在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源,或者可以从另外一个角度来表达:初始化阶段是执行类构造器()方法的过程。
了解:
()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问:
public class Test{
static{
i=0; //给变量赋值可以正常编译通过
System.out.print(i); //这句编译器会提示"非法向前引用"
}
static int i=1;
}
1.()方法(Class类的构造方法)与类的构造函数(或者说实例构造器()方法)不同,它不需要显式地调用父类构造器,虚拟机会保证在子类的()方法执行之前,父类的()方法已经执行完毕。因此在虚拟机中第一个被执行的()方法的类肯定是java.lang.Object。
2.()方法对于类或接口来说并不是必需的,如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生成()方法。
3.接口中定义的变量使用时,接口才会初始化:接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生()方法。但接口与类不同的是,执行接口的()方法不需要先执行父接口的()方法。只有当父接口中定义的变量使用时,父接口才会初始化。另外,接口的实现类在初始化时也一样不会执行接口的()方法。
4.虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕。如果在一个类的()方法中有耗时很长的操作,就可能造成多个进程阻塞,在实际应用中这种阻塞往往是很隐蔽的。
<5>类加载器
类加载器可以分为:启动类加载器、扩展类加载器、应用程序类加载器、自定义类加载器。他们的关系一般如下:
1.启动类加载器(BootstrapClassLoader)
这个类由C++语言实现,是虚拟机自身的一部分,并不继承ClassLoader,不能操作它。用来加载Java的核心类。
2.扩展类加载器(ExtClassLoader)
这个类加载器是在类sun.misc.Launcher$ExtClassLoader中以Java代码的形式实现的。它负责加载<JAVA_HOME>\lib\ext目录中,或者被java.ext.dirs系统变量所指定的路径中所有的类库。
3.应用程序类加载器(AppClassLoader)
它负责在 JVM 启动时加载来自 Java 命令的 -classpath 或者 -cp 选项、java.class.path 系统属性指定的 jar 包和类路径。在应用程序代码里可以通过 ClassLoader 的静态方法 getSystemClassLoader() 来获取应用类加载器。如果没有特别指定,则在没有使用自定义类加载器情况下,用户自定义的类都由此加载器加载。
4.2 自定义加载器
用户自定义了类加载器,则自定义类加载器都以应用类加载器作为父加载器。应用类加载器的父类加载器为扩展类加载器。这些类加载器是有层次关系的,启动加载器又叫根加载器,是扩展加载器的父加载器
<6>类加载机制——双亲委派模型
双亲委派模型的过程:如果一个类加载器收到了类加载的请求,它首先不会自己尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一层次的类加载器都是如此,因此所有的加载请求信息最终都会传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(即它的搜索范围没有找到所需要的类)时,子加载器才会尝试自己去完成加载
先查找,再进行加载
(1)从下往上找
(2)从上往下加载
双亲委派模型的好处:双亲委派模型对于java程序的稳定运行极为重要
劣势:无法满足灵活的类加载方式。(解决方案:自己重写loadClass破坏双亲委派模型 例如SPI机制)
来源:https://blog.csdn.net/qq_45704528/article/details/119178033


猜你喜欢
- 固定的策略有时候还是无法满足千变万化的需求变动,一方面需要支持特定的用户需求,另一方面又得尽可能的复用代码,避免重复开发,这就需要将这部分的
- 近期项目中需要使用到一种类似手机电池充电进度的动画效果,以前没学属性动画的时候,是用图片+定时器的方式来完成的,最近一直在学习动画这一块,再
- Android中SQLite 使用方法详解现在的主流移动设备像android、iPhone等都使用SQLite作为复杂数据的存储引擎,在我们
- 360首页搜索效果如下1、完成编写的schoolnet校园网主要目录结构如下主要实现支持中文、拼音首字母、拼音全字母的智能搜索和换肤页面效果
- 前言:mybatisplus 可以说是对mybatis更好的拓展,一些简单的增删改查的操作已经被作者实现,我们只需引用即可。1.数据库建表这
- Spring的HandlerMapping支持 * , * 必须实现HandlerInterceptor接口,此接口里面有下面3中方法:1.
- java LRU(Least Recently Used )详解LRU是Least Recently Used 的缩写,翻译过来就是“最近最
- 本文实例为大家分享了SSM实现学生管理系统的具体代码,供大家参考,具体内容如下概述基于Spring + Spring MVC 的学生管理系统
- 缘起随着 App 的成长,我们难免会遇到以下这些需求:H5 跳原生界面Notification 点击调相关界面根据后台返回数据跳转界面,例如
- 本文实例讲述了Android判断设备网络连接状态及判断连接方式的方法。分享给大家供大家参考,具体如下:在Android开发过程中,对于一个需
- 仅做学习交流,如有侵犯联系必删。前言一篇酷狗app安卓逆向的文章,难度适中。样本: 酷狗app v10.8.8工具: jadx、Pixel3
- 前言在日常开发中,除了修改请求参数、设置响应header,响应body外,还有一种需求就是url重新,或者是修改url,这里简述一下怎么在z
- Unity中的PostProcessScene:深入解析与实用案例在Unity游戏开发中,我们经常需要对场景进行后处理,以实现更丰富的视觉效
- Java 中的内部类这是一个 Java 内部类的简单实现:public class OutterJava { pr
- 作为一位开发人员,都要有严格的代码规范。为此我总结了一些代码规范案例。目 录1. 前言2. 试用范围3. JAVA命名规范--3.1 公共约
- 一、题目描述题目:有五个学生,每个学生有 3 门课的成绩,从键盘输入以上数据(包括学生号,姓名,三门课成绩),把这些数据存放在磁盘文件 &q
- Arrays.asList()方法的作用是将数组或一些元素转为集合,而你得到的集合并不是我们通常使用的List集合,而是Arrays里面的一
- Android Init进程对信号的处理流程在Android中,当一个进程退出(exit())时,会向它的父进程发送一个SIGC
- ChatGPT的基本介绍ChatGPT是一个用来进行自然语言处理任务的预训练模型。要使用ChatGPT,需要了解以下几点:理解预训练模型:预
- android在网络上下载文件,供大家参考,具体内容如下步骤: 1.使用HTTP协议下载文件- 创建一个HttpURLConnection对