zoukankan      html  css  js  c++  java
  • JVM 字节码(四)静态方法、构造代码、this 以及 synchronized 关键字

    JVM 字节码(四)静态方法、构造代码、this 以及 synchronized 关键字

    一、静态代码

    public class ByteCodeStatic {
        private static final String DEFAULT_VALUE = "default_value";
        private static String str = DEFAULT_VALUE;
        static {
            System.out.println("hello");
        }
    }
    

    编译后查看对应的字节码,生成了两个方法

    • <cinit> 静态赋值和静态代码块的集合,执行顺序和代码一致。注意不包含常量的赋值。
    • <init> 构造方法,包含普通变量值赋值和构造函数。

    cinit 的代码块如下,不包含常量 DEFAULT_VALUE 的赋值,这是通过常量值(ConstantValue)进行赋值的。

     0 ldc #3 <default_value>
     2 putstatic #4 <com/github/binarylei/jvm/bytecode/ByteCodeStatic.str>
     5 getstatic #5 <java/lang/System.out>
     8 ldc #6 <hello>
    10 invokevirtual #7 <java/io/PrintStream.println>
    13 return
    

    但常量就一定是通过 ConstantValue 赋值吗?如果编译期无法确定常量的值那也是需要通过静态代码块来赋值的,也就是会出现在 cinit 代码中。

    public class ByteCodeStatic {
        private static final String DEFAULT_VALUE = new String("default_value");
        private static String str = DEFAULT_VALUE;
        static {
            System.out.println("hello");
        }
    }
    

    将 DEFAULT_VALUE 的值修改为一个对象,这个对象需要在运行期才能确定值,重新编译后的指定集如,很明显出现了常量的赋值语句(前五个语句):

     0 new #2 <java/lang/String>
     3 dup
     4 ldc #3 <default_value>
     6 invokespecial #4 <java/lang/String.<init>>
     9 putstatic #5 <com/github/binarylei/jvm/bytecode/ByteCodeStatic.DEFAULT_VALUE>
    12 getstatic #5 <com/github/binarylei/jvm/bytecode/ByteCodeStatic.DEFAULT_VALUE>
    15 putstatic #6 <com/github/binarylei/jvm/bytecode/ByteCodeStatic.str>
    18 getstatic #7 <java/lang/System.out>
    21 ldc #8 <hello>
    23 invokevirtual #9 <java/io/PrintStream.println>
    26 return
    

    二、构造方法

    public class ByteCodeConstructor {
    
        private String str = "binarylei";
    
        {
            System.out.println("hello");
        }
    
        public ByteCodeConstructor() {
        }
    
        public ByteCodeConstructor(String str) {
            this.str = str;
        }
    }
    

    编译后查看对应的字节码,生成了两个 构造方法。

    第一个构造器:

     0 aload_0                                          // this 参数(所有的非静态方法都包含这个参数)
     1 invokespecial #1 <java/lang/Object.<init>>       // 执行父类构造方法
     4 aload_0
     5 ldc #2 <binarylei>                               // 加载字符串 binarylei
     7 putfield #3 <com/github/binarylei/jvm/bytecode/ByteCodeConstructor.str>  // str 赋值
    10 getstatic #4 <java/lang/System.out>
    13 ldc #5 <hello>                               
    15 invokevirtual #6 <java/io/PrintStream.println>   // System.out.println("hello");
    18 return
    

    第二个构造器:

     0 aload_0
     1 invokespecial #1 <java/lang/Object.<init>>
     4 aload_0
     5 ldc #2 <binarylei>
     7 putfield #3 <com/github/binarylei/jvm/bytecode/ByteCodeConstructor.str>
    10 getstatic #4 <java/lang/System.out>
    13 ldc #5 <hello>
    15 invokevirtual #6 <java/io/PrintStream.println>
    
    18 aload_0                  // this 参数(所有的非静态方法都包含这个参数)
    19 aload_1                  // str 参数
    20 putfield #3 <com/github/binarylei/jvm/bytecode/ByteCodeConstructor.str>
    23 return
    

    总结: 构造方法会将普通常量和普通代码块整合到其构造器字节块中,每个构造方法都会拼凑一份。

    三、this 参数

    对于 Java 类中的每一个实例方法(非 static 方法),其在编译后所生成的字节码中,方法参数的数量总是会比源代码中方法的数量多一个(this),它位于方法的第一个参数位置。 这样,我们可以在 Java 的实例方法中使用 this 来去访问当前对象的属性以及其方法。

    这个操作是在编译期间完成的,即由 Javac 编译器在编译的时候对 this 的访问转化为一个普通实例方法参数的访问;接下来在运行期由 JVM 在调用实例方法时,自动向实例方法传入该 this 参数,所以,在实例方法的局部变量表中,至少会有一个指向当前对象的局部变量。

    四、synchronized

    public void test2() {
        synchronized (this) {
        }
    }
    

    编译后字节码如下:

     0 aload_0
     1 dup
     2 astore_1
     3 monitorenter
     4 aload_1
     5 monitorexit
     6 goto 14 (+8)
     9 astore_2
    10 aload_1
    11 monitorexit
    12 aload_2
    13 athrow
    14 return
    

    monitorenter 和 monitorexit 获取锁和释放锁

    参考:

    1. 周志明,深入理解Java虚拟机 - 第 6 章:类文件结构
    2. Java 反编译工具 - jclasslib(比 javap -v 信息更详细,可以在 IDEA 插件中直接下载)
  • 相关阅读:
    PAT (Advanced Level) Practice 1055 The World's Richest (25 分) (结构体排序)
    PAT (Advanced Level) Practice 1036 Boys vs Girls (25 分)
    PAT (Advanced Level) Practice 1028 List Sorting (25 分) (自定义排序)
    PAT (Advanced Level) Practice 1035 Password (20 分)
    PAT (Advanced Level) Practice 1019 General Palindromic Number (20 分) (进制转换,回文数)
    PAT (Advanced Level) Practice 1120 Friend Numbers (20 分) (set)
    从零开始吧
    Python GUI编程(TKinter)(简易计算器)
    PAT 基础编程题目集 6-7 统计某类完全平方数 (20 分)
    PAT (Advanced Level) Practice 1152 Google Recruitment (20 分)
  • 原文地址:https://www.cnblogs.com/binarylei/p/10519948.html
Copyright © 2011-2022 走看看