Java技术_基础技术(0003)_类执行顺序详解+实例(阿里面试题)+详细讲解+流程图
类加载机制
加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班地开始,而解析阶段则不一定:它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运行时绑定(也称为动态绑定或晚期绑定)。以下陈述的内容都已HotSpot为基准。
1. 加载
在加载阶段(可以参考java.lang.ClassLoader的loadClass()方法),虚拟机需要完成以下3件事情:
- 通过一个类的全限定名来获取定义此类的二进制字节流(并没有指明要从一个Class文件中获取,可以从其他渠道,譬如:网络、动态生成、数据库等);
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构;
- 在内存中(堆中)生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口;
加载阶段和连接阶段(Linking)的部分内容(如一部分字节码文件格式验证动作)是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始,但这些夹在加载阶段之中进行的动作,仍然属于连接阶段的内容,这两个阶段的开始时间仍然保持着固定的先后顺序。
2. 验证
验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
验证阶段大致会完成4个阶段的检验动作:
文件格式验证:验证字节流是否符合Class文件格式的规范;例如:是否以魔术0xCAFEBABE开头、主次版本号是否在当前虚拟机的处理范围之内、常量池中的常量是否有不被支持的类型。
元数据验证:对字节码描述的信息进行语义分析(注意:对比javac编译阶段的语义分析),以保证其描述的信息符合Java语言规范的要求;例如:这个类是否有父类,除了java.lang.Object之外。
字节码验证:通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
符号引用验证:确保解析动作能正确执行。
验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用-Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。
3. 准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在堆中。其次,这里所说的初始值“通常情况”下是数据类型的零值,假设一个类变量的定义为:
public static int value=123;
1
那变量value在准备阶段过后的初始值为0而不是123.因为这时候尚未开始执行任何java方法,而把value赋值为123的putstatic指令是程序被编译后,存放于类构造器()方法之中,所以把value赋值为123的动作将在初始化阶段才会执行。
至于“特殊情况”是指:public static final int value=123,即当类字段的字段属性是ConstantValue时,会在准备阶段初始化为指定的值,所以标注为final之后,value的值在准备阶段初始化为123而非0.
4. 解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。
5. 初始化
类初始化阶段是类加载过程的最后一步,到了初始化阶段,才真正开始执行类中定义的java程序代码。在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序猿通过程序制定的主观计划去初始化类变量和其他资源,或者说:初始化阶段是执行类构造器<clinit>()方法的过程.
<clinit>()方法是由编译器按顺序自动收集类中的所有类变量的赋值动作和静态语句块static{}中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的
例子1:
1 package static1;
2
3 public class StaticTest
4 {
5 public static void main(String[] args)
6 {
7 staticFunction();
8 }
9
10 static StaticTest st = new StaticTest();
11
12 static
13 {
14 System.out.println("1");
15 }
16
17 {
18 System.out.println("2");
19 }
20
21 StaticTest()
22 {
23 System.out.println("3");
24 System.out.println("a="+a+",b="+b);
25 }
26
27 public static void staticFunction(){
28 System.out.println("4");
29 }
30
31 int a=110;
32 static int b =112;
33 }
执行结果:
2
3
a=110,b=0
1
4
字节码结果分析
- 165行:属于类的构造方法(顺序进行 类变量赋值、静态块执行),代码的第10行执行 static StaticTest st = new StaticTest(); 为st赋值,要先new一个StaticTest实例变量(会调用实例构造方法-字节码的第109行)
- 109行:属于实例构造方法:(顺序执行 初始化成员变量(-非类变量)、非静态块,再执行实例构造方法);代码执行顺序如下
- 先17-19行的非静态块
- 31行的成语变量赋值
- 执行实例构造方法(21-25行)
javp -v -p StaticTest.class


