JVM学习-类加载过程(二)

Initialization初始化阶段
  • 为类的静态变量赋予正确的初始值
具体描述
  • 类的初始化是类装载的最后一个阶段,如果前面的步骤没有问题,那么表示类可以顺利装载到系统中,此时,类才会开始执行Java字节码(即,到了初始化阶段,才真正开始执行类中定义的Java代码)
  • 初始化阶段的重要工作是执行类的初始化方法:()方法
  • 该方法仅能由Java编译器生成并由JVM调用,程序开发者无法自定义一个同名方法,更无法直接在Java程序中调用该方法,虽然该方法也是由字节码指令所组成
  • 它是由类静态成员的赋值语句以及static语句块合并产生的
    在这里插入图片描述
说明
  • 在加载一个类之前,虚拟机总是会试图加载该类的父类,因此父类的总是在子类之前被调用,也就是说,父类的static块优先级高于子类------(由父及子,静态先行)
  • Java编译器并不会为所有的类都产生初始化方法,有些类编译成字节码后,不会包含方法
  • 一个类中并没有声明任何的类变量,也没有静态代码块时
  • 一个类中声明类变量,但是没有明确使用类变量的初始化语句以及静态代码块来执行初始化操作时
  • 一个类中包含static final修饰的基本数据类型的字段,这些类字段初始化语句采用编译时常量表达式

在这里插入图片描述

/*** 说明:使用static + final修饰的字段的显示赋值操作,到底是在哪个阶段进行的赋值* 情况1:在链接阶段的准备环节赋值* 情况2:在初始化阶段赋值** 结论:在链接阶段的准备环节赋值* ①对于基本数据类型的字段来说,使用static final修饰,显示赋值(直接赋值常量,非调用方法)在链接阶段的准备环节赋值* ②对于String来说,如使用字面量赋值,使用static final修饰的话,显示赋值在链接阶段的准备环节赋值** 初始化<clinit>()中赋值的情况* 排除上述在准备环节赋值的情况之外的情况* 最终结论:使用static + final修饰,且显示赋值不涉及到方法或构造调用的基本数据类型或String类型的显示赋值,是在链接阶段的准备环节进行*/
public class InitializationTest2 {public static int a = 1;  //在初始化阶段<clinit>赋值public static final int INT_CONSTANT = 10;  //在链接阶段的准备环节赋值public static final Integer INTEGER_CONSTANT1 = Integer.valueOf(100);//在初始化阶段<clinit>赋值public static Integer INTEGER_CONSTANT2 = Integer.valueOf(1000);//在初始化阶段<clinit>赋值public static final String s1 = "helloworld1"; //在链接阶段的准备环节赋值public static final String s0 = new String("helloworld0");//在初始化阶段<clinit>赋值public static final int NUM1 = 2;//在链接阶段的准备环节赋值public static final int NUM2 = new Random().nextInt(10);//在初始化阶段<clinit>赋值
}
//字节码--<clinit>0 iconst_11 putstatic #2 <com/chapter11/InitializationTest2.a>4 bipush 1006 invokestatic #3 <java/lang/Integer.valueOf>9 putstatic #4 <com/chapter11/InitializationTest2.INTEGER_CONSTANT1>
12 sipush 1000
15 invokestatic #3 <java/lang/Integer.valueOf>
18 putstatic #5 <com/chapter11/InitializationTest2.INTEGER_CONSTANT2>
21 new #6 <java/lang/String>
24 dup
25 ldc #7 <helloworld0>
27 invokespecial #8 <java/lang/String.<init>>
30 putstatic #9 <com/chapter11/InitializationTest2.s0>
33 new #10 <java/util/Random>
36 dup
37 invokespecial #11 <java/util/Random.<init>>
40 bipush 10
42 invokevirtual #12 <java/util/Random.nextInt>
45 putstatic #13 <com/chapter11/InitializationTest2.NUM2>
48 return
()线程安全性
  • 对于()方法的调用,也就是类的初始化,虚拟机会在内部确保其多线程环境中的安全性
  • 虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程会执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕
  • 因为函数()带锁线程是安全的,如果一个类的()方法中有耗时很长的操作,就可能造成多个线程阻塞,引发死锁,并且这种死锁很难发现,因为看起来没有可用的锁信息
  • 如果之前的线程成功加载了类,则等在队列中的线程就没有机会再执行()方法了,那么当需要使用这个类时,虚拟机会直接返回它已经准备好的信息
