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 插件中直接下载)

JVM 字节码(四)静态方法、构造代码、this 以及 synchronized 关键字的更多相关文章

  1. Java finally语句到底是在return之前还是之后执行(JVM字节码分析及内部体系结构)?

    之前看了一篇关于"Java finally语句到底是在return之前还是之后执行?"这样的博客,看到兴致处,突然博客里的一个测试用例让我产生了疑惑. 测试用例如下: public ...

  2. JVM总结(五):JVM字节码执行引擎

    JVM字节码执行引擎 运行时栈帧结构 局部变量表 操作数栈 动态连接 方法返回地址 附加信息 方法调用 解析 分派 –“重载”和“重写”的实现 静态分派 动态分派 单分派和多分派 JVM动态分派的实现 ...

  3. JVM 字节码执行实例分析

    前言 最近在看<Java 虚拟机规范>和<深入理解JVM虚拟机>,对于字节码的执行有了进一步的了解.字节码就像是汇编语言,是 JVM 的指令集.下面我们先对 JVM 执行引擎做 ...

  4. JVM 字节码(二)方法表详解

    JVM 字节码(二)方法表和属性表 上一节中对 ClassFile 的整体进行了五个详细的说明, 本节围绕 ClassFile 最重要的一个内容 - 方法表的 Code 属性展开 ,更多 JVM Me ...

  5. JVM 字节码指令手册 - 查看 Java 字节码

    JVM 字节码指令手册 - 查看 Java 字节码 jdk 进行的编译生成的 .class 是 16 进制数据文件,不利于学习分析.通过下命令 javap -c Demo.class > Dem ...

  6. 一夜搞懂 | JVM 字节码执行引擎

    前言 本文已经收录到我的 Github 个人博客,欢迎大佬们光临寒舍: 我的 GIthub 博客 学习导图 一.为什么要学习字节码执行引擎? 代码编译的结果从本地机器码转变为字节码,是存储格式发展的一 ...

  7. 从jvm字节码指令看i=i++和i=++i的区别

    1. 场景的产生 先来看下下面代码展示的两个场景 @Testvoid testIPP() { int i = 0; for (int j = 0; j < 10; j++) { i = i++; ...

  8. 从JVM字节码执行看重载和重写

    Java 重写(Override)与重载(Overload) 重写(Override) 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变.即外壳不变,核心重写! 重写的 ...

  9. JVM 字节码(三)异常在字节码中的处理(catch 和 throws)

    JVM 字节码(三)异常在字节码中的处理(catch 和 throws) 在 ClassFile 中到底是如何处理异常的呢? 一.代码块异常 catch catch 中的异常代码块在异常是如何处理的呢 ...

随机推荐

  1. ADS1.2和JlinkV8 erro starting external process,Process error code 87(0x57)参数错误

    ADS1.2和JlinkV8  erro starting external process,Process error code 87(0x57)参数错误 网上的大致说法是说这个跟W7有关 说是将I ...

  2. Ubuntu 16.04 LTS 常用快捷键

    在Linux下Win键就是Super键 启动器 Win(长按) 打开启动器,显示快捷键 Win + Tab 通过启动器切换应用程序 Win + 1到9 与点击启动器上的图标效果一样 Win + Shi ...

  3. C++Primer第五版——习题答案详解(八)

    习题答案目录:https://www.cnblogs.com/Mered1th/p/10485695.html 第9章 顺序容器 练习9.1 a.list,需要按字典序插入,可能插入位置在中间 b.d ...

  4. 知识点:Mysql 基本用法之存储过程

    存储过程 一. 介绍 存储过程包含了一系列可执行的sql语句,存储过程存放于MySQL中,通过调用它的名字可以执行其内部的一堆sql 使用存储过程的优点: 用于替代程序写的SQL语句,实现程序与sql ...

  5. Pascal语言(存档)

    数据类型 标准函数 运算符和表达式 输入语句 输出语句 if语句 case语句 for语句 while语句 repeat语句 函数与过程 形参与实参 全局变量与局部变量 数组 字符串 枚举 子界 集合 ...

  6. (转)EF5+SQLserver2012迁移到EF6+mysql5.5.47

    原文地址:https://www.cnblogs.com/tinyjian/p/6235014.html:https://www.cnblogs.com/tinyjian/p/6235397.html ...

  7. day28元类与异常查找

    元类与异常处理1. 什么是异常处理    异常是错误发生的信号,一旦程序出错就会产生一个异常,如果该异常    没有被应用程序处理,那么该异常就会抛出来,程序的执行也随之终止    异常包含三个部分: ...

  8. 浅读《视觉SLAM十四讲:从理论到实践》--操作1--初识SLAM

    下载<视觉SLAM十四讲:从理论到实践>源码:https://github.com/gaoxiang12/slambook 第二讲:初识SLAM 2.4.2 Hello SLAM(书本P2 ...

  9. org.apache.ibatis.builder.IncompleteElementException: Could not find result map java.util.HashMap

    这样的配置有问题吗? <select id="getFreightCollectManagementList" resultMap="java.util.HashM ...

  10. JDK1.7 的 HashMap

    HashMap是一个用于存储key-value的键值对集合,每个键值对都是一个Entry.这些键值对分散存储在一个数组中,这个数组就是HashMap的主干. HashMap每个初始值都为null. 1 ...