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. Flask--(项目准备)--框架搭建,参数配置

    项目准备: 配置参数 项目配置: 新建工程: 配置虚拟环境: 通过配置类添加配置参数: Debug配置, 初始化数据库对象, Mysql配置及数据库创建 redis配置: 端口6379和域名: 创建存 ...

  2. 【剑指offer】合并有序链表

    输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则. *思路:假设两个链表的当前结点为n1(list1),n2(list2)比较链表结点值的大小,如果n1.va ...

  3. Substring方法(C#,JS,Java,SQL)的区别

    C#: substring(第一参数,第二参数)//  第一参数:从第几位开始截,初始是从0位开始  第二参数:截取几位 substring(参数)  如果传入参数为一个长整, 且大于等于0,则以这个 ...

  4. Java中的权限修饰符private、protected、public

    java中的修饰符分类: 权限修饰符: private, default, protected, public 状态修饰符: static, final 抽象修饰符: abstract 权限修饰符 我 ...

  5. 20165312 2017-2018-2《JAVA程序设计》第7周学习总结

    20165312 2017-2018-2<JAVA程序设计>第7周学习总结 一.对上周测试的查漏补缺 总的来说,我觉得上周两个测试都挺难的,做测试也花费了很长的时间,我认为是因为书上的知识 ...

  6. Chapter 5 数组:为什么很多编程语言种数组都是从0开始编号?

    如何实现随机访问? 线性表:数组,队列,链表,栈 非线性表:树,图 总结:数组用一块连续的内存空间,来存储相同类型的一组数据,最大的特点就是支持随机访问,但插入,删除操作也因此变得比较低效,平均情况时 ...

  7. java反编译工具cfr

    http://www.benf.org/other/cfr/ 下载反编译工具 @echo off SET home=%cd% rem 指定存放jar包的目录 SET jar_dir=%home%\ja ...

  8. leetcode56

    public class Solution { public IList<Interval> Merge(IList<Interval> intervals) { var le ...

  9. ==、equals和hashCode小结

    1.== ==是关系操作符,对于基本类型(byte,short,char,int,long,float,double,boolean),比较的是值是否相等:对于对象,比较的是对象的引用(也即栈内存中的 ...

  10. iptables随笔

    iptables 分为四表五链 四表: filter表 nat 表 mangle 表 raw 表 五链 INPUT 链 OUTPUT 链 FORWARD 链 PREROUTING(路由前) POSTR ...