前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >每日一问:Final,Static,Volatile修饰变量加载顺序

每日一问:Final,Static,Volatile修饰变量加载顺序

作者头像
疯狂的KK
发布2020-01-17 15:32:31
1.1K0
发布2020-01-17 15:32:31
举报
文章被收录于专栏:Java项目实战

我已经11天没写东西了,此时的我正在吃着泡面,写这篇开篇文章,2020年的年初计划等年后再写了,其实我想过在此期间写点东西,什么精通阿里巴巴开发手册,Netty聊天室等等,都没能静下心来去写。一是长时间没看新东西,找不到能写的东西,二是越不写,越懒得写。

我觉得都能在小伙伴的提问下写一个每日一问了,没素材的时候就从群里看,如下:

代码语言:javascript
复制
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么,非也非也!

运行结果:

代码语言:javascript
复制
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

代码语言:javascript
复制
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修饰呢?

代码语言:javascript
复制
private static final int staticPrice

结果就是7了呢?

final会在字节码中打上ACC_FINAL标签,在运行时会进行处理和优化,使变量直接等于指,并且与构造函数一起赋值,

那么加volatile呢?

private static volatile int staticPrice

代码语言:javascript
复制
javap -verbose Price.class
代码语言:javascript
复制
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。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-01-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 赵KK日常技术记录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档