//死锁例子
class staticA {static {try {Thread.sleep(1000);} catch (InterruptedException e) {}try {Class.forName("com.chapter11.staticB");}catch (ClassNotFoundException e) {e.printStackTrace();}System.out.println("StaticA init OK");}
}
class staticB {static {try {Thread.sleep(1000);} catch (InterruptedException e) {}try {Class.forName("com.chapter11.staticA");}catch (ClassNotFoundException e) {e.printStackTrace();}System.out.println("StaticB init OK");}
}
public class StaticDeadLockMain extends Thread{private char flag;public StaticDeadLockMain(char flag) {this.flag = flag;this.setName("Thread" + flag);}@Overridepublic void run() {try {Class.forName("com.chapter11.static" + flag);} catch (ClassNotFoundException e) {e.printStackTrace();}}public static void main(String[] args) {StaticDeadLockMain sdlm1 = new StaticDeadLockMain('A');StaticDeadLockMain sdlm2 = new StaticDeadLockMain('B');sdlm1.start();sdlm2.start();}
}
主动使用VS被动使用
主动使用
  • Class只有在必须首次使用的时候才会被装载,Java虚拟机不会无条件地装载Class类型,Java虚拟机规定,一个类或接口在初次使用前,必须进行初始化,这里指主动使用,主动使用有下面几种情况
  • 当创建一个类的实例时,比如使用new关键字,通过反射,克隆,反序列化
  • 当调用类的静态方法时,即当使用了字节码invokestatic指令
package com.chapter11;import org.junit.Test;import java.io.*;/*** 当创建一个类的实例时,比如使用new关键字,通过反射,克隆,反序列化* 当调用类的静态方法时,即当使用了字节码invokestatic指令*/
class Order implements Serializable{static {System.out.println("Order类的初始化");}public static void method1() {System.out.println("order method1()... ...");}
}
public class ActiveUse1 {public static void main(String[] args) {Order order = new Order();}@Test//当调用类的静态方法时public void test3() {Order.method1();  //0 invokestatic #4 <com/chapter11/Order.method1>}//序列化过程@Testpublic void testSerializable() {ObjectOutputStream oos = null;try {oos = new ObjectOutputStream(new FileOutputStream("order.dat"));oos.writeObject(new Order());} catch (IOException e) {e.printStackTrace();} finally {try {if (oos != null) {oos.close();}} catch (IOException e) {e.printStackTrace();}}}//反序列化@Testpublic void  testDeSerializable() {ObjectInputStream ois = null;try {ois = new ObjectInputStream(new FileInputStream("order.dat"));Object o = ois.readObject();if (o instanceof Order) {Order order = (Order) o;}} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();} finally {try {if (ois != null) {ois.close();}} catch (IOException e) {e.printStackTrace();}}}
}
  • 当使用类,接口的静态字段时(finl特殊考虑),如getstatic或putstatic指令(对应访问变量,赋值变量)
class User {public static int num = 1;public static final int num1 = 2;public static final int num2 = new Random().nextInt(10);static {System.out.println("User类初始化");}
}interface CompareA {public static final Thread t = new Thread() {{System.out.println("CompareA的初始化");}};public static final int NUM1 = 1;public static final int NUM2 = new Random().nextInt(10);
}public class ActiveUser2 {/*** 执行结果--执行clinit* User类初始化* 1*/@Testpublic void test1() {System.out.println(User.num);}/*** 执行结果--不执行clinit* 1*/@Testpublic void test2() {System.out.println(User.num1);}/*** 执行结果--会执行clinit* User类初始化* 7*/@Testpublic void test3() {System.out.println(User.num2);}/*** 执行结果*  1*/@Testpublic void test4() {System.out.println(CompareA.NUM1);}/*** 执行结果*  CompareA的初始化*  9*/@Testpublic void test5() {System.out.println(CompareA.NUM2);}
}
  • 当使用java.lang.reflect包中的方法反射类的方法时,如Class.forName(“className”)
public class ActiveUse3 {/*** 此处使用的Order为ActiveUse1中的Order类* 执行结果* Order类的初始化*/@Testpublic void test1() {try {Class<?> clazz = Class.forName("com.chapter11.Order");} catch (ClassNotFoundException e) {e.printStackTrace();}}
}
  • X当初始化子类时,发现父类没有初始化,需要触发其父类的初始化
 /*** 执行结果* Father类的初始化过程* Son类的初始化过程* 1*/@Testpublic void test2() {System.out.println(Son.num);}
