Java 是一个跨平台的语言,支撑跨平台的根基是: JVM不是跨平台的, 但是字节码文件(.class)是一致的。
Java规范分为Java语言规范(集中在语言的使用层面)和Java虚拟机规范(更为底层的层面),字节码是Java虚拟机规范的一部分。Java虚拟机上衍生了多种语言比如:Scala、Groovy、Kotlin等,语言是不同的但是编译出来的字节码是一致的 都是符合虚拟机规范的。
字节码的用处:
可以根源上对深层次的问题会有深刻的认识,而且认识的准确性会很高,比如synchronized关键字、volatile关键字。
可以很好的理解反编译工具的工作机制.class->.java。
反编译工具: javap.exe (JDK自带的)
Command Line:
javap the desired class(全限定名)
source
package com.compass.spring_lecture.binarycode;
public class MyTest1 {
private int a = 1;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
}
反编译:javap MyTest1(.class) 信息比较少
public class com.compass.spring_lecture.binarycode.MyTest1 {
public com.compass.spring_lecture.binarycode.MyTest1();
public int getA();
public void setA(int);
}
反编译: javap -c MyTest(.class) 包含大量的信息(如助记符:Code、invokespecial等)
public class com.compass.spring_lecture.binarycode.MyTest1 {
public com.compass.spring_lecture.binarycode.MyTest1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_1
6: putfield #2 // Field a:I
9: return
public int getA();
Code:
0: aload_0
1: getfield #2 // Field a:I
4: ireturn
public void setA(int);
Code:
0: aload_0
1: iload_1
2: putfield #2 // Field a:I
5: return
}
反编译: javap -verbose MyTest(.class)verbose(冗长的)
Classfile /C:/spring_lecture/target/classes/com/compass/spring_lecture/binarycode/MyTest1.class
Last modified 2019年6月26日; size 521 bytes
MD5 checksum e84ac7b4ed245fd5824849fb69bacc27
Compiled from "MyTest1.java"
public class com.compass.spring_lecture.binarycode.MyTest1
minor version: 0 //小版本
major version: 52 //大版本
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #3 // com/compass/spring_lecture/binarycode/MyTest1
super_class: #4 // java/lang/Object
interfaces: 0, fields: 1, methods: 3, attributes: 1
Constant pool://常量池
#1 = Methodref #4.#20 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#21 // com/compass/spring_lecture/binarycode/MyTest1.a:I
#3 = Class #22 // com/compass/spring_lecture/binarycode/MyTest1
#4 = Class #23 // java/lang/Object
#5 = Utf8 a
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/compass/spring_lecture/binarycode/MyTest1;
#14 = Utf8 getA
#15 = Utf8 ()I
#16 = Utf8 setA
#17 = Utf8 (I)V
#18 = Utf8 SourceFile
#19 = Utf8 MyTest1.java
#20 = NameAndType #7:#8 // "<init>":()V
#21 = NameAndType #5:#6 // a:I
#22 = Utf8 com/compass/spring_lecture/binarycode/MyTest1
#23 = Utf8 java/lang/Object
{
public com.compass.spring_lecture.binarycode.MyTest1();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_1
6: putfield #2 // Field a:I
9: return
LineNumberTable:
line 4: 0
line 6: 4
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/compass/spring_lecture/binarycode/MyTest1;
public int getA();
descriptor: ()I
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field a:I
4: ireturn
LineNumberTable:
line 10: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/compass/spring_lecture/binarycode/MyTest1;
public void setA(int);
descriptor: (I)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: iload_1
2: putfield #2 // Field a:I
5: return
LineNumberTable: //行号表
line 14: 0
line 15: 5
LocalVariableTable: //局部变量表
Start Length Slot Name Signature
0 6 0 this Lcom/compass/spring_lecture/binarycode/MyTest1;
0 6 1 a I
}
SourceFile: "MyTest1.java"
查看磁盘的存储文件:16进制的文件 使用Hex Editor Neo查看(Windows OS) Hex Fiend(Mac OS) Tip: Fiend:魔鬼 能手的意思 我这里使用的Windows OS
- Overview 概述:了解字节码的结构
使用javap -verbose 命令分析一个字节码文件时,将会分析该字节码文件的魔数、版本号、常量池、类信息、类的构造方法、类的方法信息、类变量和成员变量等信息。
魔数:magic number 所有的.class字节码文件的前4个字节都是魔数,魔数值是固定值:0xCAFEBABE.咖啡宝贝
魔数之后的4个字节为版本信息,前2个字节表示minor version(次版本号),后2个字节表示major version(主版本号) 这里的版本号为 00 00 00 34,换算成十进制表示次版本号为0,而主版本号为52 (jdk8) 所以该文件的版本号为1.8.0,可以通过 java -version命令来验证这一点。
安装的jdk可以通过这个版本号来确定这个class文件是否能够加载,JDK是向下兼容的,1.8的版本可以加载version<1.8的jdk编译的class文件。常量池(constant pool):紧接着主版本号之后的就是常量池入口,常量池的长度是不确定的,一个java类中定义的很多信息都是由常量池来维护和描述的,可以将常量池看作是class文件的资源仓库,比如说java类中定义的方法与变量信息,都是存储在常量池中的,常量池中主要存储两类常量:字面量和符号引用[符号引用转换成直接引用]。字面量比如说文本字符串,java中声明为final的常量值等。而符号引用如类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符等。