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

举报
赵KK日常技术记录 发表于 2023/06/24 13:37:45 2023/06/24
【摘要】 我已经11天没写东西了,此时的我正在吃着泡面,写这篇开篇文章,2020年的年初计划等年后再写了,其实我想过在此期间写点东西,什么精通阿里巴巴开发手册,Netty聊天室等等,都没能静下心来去写。一是长时间没看新东西,找不到能写的东西,二是越不写,越懒得写。 我觉得都能在小伙伴的提问下写一个每日一问了,没素材的时候就从群里看,如下:package com.kk;/** * @author ...

我已经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。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。