在运行一段java代码的时候需要经过编译
,验证
,加载
和运行
,具体如下图:
这个系列的文章是为了探讨Java字节码
是什么样的结构,如何能够准确的表达我们代码的含义。
为了探讨我们的源代码和Java字节码的关系,我们先写一段代码,尽量多用上java的关键字和特殊的方法,以便我们测试和对比:
package org.rz;
public class AppMain {
private String userName;
private int age;
private short sex;
private long userCode;
private double income;
private float ablity;
private final static String userId="000000001";
private final long indentityCardNo=34122202019029L;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public short getSex() {
return sex;
}
public void setSex(short sex) {
this.sex = sex;
}
public long getUserCode() {
return userCode;
}
public void setUserCode(long userCode) {
this.userCode = userCode;
}
public double getIncome() {
return income;
}
public float getAblity() {
return ablity;
}
public void setAblity(float ablity) {
this.ablity = ablity;
}
public static String getUserId() {
return userId;
}
public long getIndentityCardNo() {
return indentityCardNo;
}
public String getUserInfo() {
return "AppMain{" + "userName='" + userName + '\'' + ", age=" + age + ", sex=" + sex + ", userCode=" + userCode + ", income=" + income + ", ablity=" + ablity + ", indentityCardNo=" + indentityCardNo + '}';
}
private static AppMain appmain;
public static AppMain getInstance() {
synchronized (AppMain.class) {
if (appmain == null) {
appmain = new AppMain();
}
return appmain;
}
}
public synchronized boolean changeName(String name) {
if (StringUtils.isEmpty(name)) {
return false;
}
if (StringUtils.isEmpty(this.getUserName())) {
this.setUserName(name);
}
return true;
}
private void testMain(){
System.out.println("init ok");
}
}
编译后得到AppMain.class
文件,用文本工具打开后,结果如下图:
对于这个文本,我们可以尝试使用JDK中的类加载工具加载看下效果。
在原生的JDK中有对java字节码
的读取的工具类com.sun.tools
,具体使用如下:
File file=new File("/Users/fuwei/work/rzframe/rz-lib/src/test/resources/AppMain.class");
try {
ClassFile read = ClassFile.read(file);
System.out.println(read.toString());
} catch (IOException e) {
e.printStackTrace();
} catch (ConstantPoolException e) {
e.printStackTrace();
}
加载结果调试监控如下:
根据JVM的虚拟机规范(SE8)提供的资料,字节码对应的结构体如下:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
其中u2
和u4
分别代表占用的字节,u2代表占用两个字节,u4代表占用两个字节 对应的结构图如下:
在我们了解了class的结构之后,就可以开始试着解析class文件。
DataInputStream
,我们是类文件是按照顺序读取的,所以可以定义的游标的对象cursor
来读取,对cursor
可以封装几个读取的方法:
public void readFully(byte[] byteArr) throws IOException { this.dataInputStream.readFully(byteArr); } public int readUnsignedByte() throws IOException { return this.dataInputStream.readUnsignedByte(); } public int readUnsignedShort() throws IOException { return this.dataInputStream.readUnsignedShort(); } public int readInt() throws IOException { return this.dataInputStream.readInt(); } public long readLong() throws IOException { return this.dataInputStream.readLong(); } public float readFloat() throws IOException { return this.dataInputStream.readFloat(); } public double readDouble() throws IOException { return this.dataInputStream.readDouble(); } public String readUTF() throws IOException { return this.dataInputStream.readUTF(); } }
魔数
是一个固定的值cafe babe
,一共占用4个字节,我们可以通过简单的方式把魔数取出来:
byte[] byteArr=new byte[4]; cursor.readFully(byteArr); System.out.println(StringUtils.binaryToHexString(byteArr));
对应16进制转字符的方法:
public static String binaryToHexString(byte[] bytes) { String hexStr = "0123456789ABCDEF"; StringBuilder result = new StringBuilder(); String hex = ""; for (int i = 0; i < bytes.length; i++) { //字节高4位 hex = String.valueOf(hexStr.charAt((bytes[i] & 0xF0) >> 4)); //字节低4位 hex += String.valueOf(hexStr.charAt(bytes[i] & 0x0F)); result.append(hex); } return result.toString(); }
根据版本号插叙对应的jdk的版本可以看出,笔者的jdk的版本是1.8
(本文完)
作者:付威 博客地址:http://blog.laofu.online 如果觉得对您有帮助,可以下方的RSS订阅,谢谢合作 如有任何知识产权、版权问题或理论错误,还请指正。 本文是付威的网络博客原创,自由转载-非商用-非衍生-保持署名,请遵循:创意共享3.0许可证