免费APP 微信 网站平台,网站建设优化哪家专业,深圳软件公司名录,如何做招聘网站的方案①. 说说类加载分几步#xff1f;
①. 按照Java虚拟机规范,从class文件到加载到内存中的类,到类卸载出内存为止,它的整个生命周期包括如下7个阶段: 第一过程的加载(loading)也称为装载验证、准备、解析3个部分统称为链接(Linking)在Java中数据类型分为基本数据类型和引用数据…①. 说说类加载分几步
①. 按照Java虚拟机规范,从class文件到加载到内存中的类,到类卸载出内存为止,它的整个生命周期包括如下7个阶段: 第一过程的加载(loading)也称为装载验证、准备、解析3个部分统称为链接(Linking)在Java中数据类型分为基本数据类型和引用数据类型。基本数据类型由虚拟机预先定义,引用数据类型则需要进行类的加载 ②. 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载、类的链接、类的初始化这三个步骤来对类进行初始化。如果不出现意外,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者初始化
③. 从程序中类的使用过程看: ②. 过程一:类的加载(Loading)
①. 类的加载指的是将类的.class文件中的二进制数据读取到内存中,存放在运行时数据区的方法区中,并创建一个大的Java.lang.Class对象,用来封装方法区内的数据结构 在加载类时,Java虚拟机必须完成以下3件事情: ②. 对于①的说明,我们也可以这样去理解:所谓装载(加载),简而言之就是将Java类的字节码文件加载到机器内存中,并在内存中构建出Java类的原型——类模板对象
(所谓类模板对象,其实就是Java类在JVM内存中的一个快照,JVM将从字节码文件中解析出的常量池、类字段、类方法等信息存储到类模板中,这样JVM在运行期便能通过类模板而获取Java类中的任意信息,能够对Java类的成员变量进行遍历,也能进行Java方法的调用)
③. 对于类的二进制数据流,虚拟机可以通过多种途径产生或获得(只要所读取的字节码符合JVM规范即可)
1.虚拟机可能通过文件系统读入一个class后缀的文件(最常见)
2.读入jar、zip等归档数据包,提取类文件。
3.事先存放在数据库中的类的二进制数据
4.使用类似于HTTP之类的协议通过网络进行加载
5.在运行时生成一段Class的二进制信息等
④. Class实例的位置
(类将.class文件加载至元空间后,会在堆中创建一个Java.lang.Class对象,用来封装类位于方法区内的数据结构,该Class对象是在加载类的过程中创建的,每个类都对应有一个Class类型的对象) 通过类的全名,获取类的二进制数据流解析类的二进制数据流为方法区内的数据结构(Java类模型)创建java.lang.Class类的实例,表示该类型。作为方法区这个类的各种数据的访问入口
③. 过程二:链接(Linking) ①. 验证:确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性 1.目的是确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全 2.主要包括四种验证:文件格式验证,元数据验证,字节码验证,符号引用验证 3.格式检查:是否以魔术oxCAFEBABE开头,主版本和副版本是否在当前Java虚拟机的支持范围内,数据中每一项是否都拥有正确的长度等 ②. 准备(静态变量,不能是常量) 1.为类变量分配内存并且设置该类变量的默认初始化值 2.这里不包含用final修饰的static,因为final在编译的时候就会分配了,准备阶段会显式赋值 3.这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量会随着对象一起分配到Java堆中 4.注意:Java并不支持boolean类型,对于boolean类型,内部实现是int,由于int的默认值是0,故对应的,boolean的默认值就是false public class ClassInitTest {private static int num1; //类变量的赋值动作//静态代码快中的语句static{num2;number20;System.out.println(num);//System.out.println(number); 报错:非法的前向引用}//Linking之prepare: number0 --initial:20--10private static int number10;public static void main(String[] args) {System.out.println(ClassInitTest.num);System.out.println(ClassInitTest.number);}
}③. 若该类具有父类,Jvm会保证子类的 clinit () 执行前,父类的 clinit () 已经执行完成。clinit 不同于类的构造方法(init) (由父及子,静态先行) public class ClinitTest1 {static class Father{public static int A1;static{A2;}}static class Son extends Father{public static int BA;}public static void main(String[] args) {//这个输出2,则说明父类已经全部加载完毕System.out.println(Son.B);}
}/*** author TANGZHI* create 2021-01-01 18:49* 哪些场景下,java编译器就不会生成clinit()方法*/
public class InitializationTest1 {//场景1:对应非静态的字段,不管是否进行了显式赋值,都不会生成clinit()方法public int num 1;//场景2:静态的字段,没有显式的赋值,不会生成clinit()方法public static int num1;//场景3:比如对于声明为static final的基本数据类型的字段,不管是否进行了显式赋值,都不会生成clinit()方法public static final int num2 1;
}/*** author TANGZHI* create 2021-01-01 ** 说明:使用static final修饰的字段的显式赋值的操作,到底是在哪个阶段进行的赋值* 情况1:在链接阶段的准备环节赋值* 情况2:在初始化阶段clinit()中赋值* 结论:* 在链接阶段的准备环节赋值的情况:* 1. 对于基本数据类型的字段来说,如果使用static final修饰,则显式赋值(直接赋值常量,而非调用方法)通常是在链接阶段的准备环节进行* 2. 对于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 s0 helloworld0;//在链接阶段的准备环节赋值public static final String s1 new String(helloworld1);//在初始化阶段clinit()中赋值public static String s2 helloworld2;public static final int NUM1 new Random().nextInt(10);//在初始化阶段clinit()中赋值
}package com.xiaozhi;/*** author TANGZHI* create 2021-05-25*/
class StaticA {static {try {Thread.sleep(1000);} catch (InterruptedException e) {}try {Class.forName(com.xiaozhi.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.xiaozhi.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.xiaozhi.Static flag);} catch (ClassNotFoundException e) {e.printStackTrace();}System.out.println(getName() over);}public static void main(String[] args) throws InterruptedException {StaticDeadLockMain loadA new StaticDeadLockMain(A);loadA.start();StaticDeadLockMain loadB new StaticDeadLockMain(B);loadB.start();}
}⑤. 主动引用(触发在初始化阶段的Clinit方法) # 注意,如果把A接口中的默认方法注释,那么就只输出:子类初始化...... 输出: CompareB的初始化 子类初始化..... public class DemoB implements A{ static{ System.out.println(子类初始化......); } public static void main(String[] args) { } } interface A{ public static final Thread t new Thread() { { System.out.println(CompareB的初始化); } }; default void method1(){ System.out.println(); } } ⑥. 被动使用 ⑦. 过程四:类的Using(使用) ⑧. 过程五:类的Unloading(卸载)