1 Classfile /Users/wuxiong.wx/work/code/testDemo/target/classes/static1/StaticTest.class
2 Last modified 2023-4-21; size 1074 bytes
3 MD5 checksum 1f95645f966d54b5c0d941d86616d858
4 Compiled from "StaticTest.java"
5 public class static1.StaticTest
6 minor version: 0
7 major version: 52
8 flags: ACC_PUBLIC, ACC_SUPER
9 Constant pool:
10 #1 = Methodref #17.#41 // static1/StaticTest.staticFunction:()V
11 #2 = Methodref #21.#42 // java/lang/Object."<init>":()V
12 #3 = Fieldref #43.#44 // java/lang/System.out:Ljava/io/PrintStream;
13 #4 = String #45 // 2
14 #5 = Methodref #46.#47 // java/io/PrintStream.println:(Ljava/lang/String;)V
15 #6 = Fieldref #17.#48 // static1/StaticTest.a:I
16 #7 = String #49 // 3
17 #8 = Class #50 // java/lang/StringBuilder
18 #9 = Methodref #8.#42 // java/lang/StringBuilder."<init>":()V
19 #10 = String #51 // a=
20 #11 = Methodref #8.#52 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21 #12 = Methodref #8.#53 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
22 #13 = String #54 // ,b=
23 #14 = Fieldref #17.#55 // static1/StaticTest.b:I
24 #15 = Methodref #8.#56 // java/lang/StringBuilder.toString:()Ljava/lang/String;
25 #16 = String #57 // 4
26 #17 = Class #58 // static1/StaticTest
27 #18 = Methodref #17.#42 // static1/StaticTest."<init>":()V
28 #19 = Fieldref #17.#59 // static1/StaticTest.st:Lstatic1/StaticTest;
29 #20 = String #60 // 1
30 #21 = Class #61 // java/lang/Object
31 #22 = Utf8 st
32 #23 = Utf8 Lstatic1/StaticTest;
33 #24 = Utf8 a
34 #25 = Utf8 I
35 #26 = Utf8 b
36 #27 = Utf8 main
37 #28 = Utf8 ([Ljava/lang/String;)V
38 #29 = Utf8 Code
39 #30 = Utf8 LineNumberTable
40 #31 = Utf8 LocalVariableTable
41 #32 = Utf8 args
42 #33 = Utf8 [Ljava/lang/String;
43 #34 = Utf8 <init>
44 #35 = Utf8 ()V
45 #36 = Utf8 this
46 #37 = Utf8 staticFunction
47 #38 = Utf8 <clinit>
48 #39 = Utf8 SourceFile
49 #40 = Utf8 StaticTest.java
50 #41 = NameAndType #37:#35 // staticFunction:()V
51 #42 = NameAndType #34:#35 // "<init>":()V
52 #43 = Class #62 // java/lang/System
53 #44 = NameAndType #63:#64 // out:Ljava/io/PrintStream;
54 #45 = Utf8 2
55 #46 = Class #65 // java/io/PrintStream
56 #47 = NameAndType #66:#67 // println:(Ljava/lang/String;)V
57 #48 = NameAndType #24:#25 // a:I
58 #49 = Utf8 3
59 #50 = Utf8 java/lang/StringBuilder
60 #51 = Utf8 a=
61 #52 = NameAndType #68:#69 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
62 #53 = NameAndType #68:#70 // append:(I)Ljava/lang/StringBuilder;
63 #54 = Utf8 ,b=
64 #55 = NameAndType #26:#25 // b:I
65 #56 = NameAndType #71:#72 // toString:()Ljava/lang/String;
66 #57 = Utf8 4
67 #58 = Utf8 static1/StaticTest
68 #59 = NameAndType #22:#23 // st:Lstatic1/StaticTest;
69 #60 = Utf8 1
70 #61 = Utf8 java/lang/Object
71 #62 = Utf8 java/lang/System
72 #63 = Utf8 out
73 #64 = Utf8 Ljava/io/PrintStream;
74 #65 = Utf8 java/io/PrintStream
75 #66 = Utf8 println
76 #67 = Utf8 (Ljava/lang/String;)V
77 #68 = Utf8 append
78 #69 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
79 #70 = Utf8 (I)Ljava/lang/StringBuilder;
80 #71 = Utf8 toString
81 #72 = Utf8 ()Ljava/lang/String;
82 {
83 static static1.StaticTest st;
84 descriptor: Lstatic1/StaticTest;
85 flags: ACC_STATIC
86
87 int a;
88 descriptor: I
89 flags:
90
91 static int b;
92 descriptor: I
93 flags: ACC_STATIC
94
95 public static void main(java.lang.String[]);
96 descriptor: ([Ljava/lang/String;)V
97 flags: ACC_PUBLIC, ACC_STATIC
98 Code:
99 stack=0, locals=1, args_size=1
100 0: invokestatic #1 // Method staticFunction:()V
101 3: return
102 LineNumberTable:
103 line 7: 0
104 line 8: 3
105 LocalVariableTable:
106 Start Length Slot Name Signature
107 0 4 0 args [Ljava/lang/String;
108
109 static1.StaticTest();
110 descriptor: ()V
111 flags:
112 Code:
113 stack=3, locals=1, args_size=1
114 0: aload_0
115 1: invokespecial #2 // Method java/lang/Object."<init>":()V
116 4: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
117 7: ldc #4 // String 2
118 9: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
119 12: aload_0
120 13: bipush 110
121 15: putfield #6 // Field a:I
122 18: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
123 21: ldc #7 // String 3
124 23: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
125 26: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
126 29: new #8 // class java/lang/StringBuilder
127 32: dup
128 33: invokespecial #9 // Method java/lang/StringBuilder."<init>":()V
129 36: ldc #10 // String a=
130 38: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
131 41: aload_0
132 42: getfield #6 // Field a:I
133 45: invokevirtual #12 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
134 48: ldc #13 // String ,b=
135 50: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
136 53: getstatic #14 // Field b:I
137 56: invokevirtual #12 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
138 59: invokevirtual #15 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
139 62: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
140 65: return
141 LineNumberTable:
142 line 22: 0
143 line 18: 4
144 line 31: 12
145 line 23: 18
146 line 24: 26
147 line 25: 65
148 LocalVariableTable:
149 Start Length Slot Name Signature
150 0 66 0 this Lstatic1/StaticTest;
151
152 public static void staticFunction();
153 descriptor: ()V
154 flags: ACC_PUBLIC, ACC_STATIC
155 Code:
156 stack=2, locals=0, args_size=0
157 0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
158 3: ldc #16 // String 4
159 5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
160 8: return
161 LineNumberTable:
162 line 28: 0
163 line 29: 8
164
165 static {};
166 descriptor: ()V
167 flags: ACC_STATIC
168 Code:
169 stack=2, locals=0, args_size=0
170 0: new #17 // class static1/StaticTest
171 3: dup
172 4: invokespecial #18 // Method "<init>":()V
173 7: putstatic #19 // Field st:Lstatic1/StaticTest;
174 10: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
175 13: ldc #20 // String 1
176 15: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
177 18: bipush 112
178 20: putstatic #14 // Field b:I
179 23: return
180 LineNumberTable:
181 line 10: 0
182 line 14: 10
183 line 32: 18
184 }
185 SourceFile: "StaticTest.java"
java中类不会被初始化(仅加载)的几种情况
1. 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。子类和父类都会被加载
1_1. 通过AA.num引用静态常量,不会触发AA类的加载、也不会初始化(常量在编译期间会存入调用类的常量池中)
2. 通过 Class.forName 加载指定类时,如果指定参数 initialize 为 false 时,也不会触
* 发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。会加载类。
3. 通过类名获取 Class 对象(Class.forName(**)),不会触发类的初始化,会加载
4. 通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作(加载了,但是不初始化)。
5. 通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作(加载了,但是不初始化)。
6. 定义对象数组,不会触发该类的初始化。该类会加载 (A[] bs=new A[10];)
6_1. new对象(子类):会加载父类、子类,再初始化父类、子类 —— 应该是这个
* (还是先加载父类、初始化父类,再加载子类、初始化子类?)
深入理解JAVA虚拟机:https://doc.yonyoucloud.com/doc/wiki/project/java-vm/class-initialization.html
类初始化是类加载过程的最后一个阶段,到初始化阶段,才真正开始执行类中的 Java 程序代码。虚拟机规范严格规定了有且只有四种情况必须立即对类进行初始化:
- 遇到 new、getstatic、putstatic、invokestatic 这四条字节码指令时,如果类还没有进行过初始化,则需要先触发其初始化。生成这四条指令最常见的 Java 代码场景是:使用 new 关键字实例化对象时、读取或设置一个类的静态字段(static)时(被 static 修饰又被 final 修饰的,已在编译期把结果放入常量池的静态字段除外)、以及调用一个类的静态方法时。
- 使用 Java.lang.refect 包的方法对类进行反射调用时,如果类还没有进行过初始化,则需要先触发其初始化。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,用户需要指定一个要执行的主类,虚拟机会先执行该主类。
虚拟机规定只有这四种情况才会触发类的初始化,称为对一个类进行主动引用,除此之外所有引用类的方式都不会触发其初始化,称为被动引用。下面举一些例子来说明被动引用。
通过子类引用父类中的静态字段,这时对子类的引用为被动引用,因此不会初始化子类,只会初始化父类:
1 package static2;
2
3 class A {
4 public static int a = 10;
5 public static final int num = 1998;
6 static {
7 System.out.println("A-初始化");
8 }
9 }
10 public class AA extends A{
11 static {
12 System.out.println("AA-初始化");
13 }
14 }
1 package static2;
2
3 import java.lang.reflect.Field;
4 import java.util.ArrayList;
5 import java.util.List;
6 import java.util.Vector;
7 import java.util.stream.Collectors;
8
9 public class PrintClassInfo {
10
11 public static void printLoadedClass(String packageNameFilter)throws Exception{
12 if(packageNameFilter==null){
13 packageNameFilter="static2";
14 }
15 final String condition=packageNameFilter;
16 ClassLoader classLoader=Thread.currentThread().getContextClassLoader();
17 Class loader=ClassLoader.class;
18 Field classesFiled = loader.getDeclaredField("classes");
19 classesFiled.setAccessible(true);
20 Vector<Class> classVector = (Vector<Class>)classesFiled.get(classLoader);
21 List<Class> classes=new ArrayList<>(classVector);
22 classes = classes.stream().filter(clazz->clazz.getName().contains(condition)).collect(Collectors.toList());
23 System.out.println("加载的类:");
24 classes.forEach(clazz-> System.out.println(clazz.getName()));
25 }
26
27 }
1 package static2;
2
3 /**
4 * java中类不会被初始化(仅加载)的几种情况:https://blog.csdn.net/CaptHua/article/details/120925452
5 *
6 * 1. 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。子类和父类都会被加载
7 *
8 * 输出结果:
9 * A-初始化
10 * 10
11 * ************************
12 * 加载的类:
13 * static2.PrintClassInfo
14 * static2.Test1
15 * static2.A
16 * static2.AA
17 */
18 public class Test1 extends PrintClassInfo{
19 public static void main(String[] args) throws Exception {
20 System.out.println(AA.a);
21
22 System.out.println("************************");
23
24 printLoadedClass("static2");
25 }
26 }
1 package static2;
2
3 /**
4 * 1_1. 通过AA.num引用静态常量,不会触发AA类的加载、也不会初始化(常量在编译期间会存入调用类的常量池中)
5 *
6 * 输出结果:
7 * 1998
8 * ************************
9 * 加载的类:
10 * static2.PrintClassInfo
11 * static2.Test1_1
12 */
13 public class Test1_1 extends PrintClassInfo{
14 public static void main(String[] args) throws Exception {
15 System.out.println(AA.num);
16
17 System.out.println("************************");
18
19 printLoadedClass("static2");
20 }
21 }
1 package static2;
2
3 /**
4 * 2. 通过 Class.forName 加载指定类时,如果指定参数 initialize 为 false 时,也不会触
5 * 发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。会加载类。
6 *
7 * 输出结果:
8 * class static2.A
9 * ************************
10 * 加载的类:
11 * static2.PrintClassInfo
12 * static2.Test2
13 * static2.A
14 *
15 */
16 public class Test2 extends PrintClassInfo{
17 public static void main(String[] args) throws Exception {
18 System.out.println(A.class);
19
20 System.out.println("************************");
21
22 printLoadedClass(null);
23 }
24 }
1 package static2;
2
3 /**
4 * 3. 通过类名获取 Class 对象,不会触发类的初始化,会加载
5 *
6 * ************************
7 * 加载的类:
8 * static2.PrintClassInfo
9 * static2.Test3
10 * static2.A
11 */
12 public class Test3 extends PrintClassInfo{
13 public static void main(String[] args) throws Exception {
14 Class.forName("static2.A",false,Thread.currentThread().getContextClassLoader());
15
16 System.out.println("************************");
17
18 printLoadedClass(null);
19 }
20 }
1 package static2;
2
3 /**
4 * 4. 通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作(加载了,但是不初始化)。
5 *
6 * 输出结果:
7 * ************************
8 * 加载的类:
9 * static2.PrintClassInfo
10 * static2.Test4
11 * static2.A
12 */
13 public class Test4 extends PrintClassInfo{
14 public static void main(String[] args) throws Exception {
15 Class.forName("static2.A",false,Thread.currentThread().getContextClassLoader());
16
17 System.out.println("************************");
18
19 printLoadedClass(null);
20 }
21 }
1 package static2;
2
3 /**
4 * 5. 通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作(加载了,但是不初始化)。
5 *
6 * 输出结果:
7 * ************************
8 * 加载的类:
9 * static2.PrintClassInfo
10 * static2.Test5
11 * static2.A
12 */
13 public class Test5 extends PrintClassInfo{
14 public static void main(String[] args) throws Exception {
15 ClassLoader classLoader=Thread.currentThread().getContextClassLoader();
16 classLoader.loadClass("static2.A");
17
18 System.out.println("************************");
19
20 printLoadedClass(null);
21 }
22 }
1 package static2;
2
3 /**
4 * 6. 定义对象数组,不会触发该类的初始化。该类会加载
5 *
6 * 输出结果:
7 * ************************
8 * 加载的类:
9 * static2.PrintClassInfo
10 * static2.Test6
11 * static2.A
12 */
13 public class Test6 extends PrintClassInfo{
14 public static void main(String[] args) throws Exception {
15 A[] bs=new A[10];
16
17 System.out.println("************************");
18
19 printLoadedClass(null);
20 }
21 }
1 package static2;
2
3 /**
4 * 6_1. new对象(子类):会加载父类、子类,再初始化父类、子类 —— 应该是这个
5 * (还是先加载父类、初始化父类,再加载子类、初始化子类?)
6 *
7 * 输出结果:
8 * ************************
9 * 加载的类:
10 * static2.PrintClassInfo
11 * static2.Test6
12 * static2.B
13 */
14 public class Test6_1 extends PrintClassInfo{
15 public static void main(String[] args) throws Exception {
16 A b=new AA();
17
18 System.out.println("************************");
19
20 printLoadedClass(null);
21 }
22 }
例子2:
一、总体原则
列出执行顺序的原则(这里本人出了简化,比较明了。可能有漏的,请帮忙补充,但应付该实例足以):
==父类先于子类;
==静态先于非静态;
==变量和块先于构造方法;
==变量声明先于执行(变量赋值、块执行);(这一点是根据数据在内存中是如何存储的得出的,基本类型、对象、String均不一样,但是总结起来应该是这样)
==变量赋值与块优先级相同;
==同优先级的从上至下执行;
二、实例
public class StaticTest {
public static int k = 0; // 1
public static StaticTest s1 = new StaticTest("t1"); // 2
public static StaticTest s2 = new StaticTest("t2"); //3
public static int i = print("i"); //4
public static int n = 99; //5
public int j = print("j");// 6 {
print("构造快");
} // 7 static{
print("静态块");
} // 8 public StaticTest(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n;
++i;
} // 9 public static int print(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++i;
return ++n;
} public static void main(String[] args) {
StaticTest t = new StaticTest("init");
}
}
三、结果
syso1 ==== 1:j i=0 n=0
syso2 ==== 2:构造快 i=1 n=1
syso3 ==== 3:t1 i=2 n=2
syso4 ==== 4:j i=3 n=3
syso5 ==== 5:构造快 i=4 n=4
syso6 ==== 6:t2 i=5 n=5
syso7 ==== 7:i i=6 n=6
syso8 ==== 8:静态块 i=7 n=99
syso9 ==== 9:j i=8 n=100
syso10 ==== 10:构造快 i=9 n=101
syso11 ==== 11:init i=10 n=102
四、说明
一步一步按照上面的原则来分析,应该比较详细了,这一部分内容在下载的代码里也有。分析如下:
/**
* ====step1====首先声明StaticTest类的static变量并给与默认值。
*
* 故,执行1、2、3、4、5变量的声明。运行后变量如下:
*
* k=0; s1=null; s2=null; i=0; n=0; j未声明;
*
* ====step1 end====
*/
/**
* ====step2====变量声明完成后,为这些static变量赋值以及执行static块(赋值和块优先级一致,在前面的先执行).
*
* 故,执行1、2、3、4、5的赋值代码,8的块代码
*
* ========step2_1====当step2执行到2时,new了一个新的StaticTest对象.
* (此时2、3、4、5的赋值、8的代码都没执行,变量k被赋值为0,其余仍与step1中一致 )
* 这时,static变量已经声明(static变量是类属性,与对象无关),所以这里直接声明非static变量并给与默认值。
*
* 故,执行6变量的声明,运行后变量如下:
*
* k=0; s1=null; s2=null; i=0; n=0; j=0(s1的);
*
* ========step2_2====变量声明完成后,为这些非static变量赋值以及执行非static块。
*
* 故,执行6变量的赋值代码(syso1====k、n、i、j均+1),7的块代码(syso2====k、n、i均+1),运行后变量如下:
*
* k=2; s1=null; s2=null; i=2; n=2; j=1(s1的);
*
* ========step2_3====变量赋值、块执行完毕后,执行构造方法。
*
* 故,执行9构造方法(syso3====k、n、i均+1),运行后变量如下:
*
* k=3; s1=StaticTest对象; s2=null; i=3; n=3; j=1(s1的);
*
* ========step2_4====s1构造完毕后,继续执行3。同样是new一个新的StaticTest对象.
* 过程与step2_1至step2_3一致。
*
* 故,执行3(syso4====k、n、i、j(j=0)均+1;syso5====k、n、i均+1;syso6====k、n、i均+1),
* 运行后变量如下:
*
* k=6; s1=StaticTest对象; s2=StaticTest对象; i=6; n=6; j=1(s2的);
*
* ========step2_5====s2构造完毕后,继续执行4、5的赋值,为i和n赋值.
*
* 故,执行4(syso7====k、n、i均+1,i被重新赋值)、5(n被重新赋值)的赋值代码,运行后变量如下:
*
* k=7; s1=StaticTest对象; s2=StaticTest对象; i=7; n=99; j未声明;
*
* ========step2_6====static变量赋值完毕后,执行8的static块。
*
* 故,执行8(syso8====k、n、i均+1),运行后变量如下:
*
* k=8; s1=StaticTest对象; s2=StaticTest对象; i=8; n=100; j未声明;
*
* ====step2 end====
*/
/**
* ====step3====static变量及块完成后,声明非static变量并给与默认值.
*
* 故,执行6变量的声明。运行后变量如下:
*
* k=8; s1=StaticTest对象; s2=StaticTest对象; i=8; n=100; j=0(main的);
*
* ====step3 end====
*/
/**
* ====step4====非static变量声明完成后,为这些变量赋值以及执行非static块(赋值和块优先级一致,在前面的先执行)。
*
* 故,执行6的赋值代码(syso9====k、n、i均+1,j=++i),7的块代码(syso10====k、n、i均+1)。运行后变量如下 :
*
* k=10; s1=StaticTest对象; s2=StaticTest对象; i=10; n=102; j=9(main的);
*
* ====step4 end====
*/
/**
* ====step5====变量赋值、块执行完毕后,执行构造方法。
*
* 故,执行9构造方法(syso11====k、n、i均+1),运行后变量如下:
*
* k=11; s1=StaticTest对象; s2=StaticTest对象; i=11; n=103; j=9(main的);
*
* ====step5 end====
*/
参考
- Java虚拟机类加载机制 https://blog.csdn.net/u013256816/article/details/50829596
- Java虚拟机类加载机制——案例分析 https://blog.csdn.net/u013256816/article/details/50837863
- Java技术_基础技术(0003)_类执行顺序详解+实例(阿里面试题)+详细讲解+流程图 https://developer.aliyun.com/article/49257
java中类不会被初始化(仅加载)的几种情况:https://blog.csdn.net/CaptHua/article/details/120925452
Java技术_基础技术(0003)_类执行顺序详解+实例(阿里面试题)+详细讲解+流程图的更多相关文章
- Java 学习 时间格式化(SimpleDateFormat)与历法类(Calendar)用法详解
基于Android一些时间创建的基本概念 获取当前时间 方式一: Date date = new Date(); Log.e(TAG, "当前时间="+date); 结果: E/T ...
- Java学习之继承中的执行顺序详解
代码块(理解) (1)用{}括起来的代码. (2)分类: A:局部代码块 用于限定变量的生命周期,及早释放,提高内存利用率. B:构造代码块 把多个构造方法中相同的代码可以放到这里,每个构造方法执行前 ...
- Java的反射基础技术
今天本人给大家讲解一下Java的反射基础技术,如有不对的或者讲的不好的可以多多提出,我会进行相应的更改,先提前感谢提出意见的各位了!!! 什么是反射? 反射它是根据字节码文件可以反射出类的信息.字段. ...
- WCF技术剖析之十七:消息(Message)详解(中篇)
原文:WCF技术剖析之十七:消息(Message)详解(中篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在上篇中 ...
- WCF技术剖析之十七:消息(Message)详解(上篇)
原文:WCF技术剖析之十七:消息(Message)详解(上篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]消息交换 ...
- C++框架_之Qt的信号和槽的详解
C++_之Qt的信号和槽的详解 1.概述 信号槽是 Qt 框架引以为豪的机制之一.所谓信号槽,实际就是观察者模式.当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal ...
- C++框架_之Qt的窗口部件系统的详解-上
C++框架_之Qt的窗口部件系统的详解-上 第一部分概述 第一次建立helloworld程序时,曾看到Qt Creator提供的默认基类只有QMainWindow.QWidget和QDialog三种. ...
- WCF技术剖析之十七:消息(Message)详解(下篇)
原文:WCF技术剖析之十七:消息(Message)详解(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]< ...
- 搞懂分布式技术4:ZAB协议概述与选主流程详解
搞懂分布式技术4:ZAB协议概述与选主流程详解 ZAB协议 ZAB(Zookeeper Atomic Broadcast)协议是专门为zookeeper实现分布式协调功能而设计.zookeeper主要 ...
- 《手把手教你》系列基础篇(七十五)-java+ selenium自动化测试-框架设计基础-TestNG实现DDT - 中篇(详解教程)
1.简介 上一篇中介绍了DataProvider如何传递参数,以及和一些其他方法结合传递参数,今天宏哥接着把剩下的一些常用的也做一下简单的介绍和分享. 2.项目实战1 @DataProvider + ...
随机推荐
- wireshark 怎么过滤字符串 和 复制data中的可读文本
设置 首先点击,[捕获]下面的小放大镜 选择 分组字节流 - 窄(UTF-8/ASCII)- 字符串 (注意,要向选择字符串) 效果 复制报文中的可读数据 右键报文,复制,...as Printabl ...
- 好用工具:Apipost配置环境变量
配置环境 点击小眼睛可查看环境配置 配置环境url api请求
- 2023-08-10:景区里有m个项目,也就是项目数组为int[][] game,这是一个m*2的二维数组 景区的第i个项目有如下两个参数: game[i] = { Ki, Bi } Ki一定是负数,
2023-08-10:景区里有m个项目,也就是项目数组为int[][] game,这是一个m*2的二维数组 景区的第i个项目有如下两个参数: game[i] = { Ki, Bi } Ki一定是负数, ...
- windows和linux键值表
windows系统下对应键值 {8,KEY_BACKSPACE}, {9,KEY_TAB}, {13,KEY_ENTER}, {16,KEY_LEFTSHIFT}, {17,KEY_LEFTCTRL} ...
- [ABC305D] Sleep Log题解
题目大意 给 \(N\) 个时刻: 当 \(i\) 为奇数时,\(A_i\) 表示刚刚起床的时刻. 当 \(i\) 为偶数时,\(A_i\) 表示开始睡觉的时刻. 有 \(Q\) 次询问,每次求在 \ ...
- 【路由器】OpenWrt 配置使用
目录 Web 界面 汉化 root 密码 ssh 升级 LuCI 美化 锐捷认证 MentoHUST MiniEAP 防火墙 开放端口 端口转发 IPv6 USB 安装 USB 驱动 自动挂载 Ext ...
- 关于ChatGPT的一些闲扯淡(1)
这篇写的有点迟了,前者子ChatGPT正火的时候,懒病发作一直拖延.今天对ChatGPT做一个简单的讨论,也是把学习的心得和大家分享一下. 首先什么是GPT,英文全称是Generative Pretr ...
- API接口设计规范,看这篇就足以了
优秀的设计是产品变得卓越的原因.设计API意味着提供有效的接口,可以帮助API使用者更好地了解.使用和集成,同时帮助人们有效地维护它.每个产品都需要使用手册,API也不例外. 在API领域,可以将 ...
- sql-labs--Less-1--Error based-Single quotes
sql="SELECT * FROM users WHERE id='id' LIMIT 0,1"; 打开第一关,我们看到如下界面,上面写着Please input the ID ...
- C++算法之旅、06 基础篇 | 第四章 动态规划 详解
常见问题 闫式DP分析法 状态表示 集合 满足一定条件的所有方案 属性 集合(所有方案)的某种属性(Max.Min.Count等) 状态计算(集合划分) 如何将当前集合划分成多个子集合 状态计算相当于 ...