class Father {static {System.out.println("Father类的初始化过程");}
}
class Son extends Father {static {System.out.println("Son类的初始化过程");}public static int  num = 1;
}
  • 一个接口定义了default方法,那直接实现或间接实现该接口类的初始化,该接口要在其之前被初始化
  • 当虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的那个类),虚拟机会先初始化这个主类
  • 当初次调用MethodHandle实例时,初始化该MethodHandle指向的方法所在的类(涉及解析REF_getStatic,REF_putStatic,REF_invokeStatic方法句柄对应的类)
  • 针对X项的补充,当Java虚拟机初始化一个类时,要求它的所有父类都已经被初始化,但这条规则不适用于接口
  • 在初始化一个类时,并不会先初始化它所实现的接口
  • 在初始化一个接口时,并不会先初始化它的父接口
 /*** Father类的初始化过程--(初始化类时,并不会先初始化它所实现的接口)* Son类的初始化过程* 1*/@Testpublic void test3() {System.out.println(Son.num);}/*** 执行结果* CompareC的初始化---初始化一个接口时,不会初始化它的父接口*/@Testpublic void test4() {System.out.println(compareC.num1);}
}
class Father {static {System.out.println("Father类的初始化过程");}
}
class Son extends Father implements compareB{static {System.out.println("Son类的初始化过程");}public static int  num = 1;
}
interface compareB {public static final Thread t = new Thread() {{System.out.println("CompareB的初始化");}};
}
interface compareC extends compareB {public static final Thread t = new Thread() {{System.out.println("CompareC的初始化");}};int num1 = new Random().nextInt(10);
}
  • 因此,一个父接口并不会因为它的子接口或者实现类的初始化而初始化,只有当程序员首次使用特定接口的静态字段时,才会导致该接口的初始化
 @Testpublic void test5() {System.out.println(Son.num);}
class Father {static {System.out.println("Father类的初始化过程");}
}
class Son extends Father implements compareB{static {System.out.println("Son类的初始化过程");}public static int  num = 1;
}
interface compareB {public static final Thread t = new Thread() {{System.out.println("CompareB的初始化");}};public default void method1() {System.out.println("compareB--method1");}
}
//执行结果
Father类的初始化过程
CompareB的初始化
Son类的初始化过程
1
  • 针对JDK7,JVM启动的时候通过引导类加载器加载一个初始类,这个类在调用public static void main(String[] args)方法之前被链接和初始化,这个方法的执行将依次导致所需的类的加载,链接和初始化。
被动使用
  • 被动使用不会引起类的初始化,并不是代码中出现的类,就一定会被加载或者初始化。如果不符合主动使用的条件,类就不会初始化
  • 当访问一个静态字段时,只有真正声明这个字段的类才会被初始化
  • 当通过子类引用父类的静态变量,不会导致此类的初始化
  • 通过数组定义类引用,不会触发此类的初始化
package com.chapter11;import org.junit.Test;/*** Administrator* 2024/5/31*/
class Parent {static {System.out.println("Parent初始化");}public static int num = 1;
}
class Child extends Parent {static {System.out.println("Child初始化");}
}
public class PassiveUse1 {/*** 注:没有初始化不代表没有加载* 执行结果* Parent初始化* 1*/@Testpublic void test1() {System.out.println(Child.num);}/*** 执行结果* 空*/@Testpublic void test2() {Parent[] parents = new Parent[10];}/*** * 执行结果*   Parent初始化*/@Testpublic void test3() {Parent[] parents = new Parent[10];parents[0] = new Parent();}
}
  • 引用常量不会触发此类或接口的初始化,因为常量在链接阶段就已经被显示赋值了
class Person {static {System.out.println("Person类的初始化");}public final static int NUM = 1;public final static int NUM1 = new Random().nextInt(10);
}
public class PassiveUse2 {/*** 执行结果--没有执行初始化* 1*/@Testpublic void test1() {System.out.println(Person.NUM);}/*** 执行结果* Person类的初始化* 1*/@Testpublic void test2() {System.out.println(Person.NUM1);}/*** 执行结果* 2*/@Testpublic void test3() {System.out.println(SerialA.NUM1);}/*** 执行结果* SerialA的初始化* 5*/@Testpublic void test4() {System.out.println(SerialA.NUM2);}
}
interface SerialA {public static final Thread t = new Thread() {{System.out.println("SerialA的初始化");}};int NUM1 = 2;int NUM2 = new Random().nextInt(10);   //赋值操作需要在clinit中执行
}
  • 调用ClassLoader类的loadClass()方法加载一个类,并不对类的主动使用,不会导致类的初始化
