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. 前段开发神奇webstorm安装注册和汉化

    软件下载地址: http://www.jetbrains.com/webstorm/ 安装完后退出. 重新打开,进行激活 这里我们选择“license server”然后输入:http://idea. ...

  2. Python判断语句

    什么是判断语句 如果 今天是周六或者周日: 约妹子 如果 今天是情人节: 买玫瑰 如果 今天发工资: 先还信用卡的钱 如果 有剩余: 又可以happy了,O(∩_∩)O哈哈~ 否则: 啊啊啊啊,等30 ...

  3. BCC校验小知识

    BCC校验其实是奇偶校验的一种,但也是经常使用并且效率较高的一种.所谓BCC校验法,就是在发送前和发送后分别把BCC以前包括ETX字符的所有字符按位异或后,按要求变换(增加或去除一个固定的值)后所得到 ...

  4. 一次Windows 安装问题

    在 thinpad x250上安装 windows 10时,提示"安装程序无法创建新的系统分区,也无法定位现有系统分区". x250 的主板上自带一个16G的闪存且标识为 主分区, ...

  5. phpcms基础循环

    lists循环{pc:content action="lists" catid="2" order="id DESC" num=" ...

  6. 使用Oracle DBLink进行数据库之间对象的访问操作

    Oracle中自带了DBLink功能,它的作用是将多个oracle数据库逻辑上看成一个数据库,也就是说在一个数据库中可以操作另一个数据库中的对象,例如我们新建了一个数据database1,我们需要操作 ...

  7. C#委托防止事件多次注册

    示例代码如下: class NodeInf { public delegate void mydelegate(ProcessContent processContent); private myde ...

  8. C#获取当前路径的七种方法

    //1.获取模块的完整路径. string path1 = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName; // ...

  9. VIVADO 入门之仿真与逻辑分析仪使用

    多路分频器设计 在第七节的学习中,笔者带大家通过一个入门必学的流水灯实验实现,快速掌握了VIVADO基于FPGA开发板的基本流程.考虑到很多初学者并没有掌握好Vivado 下FPGA的开发流程,本章开 ...

  10. Tomcat 配置详解和优化

    2018年01月09日 18:14:41 tianxiaojun2014 阅读数:306   转自:https://www.cnblogs.com/xbq8080/p/6417671.html htt ...