idea java文件显示j_IDEA下如何查看字节码?

我们都知道编写java程序是要先安装jdk, 要知道 jdk与 java源文件 之间的执行原理,就需要理解字节码了。

本篇文章教大家IDEA中查看字节码的三个方法 以及 jdk对字符串拼接、自动装箱和拆箱的操作过程。

首先要知道jdk、jre、jvm三者之间的关系:

f2d7326f03e74f6df1bd8b416912f639.png

JDK:Java 开发工具包,同时还包含了编译java源码的编译器javac,供开发使用。
JRE:Java 运行时环境,一些基本类库,供运行使用。
JVM:Java 虚拟机。它只认识 xxx.class 这种类型的文件,它能够将 class 文件中的字节码指令进行识别并针对不同操作系统向上的 API 完成动作。

Java程序的大致执行流程如下:

aa57490e3f89fe69b8205177b37813c5.png
java程序执行流程

所以说,jvm 是 Java 能够跨平台的核心。

新建一个Test.java 文件,并编译。下面进入正题,介绍三种方法查看 字节码。

1、使用 JDK 自带 的 javap

javap是jdk自带的一个反汇编工具,可用于查看编译后的字节码。

在编译完成后,定位到你的 .class 文件

b94d53326109e9a786d71e82da979045.png

但是要看这个字节码,就很麻烦,需要先编译。每次都要找到这个.class文件,然后输入 javap -c xxx.class

那有什么便捷的方法?

当然是有的。

打开 Settings ——》External Tools  

dfd9641787cc7bfef4dc021c4d2df117.png

name:插件的名字,我写了 MyjavapProgram:插件路径,我这里是 E:\JDK1.8\bin\javap.exe,按照你本地路径写即可。Arguments:参数,直接写 -c $FileNameWithoutExtension$.classWorking directory:class的输出目录,直接写 $OutputPath$\$FileDirRelativeToSourcepath$

点击 OK ,然后选中 你的 .java 源文件,点击 Tools——》External Tools ——》Myjavap

6e62158f1685c991a6f943631ddf2f89.png

以上就可以很方便地看到控制台输出了字节码。

2、IDEA 自带 show byteCode

点击一下你的 .java 源文件 ,然后 点击菜单栏  View ——》Show byteCode

0f6caa5462a1efbbd62d5d737e59793e.png

然后就会弹出一个字节码的窗口。

3、jclasslib 插件

在插件市场搜索 jclasslib,点击安装。

466bffb418c4170149eec85efd555bab.png

重启IDEA。

菜单栏 View——》Show Bytecode With jclasslib

48b1f41d9e833655fad1f8beef5c9b64.png

字节码含义

下面说一下字节码的含义。

更多字节码的指令解释可参考:https://blog.csdn.net/qq_31407255/article/details/88978630

我本地的 Test.java :

public class Test {
    public static void main(String[] args) {
        String a = "I am ";
        String b = "HaC";
        String c = a + b;
        String d = "I am " + "HaC";
        System.out.println(c == d); //false
        Integer i =100; //装箱
        int j = i;  //拆箱
        System.out.println(i == j); //true
    }
}

这个是使用jclasslib的字节码:

 0 ldc #2        # JVM采用ldc指令将常量压入栈中
 2 astore_1            # 将栈顶引用类型值保存到局部变量1中,即a
 3 ldc #3 
 5 astore_2
 6 new #4         # 创建新的对象实例,即c
 9 dup                                  # 复制栈顶一个字长的数据,将复制后的数据压栈。
10 invokespecial #5 > # 编译时方法绑定调用方法,编译时调用StringBuilder,相当于 new StringBuilder("I am "),下同
13 aload_1                                         # 从局部变量1,即对new StringBuilder("I am ")的引用,装载引用类型值入栈。
14 invokevirtual #6  # 这里调用StringBuilder的appen方法,运行时进行拼接
17 aload_2
18 invokevirtual #6 
21 invokevirtual #7     # 最后调用 toString方法,即 "I am HaC".toString
24 astore_3                                         
25 ldc #8       # 这里jvm直接优化了,把  d 变成了"I am HaC"
27 astore 4
29 getstatic #9 
32 aload_3
33 aload 4
35 if_acmpne 42 (+7)
38 iconst_1
39 goto 43 (+4)
42 iconst_0
43 invokevirtual #10 
46 bipush 100
48 invokestatic #11         # Integer i =100;装箱,即等于Integer i =Integer.valueOf(100);
51 astore 5
53 aload 5
55 invokevirtual #12    # int j = i; 拆箱,即等于 int j = i.intValue();
58 istore 6
60 getstatic #9 
63 aload 5
65 invokevirtual #12   # Integer和int比较, Integer自动调用intValue方法,即拆箱
68 iload 6
70 if_icmpne 77 (+7)
73 iconst_1
74 goto 78 (+4)
77 iconst_0
78 invokevirtual #10 
81 return

所以说,

String c = a + b;
String d = "I am " + "HaC";
System.out.println(c == d); //false

c 是使用了 StringBuilder 进行拼接,最后返回的是堆的内存引用,而 d 是 jvm优化后,直接指向常量池的 "I am HaC",两者是不相等的。

而当 Integer对象 和 int 基本类型 比较的时候,Integer会自动拆箱,转化成 int 类型,比较的是值,两者自然相等。

 Integer i =100; //装箱
 int j = i;  //拆箱
 System.out.println(i == j); //true

--end--

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值