//执行结果为空,表示没有显示初始化类
@Testpublic void test5() {try {Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass("com.chapter11.Person");} catch (ClassNotFoundException e) {e.printStackTrace();}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/19507.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

React-基础样式控制

组件基础样式方案 React组件基础的样式控制有两种方式 1、行内样式&#xff08;不推荐&#xff09; 属性名是多个单词的需要使用驼峰写法 也可以把样式都提取到一个变量里&#xff0c;再赋值到style里 2、class类名控制 classnames优化类名控制 classnames是一个简单的JS库&…

arcgis api for javascript点击获取要素错乱的问题

今天帮同事看了一个前端地图点击的问题&#xff1a;点击时总会获取到周边的图元&#xff0c;即使我点击线的周围&#xff0c;也是能获取到的&#xff0c;除非离得特别远。 地图组件用的是arcgis api, 图层类是grahicslayer,要素类型是线。这是添加图元的代码&#xff1a; grap…

探索多模态MR图像的脑肿瘤分割任务结构| 文献速递-深度学习肿瘤自动分割

Title 题目 Exploring Task Structure for Brain Tumor Segmentation From Multi Modality MR Images 探索多模态MR图像的脑肿瘤分割任务结构 01 文献速递介绍 脑肿瘤分割旨在从多模态磁共振&#xff08;MR&#xff09;序列中自动分割肿瘤区域&#xff0c;这些序列由先进的…

万字长文深度解析Agent反思工作流框架Reflexion中篇

前文《LLM-Agents]万字长文深度解析Agent反思工作流框架Reflexion上篇&#xff1a;安装与运行》我们已经介绍了 Reflexion 框架的背景知识、数据集以及安装运行方法。在本文中&#xff0c;我们将深入探讨 Agent 的具体运行细节。 上篇讲到agent.run(reflect_strategystrategy)…

艾体宝方案 | redis赋能游戏开发,游戏玩家纵享丝滑

掉线&#xff0c;加载缓慢&#xff0c;反馈无跟进&#xff0c;这些令游戏玩家炸毛的问题&#xff0c;同时也是游戏开发者关注的问题。开发者将目光投向了Redis&#xff0c;一个实时数据平台&#xff0c;告别卡顿延迟&#xff01; 一、玩家不掉线&#xff0c;游戏更丝滑 在大型…

NVIDIA Blackwell Architecture

本文翻译自&#xff1a;NVIDIA Blackwell Architecture https://www.nvidia.com/en-us/data-center/technologies/blackwell-architecture/ 文章目录 了解技术突破1、新型人工智能超级芯片2、第二代 Transformer 引擎3、Secure AI4、NVLink 和 NVLink 交换机5、解压缩引擎6、可…

VNC server ubuntu20 配置

介绍 最近想使用实验室的4卡服务器跑一些深度学习实验&#xff0c;因为跑的是三维建图实验&#xff0c;需要配上可视化界面&#xff0c;本来自带的IPMI可以可视化&#xff0c;但分辨率固定在640*480&#xff0c;看起来很别扭&#xff0c;就捣鼓服务器远程可视化访问了两天&…

vue2转vue3初步下载pnpm遇到的问题 pnpm : 无法加载文件 D:\nodejs\pnpm.ps1

安装pnpm npm install -g pnpm pnpm -v 提示&#xff1a; 解决&#xff1a;nvm install 18.18.0 下载最稳定版本的nodejs nvm use 18.18.0 然后注意重新下载删除pnpm npm uninstall -g pnpm npm install -g pnpmlatest 在vscode使用pnpm报错 解决&#xff1a;管理员运行Windo…

web项目规范配置(husky、eslint、lint-staged、commit)

背景&#xff1a; 团队开发为了保证提交代码格式统一&#xff0c;通常在进行代码提交的时候对暂存区代码进行校验&#xff0c;如没有通过eslint(本例使用eslint)校验&#xff0c;则不能提交到远端。 安装依赖 husky 、eslint 、prettier 、lint-staged npm install husky e…

