每日一问:Final,Static,Volatile修饰变量加载顺序
我已经11天没写东西了,此时的我正在吃着泡面,写这篇开篇文章,2020年的年初计划等年后再写了,其实我想过在此期间写点东西,什么精通阿里巴巴开发手册,Netty聊天室等等,都没能静下心来去写。一是长时间没看新东西,找不到能写的东西,二是越不写,越懒得写。
我觉得都能在小伙伴的提问下写一个每日一问了,没素材的时候就从群里看,如下:
package com.kk;
/**
* @author zhaokk
* @create 2020-01-09-19:28
*/
public class Price {
public static final Price INSTANCE = new Price(12);
private static int staticPrice = 5;
public int todayPrice = 20;
public Price(int price) {
todayPrice = price - staticPrice;
}
public static void main(String[] args) {
System.out.println(Price.INSTANCE.todayPrice);
}
}
表面分析一下对象实例化传入参数12,price=12,静态变量初始化5,那todayprice非静态还未加载,就是12-5=7么,非也非也!
运行结果:
D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\bin\java.exe -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:50834,suspend=y,server=n -javaagent:C:\Users\14620\.IntelliJIdea2019.1\system\captureAgent\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath "D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\charsets.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\deploy.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\ext\access-bridge-64.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\ext\cldrdata.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\ext\dnsns.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\ext\jaccess.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\ext\jfxrt.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\ext\localedata.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\ext\nashorn.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\ext\sunec.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\ext\sunjce_provider.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\ext\sunmscapi.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\ext\sunpkcs11.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\ext\zipfs.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\javaws.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\jce.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\jfr.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\jfxswt.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\jsse.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\management-agent.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\plugin.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\resources.jar;D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\jre\lib\rt.jar;D:\project01\target\classes;C:\Users\14620\.m2\repository\commons-logging\commons-logging\1.1.3\commons-logging-1.1.3.jar;C:\Users\14620\.m2\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;C:\Users\14620\.m2\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;C:\Users\14620\.m2\repository\org\slf4j\slf4j-log4j12\1.7.7\slf4j-log4j12-1.7.7.jar;C:\Users\14620\.m2\repository\ch\qos\logback\logback-classic\1.1.7\logback-classic-1.1.7.jar;C:\Users\14620\.m2\repository\ch\qos\logback\logback-core\1.1.7\logback-core-1.1.7.jar;C:\Users\14620\.m2\repository\ch\qos\logback\logback-access\1.1.7\logback-access-1.1.7.jar;C:\Users\14620\.m2\repository\org\apache\logging\log4j\log4j-core\2.5\log4j-core-2.5.jar;C:\Users\14620\.m2\repository\org\apache\logging\log4j\log4j-api\2.5\log4j-api-2.5.jar;C:\Users\14620\.m2\repository\junit\junit\4.12\junit-4.12.jar;C:\Users\14620\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;D:\idea\IntelliJ IDEA 2019.1.1\lib\idea_rt.jar" com.kk.Price
Connected to the target VM, address: '127.0.0.1:50834', transport: 'socket'
12
Disconnected from the target VM, address: '127.0.0.1:50834', transport: 'socket'
Process finished with exit code 0
那么为什么staticPirce加载没有被赋值呢?
惯例,看下字节码执行顺序
我先吃口泡面…Ten minutes later
D:\ProgramFiles\JavaTest\java\jdk1.8.0_141\bin\javap.exe -c com.kk.Price
Compiled from "Price.java"
public class com.kk.Price {
public static final com.kk.Price INSTANCE;
public int todayPrice;
public com.kk.Price(int);
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 20
7: putfield #2 // Field todayPrice:I
10: aload_0
11: iload_1
12: getstatic #3 // Field staticPrice:I
15: isub
16: putfield #2 // Field todayPrice:I
19: return
public static void main(java.lang.String[]);
Code:
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #5 // Field INSTANCE:Lcom/kk/Price;
6: getfield #2 // Field todayPrice:I
9: invokevirtual #6 // Method java/io/PrintStream.println:(I)V
12: return
static {};
Code:
0: new #7 // class com/kk/Price
3: dup
4: bipush 12
6: invokespecial #8 // Method "<init>":(I)V
9: putstatic #5 // Field INSTANCE:Lcom/kk/Price;
12: iconst_5
13: putstatic #3 // Field staticPrice:I
16: return
}
Process finished with exit code 0
main方法执行了0.3.6.9.12
0:打印流
3:对象实例化
6:get todayprice字段
9:打印
12:返回
就是说此时并没有对staticprice进行赋值,结果为12;
同样是静态变量,为什么没被加载到呢?
构造函数先调用父类的构造函数,也就是Price,虽然staticprice进行了赋值,但是直到构造函数被执行时才被赋值,而此时元素加载到栈时是aload0,读取到0,就执行构造方法去了,所以结果是12。
那么final修饰呢?
private static final int staticPrice
结果就是7了呢?
final会在字节码中打上ACC_FINAL标签,在运行时会进行处理和优化,使变量直接等于指,并且与构造函数一起赋值,
那么加volatile呢?
private static volatile int staticPrice
javap -verbose Price.class
D:\project01\target\classes\com\kk>javap -verbose Price.class
Classfile /D:/project01/target/classes/com/kk/Price.class
Last modified 2020-1-9; size 723 bytes
MD5 checksum 3877a8ac15a71a5bef54d6e42eae01ea
Compiled from "Price.java"
public class com.kk.Price
minor version: 0
major version: 49
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #9.#30 // java/lang/Object."<init>":()V
#2 = Fieldref #7.#31 // com/kk/Price.todayPrice:I
#3 = Fieldref #7.#32 // com/kk/Price.staticPrice:I
#4 = Fieldref #33.#34 // java/lang/System.out:Ljava/io/PrintStream;
#5 = Fieldref #7.#35 // com/kk/Price.INSTANCE:Lcom/kk/Price;
#6 = Methodref #36.#37 // java/io/PrintStream.println:(I)V
#7 = Class #38 // com/kk/Price
#8 = Methodref #7.#39 // com/kk/Price."<init>":(I)V
#9 = Class #40 // java/lang/Object
#10 = Utf8 INSTANCE
#11 = Utf8 Lcom/kk/Price;
#12 = Utf8 staticPrice
#13 = Utf8 I
#14 = Utf8 todayPrice
#15 = Utf8 <init>
#16 = Utf8 (I)V
#17 = Utf8 Code
#18 = Utf8 LineNumberTable
#19 = Utf8 LocalVariableTable
#20 = Utf8 this
#21 = Utf8 price
#22 = Utf8 main
#23 = Utf8 ([Ljava/lang/String;)V
#24 = Utf8 args
#25 = Utf8 [Ljava/lang/String;
#26 = Utf8 <clinit>
#27 = Utf8 ()V
#28 = Utf8 SourceFile
#29 = Utf8 Price.java
#30 = NameAndType #15:#27 // "<init>":()V
#31 = NameAndType #14:#13 // todayPrice:I
#32 = NameAndType #12:#13 // staticPrice:I
#33 = Class #41 // java/lang/System
#34 = NameAndType #42:#43 // out:Ljava/io/PrintStream;
#35 = NameAndType #10:#11 // INSTANCE:Lcom/kk/Price;
#36 = Class #44 // java/io/PrintStream
#37 = NameAndType #45:#16 // println:(I)V
#38 = Utf8 com/kk/Price
#39 = NameAndType #15:#16 // "<init>":(I)V
#40 = Utf8 java/lang/Object
#41 = Utf8 java/lang/System
#42 = Utf8 out
#43 = Utf8 Ljava/io/PrintStream;
#44 = Utf8 java/io/PrintStream
#45 = Utf8 println
{
public static final com.kk.Price INSTANCE;
descriptor: Lcom/kk/Price;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
public int todayPrice;
descriptor: I
flags: ACC_PUBLIC
public com.kk.Price(int);
descriptor: (I)V
flags: ACC_PUBLIC
Code:
stack=3, locals=2, args_size=2
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 20
7: putfield #2 // Field todayPrice:I
10: aload_0
11: iload_1
12: getstatic #3 // Field staticPrice:I
15: isub
16: putfield #2 // Field todayPrice:I
19: return
LineNumberTable:
line 15: 0
line 13: 4
line 16: 10
line 18: 19
LocalVariableTable:
Start Length Slot Name Signature
0 20 0 this Lcom/kk/Price;
0 20 1 price I
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #5 // Field INSTANCE:Lcom/kk/Price;
6: getfield #2 // Field todayPrice:I
9: invokevirtual #6 // Method java/io/PrintStream.println:(I)V
12: return
LineNumberTable:
line 21: 0
line 22: 12
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 args [Ljava/lang/String;
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=3, locals=0, args_size=0
0: new #7 // class com/kk/Price
3: dup
4: bipush 12
6: invokespecial #8 // Method "<init>":(I)V
9: putstatic #5 // Field INSTANCE:Lcom/kk/Price;
12: iconst_5
13: putstatic #3 // Field staticPrice:I
16: return
LineNumberTable:
line 9: 0
line 11: 12
}
SourceFile: "Price.java"
volatile 只是保证从主内存加载到线程工作内存的值是最新的,此时并未加载并刷新到主内存中(#12 才读取到staticprice)就被读走了,结果还是12。
- 点赞
- 收藏
- 关注作者
评论(0)