JAVA 字节码全解

1:IDEA 如何查看字节码文件

对于 IDEA 查看字节码,有人喜欢每次使用的时候在命令行敲命令,我觉得比较麻烦,于是添加工具组来实现:
参考:https://blog.csdn.net/xqt1028/article/details/106366375

1.1:通过File–>Settings… 打开 External Tools 窗口

在这里插入图片描述

1.2:点击图中的“+”,填写内容

在这里插入图片描述
Program:javap.exe的路径,这里要使用绝对路径(jdk安装包的bin目录下)。
Arguments:-v F i l e N a m e W i t h o u t E x t e n s i o n FileNameWithoutExtension FileNameWithoutExtension.class
Working directory: O u t p u t P a t h OutputPath OutputPath/ F i l e D i r R e l a t i v e T o S o u r c e p a t h FileDirRelativeToSourcepath FileDirRelativeToSourcepath

1.3:选择一个java文件,右键选择 External Tools–>查看字节码(这是自己定义的名),即可查看字节码。

2:查看字节码文件(需要 jvm 基础)

虚拟机栈中是一个个栈帧,对应着调用的一个个 java 方法,但是栈帧中还是有内容的,可以分成以下几部分:

  • Local Variable Table:本地变量(局部变量)表,方法内部使用的,参数也算在内,以变量槽为最小单位,每个槽32位的内存空间。
    局部变量表主要用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括各类原始数据类型、对象引用(reference),以及returnAddress类型。 局部变量表所需的容量大小在编译期就可以被完全确定下来,并保存在方法的Code属性中。
  • Operand Stack:操作数栈(表达式栈)
    对于long的处理(store and load),多数虚拟机的实现都是原子的
    局部变量,没必要加volatile,线程私有的。
    操作数栈所需的容量大小在编译期就可以被完全确定下来,并保存在方法的Code属性中。通过标准的出入栈完成数据访问
    32位数据类型所占的栈容量为1,64位数据类型所占的栈容量为2。
  • Dynamic Linking:动态链接,指向常量池的符号链接,如果没有解析,就去动态解析
  • return address:返回值地址
    a() -> b(),方法a调用了方法b, b方法的返回值放在什么地方

2.1:基本的字节码

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}

在这里插入图片描述
在这里插入图片描述
注意:字节码的一些指令需要自己去了解学习,这里不再花费大的篇幅去介绍。

2.2:带有异常的字节码

public class Demo3_11_1 {

    public static void main(String[] args) {
        int i = 0;
        try {
            i = 10;
        } catch (Exception e) {
            i = 20;
        }
    }
}

我们主要查看 main 方法对应的字节码;
在这里插入图片描述

2.3:带有 finally 块

public class Demo3_11_4 {

    public static void main(String[] args) {
        int i = 0;
        try {
            i = 10;
        } catch (Exception e) {
            i = 20;
        } finally {
            i = 30;
        }
    }
}

在这里插入图片描述

2.4:了解具体的字节码指令含义

可以把指令复制到网页上进行查询,https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5

3:语法糖

所谓的语法糖,指java编译器把.java源代码编译为.class字节码的过程中,自动生成和转换的一些代码,主要是为了减轻程序员的负担,算是java编译器我们的一个额外福利.

这里不再一一展示,大概列举一下:

1:默认构造器:编译器加上的,调用父类object的无参构造方法,前提是自己没有自定义构造器      
2:自动拆装箱:编译器给自动转化        
3:泛型集合取值:编译泛型会执行泛型擦除的动作,泛型信息消失,被当做object来处理.所以,在取值时,编译器会进行类型转 泛型擦除在字节码是看到的,但是在本地类型方法表还是可以看到的     
4:可变参数: String ... args 编译后是 string[] arges ,如果不传参数,会生成空数组,而不是 null     
5:foreach循环:数组foreach会被编译成for循环,集合foreach会被编译成iterator遍历        
6:swith字符串:变量不能为null,字符串比较的是hashcode码和equals()方法,先比较hashcode是为了提高效率,减少可能的比较,equals()是为了防止hash碰撞. 同时执行了两遍swith,第一次是根据字符串的hashcode和equals()将字符串转换为相应的byte类型,第二遍才是利用byte类型进行比较.       
7:swith枚举类:变量不能为null,会定义一个合成类,对jvm可见,对我们不可见.用来映射枚举的 ordinal 和数组元素的关系,枚举的ordinal 表示枚举对象的序号,从0开始.生成一个整形数组,存储枚举对象的序号.真正去swith的时候,实际上是对数组元素进行匹配.       
8:枚举类:编译后也是一个类,将枚举元素放在一个数组中.        
9:try-with-resources:简化资源关闭.其中,资源对象需要实现AutoCloseable接口,例如,InputStream,OutputStream,Connection,Statement, ResultSet等接口,使用try-with-resources可以不用写finally语句块,编译器会帮助生成关闭资源代码. 内外层异常信息都不会丢失,异常捕捉方式值得我们学习.     
10:方法重写时的桥接方法: 方法重写对返回值分两种情况:1是父子类的返回值完全一致 2是子类返回值是父类返回值的子类. 编译器会新建一个方法,合成方法,对程序员不可见.       
11:匿名内部类:生成一个新类,会有构造器,传值要求final修饰局部变量.   
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值