Satellite Stereo Pipeline学习

1.在Anaconda某个环境中安装s2p pip install s2p 2.在Ubuntu系统中安装s2p源代码 git clone https://github.com/centreborelli/s2p.git --recursive cd s2p pip install -e ".[test]" 3.在s2p中进行make all处理 中间会有很多情况&#xff0c;基本上哪个包出问题…

基于网关的ip频繁访问web限制

一、前言 外部ip对某一个web进行频繁访问&#xff0c;有可能是对web进行攻击&#xff0c;现在提供一种基于网关的ip频繁访问web限制策略&#xff0c;犹如带刀侍卫&#xff0c;审查异常身份人员。如发现异常或者暴力闯关者&#xff0c;即可进行识别管制。 二、基于网关的ip频繁访…

【Python】 倒序遍历列表:Python中的简单技巧

基本原理 在Python中&#xff0c;列表是一种非常灵活的数据结构&#xff0c;它允许我们存储一系列的元素。有时&#xff0c;我们需要按照与元素添加顺序相反的顺序来遍历列表。这通常被称为“倒序遍历”。Python提供了几种不同的方法来实现这一功能。 代码示例 示例1&#x…

【vueCms】vueCms后台管理系统安装问题集合

开源项目地址: https://www.vuecms.cn/ 开源代码地址: https://gitee.com/derekgo/vue-cms_xg 问题一 如果出现提示少了个index.html。如下图 解决办法: 重新安装前端(vue3_vite)项目依赖 问题二 npm版本高无法解析依赖树导致依赖下载失败 解决方案: npm install --legacy…

【Flask-app.py运行】已解决Cannot run program “D:\APP\python\python.exe”

文章目录 一、问题描述二、解决方法 一、问题描述 Cannot run program “D:\APP\python\python.exe” (in directory “F:\Codes\竞赛\大计赛\group\code\web\web”): CreateProcess error2, 系统找不到指定的文件。 这段报错源于运行 flask 项目的 app.py 时报错找不到程序&…

C++ | Leetcode C++题解之第119题杨辉三角II

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> getRow(int rowIndex) {vector<int> row(rowIndex 1);row[0] 1;for (int i 1; i < rowIndex; i) {row[i] 1LL * row[i - 1] * (rowIndex - i 1) / i;}return row;} };

SNCScan:针对SAP安全网络通信(SNC)的安全分析与评估工具

关于SNCScan SNCScan是一款针对SAP安全网络通信&#xff08;SNC&#xff09;的安全分析与评估工具&#xff0c;该工具旨在帮助广大研究人员分析SAP安全网络通信&#xff08;SNC&#xff09;&#xff0c;并分析和检测SNC配置与SAP组件中的潜在问题。 SNC系统参数 SNC基础 SAP协…

面向Java程序员的Go工程开发入门流程

对于一个像我这样没有go背景的java程序员来说&#xff0c;使用go开发一个可用的程序的速度是肉眼可见的缓慢。 其难点不在于go语言本身&#xff0c;而是搭建整个工程链路的过程&#xff0c;即所谓的“配环境”。 本文主要讲述如何配出一个适合go开发的环境&#xff0c;以免有同…

未来工厂新篇章:大型工厂3D可视化技术引领工业新潮流

在科技日新月异的今天&#xff0c;大型工厂不再是我们印象中机器轰鸣、尘土飞扬的钢铁丛林&#xff0c;而是变成了智慧与效率并存的现代化生产中心。这一切的改变&#xff0c;都离不开一项革命性的技术——3D可视化。 一、何为大型工厂3D可视化&#xff1f; 大型工厂3D可视化&…

MATLAB函数模块光显示zeros/poles怎么办?

出现下面这种图了怎么办&#xff1f;是做错了吗&#xff1f; 这种图就是它显示不完整了&#xff0c;把它拉大点就可以完全显示了。

K8s Ingress 详解

文章目录 K8s Ingress 详解Ingress 资源清单Ingress 基于URL 实现路由Ingress 基于名称虚拟主机Ingress 实现HTTPS创建TLS 证书创建Secrets配置ingress Ingress RewriteIngress 灰度发布Ingress 配置认证 K8s Ingress 详解 Ingress 资源清单 apiVersion: networking.k8s.io/v…