案例介绍
本案例主要介绍通过java代码从class文件中解析;class文件、常量池、属性表;
环境准备
- jdk 1.8.0
- IntelliJ IDEA Community Edition 2018.3.1 x64
配置信息
- 调试配置配置位置:Run/Debug Configurations -> program arguments配置内容:-X jre “C:Program FilesJavajdk1.8.0_161jre” java. lang .String
代码示例
itstack-demo- jvm -03
├── pom.xml
└── src
└── main
│ └── java
│ └── org.itstack.demo.jvm
│ ├── classfile
│ │ ├── attributes {BootstrapMethods/Code/ConstantValue...}
│ │ ├── constantpool {CONSTANT_TAG_CLASS/CONSTANT_TAG_FIELDREF/CONSTANT_TAG_METHODREF...}
│ │ ├── ClassFile.java
│ │ ├── Class reader .java
│ │ └── MemberInfo.java
│ ├── classpath
│ │ ├── impl
│ │ │ ├── CompositeEntry.java
│ │ │ ├── DirEntry.java
│ │ │ ├── WildcardEntry.java
│ │ │ └── ZipEntry.java
│ │ ├── Classpath.java
│ │ └── Entry.java
│ ├── Cmd.java
│ └── Main.java
└── test
└── java
└── org.itstack.demo.test
└── HelloWorld.java
AttributeInfo.java
package org.itstack.demo.jvm.classfile.attributes;
import org.itstack.demo.jvm.classfile.ClassReader;
import org.itstack.demo.jvm.classfile.attributes.impl.*;
import org.itstack.demo.jvm.classfile.constantpool.ConstantPool;
/**
*
* create by fuzhengwei on/4/26
*/public interface AttributeInfo {
void readInfo(ClassReader reader);
static AttributeInfo[] readAttributes(ClassReader reader, ConstantPool constantPool) {
int attributesCount = reader.readUToInt();
AttributeInfo[] attributes = new AttributeInfo[attributesCount];
for (int i =; i < attributesCount; i++) {
attributes[i] = readAttribute(reader, constantPool);
}
return attributes;
}
static AttributeInfo readAttribute(ClassReader reader, ConstantPool constantPool) {
int attrNameIdx = reader.readUToInt();
String attrName = constantPool.getUTF(attrNameIdx);
int attrLen = reader.readUToInt();
AttributeInfo attrInfo = newAttributeInfo(attrName, attrLen, constantPool);
attrInfo.readInfo(reader);
return attrInfo;
}
static AttributeInfo newAttributeInfo(String attrName, int attrLen, ConstantPool constantPool) {
switch (attrName) {
case "BootstrapMethods":
return new BootstrapMethodsAttribute();
case "Code":
return new CodeAttribute(constantPool);
case "ConstantValue":
return new ConstantValueAttribute();
case "Deprecated":
return new DeprecatedAttribute();
case "EnclosingMethod":
return new EnclosingMethodAttribute(constantPool);
case "Exceptions":
return new ExceptionsAttribute();
case "InnerClasses":
return new InnerClassesAttribute();
case "LineNumberTable":
return new LineNumberTableAttribute();
case "LocalVariableTable":
return new LocalVariableTableAttribute();
case "LocalVariableTypeTable":
return new LocalVariableTypeTableAttribute();
// case "MethodParameters":
// case "RuntimeInvisibleAnnotations":
// case "RuntimeInvisibleParameterAnnotations":
// case "RuntimeInvisibleTypeAnnotations":
// case "RuntimeVisibleAnnotations":
// case "RuntimeVisibleParameterAnnotations":
// case "RuntimeVisibleTypeAnnotations":
case "Signature":
return new SignatureAttribute(constantPool);
case "SourceFile":
return new SourceFileAttribute(constantPool);
// case "SourceDebugExtension":
// case "StackMapTable":
case "Synthetic":
return new SyntheticAttribute();
default:
return new UnparsedAttribute(attrName, attrLen);
}
}
}
ConstantInfo.java
package org.itstack.demo.jvm.classfile.constantpool;
import org.itstack.demo.jvm.classfile.ClassReader;
import org.itstack.demo.jvm.classfile.constantpool.impl.*;
/**
*
* create by fuzhengwei on/4/26
*/public interface ConstantInfo {
int CONSTANT_TAG_CLASS =;
int CONSTANT_TAG_FIELDREF =;
int CONSTANT_TAG_METHODREF =;
int CONSTANT_TAG_INTERFACEMETHODREF =;
int CONSTANT_TAG_STRING =;
int CONSTANT_TAG_INTEGER =;
int CONSTANT_TAG_FLOAT =;
int CONSTANT_TAG_LONG =;
int CONSTANT_TAG_DOUBLE =;
int CONSTANT_TAG_NAMEANDTYPE =;
int CONSTANT_TAG_UTF = 1;
int CONSTANT_TAG_METHODHANDLE =;
int CONSTANT_TAG_METHODTYPE =;
int CONSTANT_TAG_INVOKEDYNAMIC =;
void readInfo(ClassReader reader);
int tag();
static ConstantInfo readConstantInfo(ClassReader reader, ConstantPool constantPool) {
int tag = reader.readUToInt();
ConstantInfo constantInfo = newConstantInfo(tag, constantPool);
constantInfo.readInfo(reader);
return constantInfo;
}
static ConstantInfo newConstantInfo(int tag, ConstantPool constantPool) {
switch (tag) {
case CONSTANT_TAG_INTEGER:
return new ConstantIntegerInfo();
case CONSTANT_TAG_FLOAT:
return new ConstantFloatInfo();
case CONSTANT_TAG_LONG:
return new ConstantLongInfo();
case CONSTANT_TAG_DOUBLE:
return new ConstantDoubleInfo();
case CONSTANT_TAG_UTF:
return new ConstantUtfInfo();
case CONSTANT_TAG_STRING:
return new ConstantStringInfo(constantPool);
case CONSTANT_TAG_CLASS:
return new ConstantClassInfo(constantPool);
case CONSTANT_TAG_FIELDREF:
return new ConstantFieldRefInfo(constantPool);
case CONSTANT_TAG_METHODREF:
return new ConstantMethodRefInfo(constantPool);
case CONSTANT_TAG_INTERFACEMETHODREF:
return new ConstantInterfaceMethodRefInfo(constantPool);
case CONSTANT_TAG_NAMEANDTYPE:
return new ConstantNameAndTypeInfo();
case CONSTANT_TAG_METHODTYPE:
return new ConstantMethodTypeInfo();
case CONSTANT_TAG_METHODHANDLE:
return new ConstantMethodHandleInfo();
case CONSTANT_TAG_INVOKEDYNAMIC:
return new ConstantInvokeDynamicInfo();
default:
throw new ClassFormatError("constant pool tag");
}
}
}
ClassFile.java
package org.itstack.demo.jvm.classfile;
import org.itstack.demo.jvm.classfile.attributes.AttributeInfo;
import org.itstack.demo.jvm.classfile.constantpool.ConstantPool;
/**
*
* create by fuzhengwei on/4/26
*/public class ClassFile {
private int minorVersion;
private int majorVersion;
private ConstantPool constantPool;
private int accessFlags;
private int thisClassIdx;
private int supperClassIdx;
private int[] interfaces;
private MemberInfo[] fields;
private MemberInfo[] methods;
private AttributeInfo[] attributes;
public ClassFile(byte[] classData) {
ClassReader reader = new ClassReader(classData);
this.readAndCheckMagic(reader);
this.readAndCheckVersion(reader);
this.constantPool = this.readConstantPool(reader);
this.accessFlags = reader.readUToInt();
this.thisClassIdx = reader.readUToInt();
this.supperClassIdx = reader.readUToInt();
this.interfaces = reader.readUInts();
this.fields = MemberInfo.readMembers(reader, constantPool);
this.methods = MemberInfo.readMembers(reader, constantPool);
this.attributes = AttributeInfo.readAttributes(reader, constantPool);
}
private void readAndCheckMagic(ClassReader reader) {
String magic = reader.readUToHexStr();
if (!"cafebabe".equals(magic)) {
throw new ClassFormatError("magic!");
}
}
private void readAndCheckVersion(ClassReader reader) {
this.minorVersion = reader.readUToInt();
this.majorVersion = reader.readUToInt();
switch (this.majorVersion) {
case:
return;
case:
case:
case:
case:
case:
case:
case:
if (this.minorVersion ==)
return;
}
throw new UnsupportedClassVersionError();
}
private ConstantPool readConstantPool(ClassReader reader) {
return new ConstantPool(reader);
}
public int minorVersion(){
return this.minorVersion;
}
public int majorVersion(){
return this.majorVersion;
}
public ConstantPool constantPool(){
return this.constantPool;
}
public int accessFlags() {
return this.accessFlags;
}
public MemberInfo[] fields() {
return this.fields;
}
public MemberInfo[] methods() {
return this.methods;
}
public String className() {
return this.constantPool.getClassName(this.thisClassIdx);
}
public String superClassName() {
if (this.supperClassIdx <=) return "";
return this.constantPool.getClassName(this.supperClassIdx);
}
public String[] interfaceNames() {
String[] interfaceNames = new String[this.interfaces.length];
for (int i =; i < this.interfaces.length; i++) {
interfaceNames[i] = this.constantPool.getClassName(interfaces[i]);
}
return interfaceNames;
}
}
ClassReader.java
package org.itstack.demo.jvm.classfile;
import java.math.BigInteger;
/**
*
* create by fuzhengwei on/5/13
* <p>
* java虚拟机定义了u、u2、u4三种数据类型来表示;1字节、2字节、4字节,无符号整数。
* 在如下实现中,用增位方式表示无符号类型:
* u、u2可以用int类型存储,因为int类型是4字节
* u 需要用long类型存储,因为long类型是8字节
*/public class ClassReader {
private byte[] data;
public ClassReader(byte[] data) {
this.data = data;
}
//u
public int readUint() {
byte[] val = readByte();
return byteint(val);
}
//u
public int readUint() {
byte[] val = readByte();
return byteint(val);
}
//u
public long readUint() {
byte[] val = readByte();
String str_hex = new BigInteger(, val). toString (16);
return Long.parseLong(str_hex,);
}
public float readUintTFloat() {
byte[] val = readByte();
return new BigInteger(, val).floatValue();
}
public long readUintTLong() {
byte[] val = readByte();
return new BigInteger(, val).longValue();
}
public double readUintTDouble() {
byte[] val = readByte();
return new BigInteger(, val).doubleValue();
}
public int[] readUints() {
int n = this.readUint();
int[] s = new int[n];
for (int i =; i < n; i++) {
s[i] = this.readUint();
}
return s;
}
public byte[] readBytes(int n) {
return readByte(n);
}
private byte[] readByte(int length) {
byte[] copy = new byte[length];
System.arraycopy(data,, copy, 0, length);
System.arraycopy(data, length, data,, data.length - length);
return copy;
}
private int byteint(byte[] val) {
String str_hex = new BigInteger(, val).toString(16);
return Integer.parseInt(str_hex,);
}
}
MemberInfo.java
package org.itstack.demo.jvm.classfile;
import org.itstack.demo.jvm.classfile.attributes.AttributeInfo;
import org.itstack.demo.jvm.classfile.attributes.impl.CodeAttribute;
import org.itstack.demo.jvm.classfile.attributes.impl.ConstantValueAttribute;
import org.itstack.demo.jvm.classfile.constantpool.ConstantPool;
/**
*
* create by fuzhengwei on/4/26
*/public class MemberInfo {
private ConstantPool constantPool;
private int accessFlags;
private int nameIdx;
private int descriptorIdx;
private AttributeInfo[] attributes;
public MemberInfo(ClassReader reader, ConstantPool constantPool) {
this.constantPool = constantPool;
this.accessFlags = reader.readUToInt();
this.nameIdx = reader.readUToInt();
this.descriptorIdx = reader.readUToInt();
this.attributes = AttributeInfo.readAttributes(reader, constantPool);
}
public static MemberInfo[] readMembers(ClassReader reader, ConstantPool constantPool) {
int fieldCount = reader.readUToInt();
MemberInfo[] fields = new MemberInfo[fieldCount];
for (int i =; i < fieldCount; i++) {
fields[i] = new MemberInfo(reader, constantPool);
}
return fields;
}
public int accessFlags() {
return this.accessFlags;
}
public String name() {
return this.constantPool.getUTF(this.nameIdx);
}
public String descriptor() {
return this.constantPool.getUTF(this.descriptorIdx);
}
public CodeAttribute codeAttribute() {
for (AttributeInfo attrInfo : attributes) {
if (attrInfo instanceof CodeAttribute) return (CodeAttribute) attrInfo;
}
return null;
}
public ConstantValueAttribute ConstantValueAttribute() {
for (AttributeInfo attrInfo : attributes) {
if (attrInfo instanceof ConstantValueAttribute) return (ConstantValueAttribute) attrInfo;
}
return null;
}
}
Main.java
package org.itstack.demo.jvm;
import org.itstack.demo.jvm.classfile.ClassFile;
import org.itstack.demo.jvm.classfile.MemberInfo;
import org.itstack.demo.jvm.classpath.Classpath;
import java.util.Arrays;
/**
*
* create by fuzhengwei on/4/24
*/public class Main {
public static void main(String[] args) {
Cmd cmd = Cmd.parse(args);
if (!cmd.ok || cmd.helpFlag) {
System.out.println("Usage: <main class> [-options] class [args...]");
return;
}
if (cmd.versionFlag) {
//注意案例测试都是基于.8,另外jdk1.9以后使用模块化没有rt.jar
System.out.println("java version ".8.0"");
return;
}
startJVM(cmd);
}
private static void startJVM(Cmd cmd) {
Classpath classpath = new Classpath(cmd.jre, cmd.classpath);
System.out.printf("classpath:%s class:%s args:%sn",
classpath, cmd.getMainClass(), cmd.getAppArgs());
//获取className
String className = cmd.getMainClass().replace(".", "/");
ClassFile classFile = loadClass(className, classpath);
assert classFile != null;
printClassInfo(classFile);
}
private static ClassFile loadClass(String className, Classpath classpath) {
try {
byte[] classData = classpath.readClass(className);
return new ClassFile(classData);
} catch (Exception e) {
System.out.println("Could not find or load main class " + className);
return null;
}
}
private static void printClassInfo(ClassFile cf) {
System.out.println("version: " + cf.majorVersion() + "." + cf.minorVersion());
System.out.println("constants count:" + cf.constantPool().getSiz());
System.out.format("access flags:x%xn", cf.accessFlags());
System.out.println("this class:" + cf.className());
System.out.println("super class:" + cf.superClassName());
System.out.println("interfaces:" + Arrays.toString(cf.interfaceNames()));
System.out.println("fields count:" + cf.fields().length);
for (MemberInfo memberInfo : cf.fields()) {
System.out.format(" %sn", memberInfo.name());
}
System.out.println("methods count: " + cf.methods().length);
for (MemberInfo memberInfo : cf.methods()) {
System.out.format(" %sn", memberInfo.name());
}
}
}
测试结果
"C:Program FilesJavajdk.8.0_161binjava.exe" "-javaagent:D:Program FilesJetBrainsIntelliJ IDEA Community Edition 2018.3.1libidea_rt.jar=61458:D:Program FilesJetBrainsIntelliJ IDEA Community Edition 2018.3.1bin" -Dfile.encoding=UTF-8 -classpath "C:Program FilesJavajdk1.8.0_161jrelibcharsets.jar;C:Program FilesJavajdk1.8.0_161jrelibdeploy.jar;C:Program FilesJavajdk1.8.0_161jrelibextaccess-bridge-64.jar;C:Program FilesJavajdk1.8.0_161jrelibextcldrdata.jar;C:Program FilesJavajdk1.8.0_161jrelibextdnsns.jar;C:Program FilesJavajdk1.8.0_161jrelibextjaccess.jar;C:Program FilesJavajdk1.8.0_161jrelibextjfxrt.jar;C:Program FilesJavajdk1.8.0_161jrelibextlocaledata.jar;C:Program FilesJavajdk1.8.0_161jrelibextnashorn.jar;C:Program FilesJavajdk1.8.0_161jrelibextsunec.jar;C:Program FilesJavajdk1.8.0_161jrelibextsunjce_provider.jar;C:Program FilesJavajdk1.8.0_161jrelibextsunmscapi.jar;C:Program FilesJavajdk1.8.0_161jrelibextsunpkcs11.jar;C:Program FilesJavajdk1.8.0_161jrelibextzipfs.jar;C:Program FilesJavajdk1.8.0_161jrelibjavaws.jar;C:Program FilesJavajdk1.8.0_161jrelibjce.jar;C:Program FilesJavajdk1.8.0_161jrelibjfr.jar;C:Program FilesJavajdk1.8.0_161jrelibjfxswt.jar;C:Program FilesJavajdk1.8.0_161jrelibjsse.jar;C:Program FilesJavajdk1.8.0_161jrelibmanagement-agent.jar;C:Program FilesJavajdk1.8.0_161jrelibplugin.jar;C:Program FilesJavajdk1.8.0_161jrelibresources.jar;C:Program FilesJavajdk1.8.0_161jrelibrt.jar;E:itstackgitistack-demoitstack-demo-jvmitstack-demo-jvm-03targetclasses;D:Program Files (x86)apache-maven-2.2.1repositorycombeustjcommander1.72jcommander-1.72.jar;D:Program Files (x86)apache-maven-2.2.1repositoryorgprojectlomboklombok1.18.0lombok-1.18.0.jar;D:Program Files (x86)apache-maven-2.2.1repositorycomalibabafastjson1.2.40fastjson-1.2.40.jar" org.itstack.demo.jvm.Main -Xjre "C:Program FilesJavajdk1.8.0_161jre" java.lang.String
classpath:org.itstack.demo.jvm.classpath.Classpath@bf558aa class:java.lang.String args:null
version:.0
constants count:
access flags:x31
this class:java/lang/String
super class:java/lang/ Object
interfaces:[java/io/Serializable, java/lang/Comparable, java/lang/CharSequence]
fields count:
value [C
hash I
serialVersionUID J
serialPersistentFields [Ljava/io/ObjectStreamField;
CASE_INSENSITIVE_ORDER Ljava/util/Comparator;
methods count:
<init> ()V
<init> (Ljava/lang/String;)V
<init> ([C)V
<init> ([CII)V
<init> ([III)V
<init> ([BIII)V
<init> ([BI)V
checkBounds ([BII)V
<init> ([BIILjava/lang/String;)V
<init> ([BIILjava/nio/charset/Charset;)V
<init> ([BLjava/lang/String;)V
<init> ([BLjava/nio/charset/Charset;)V
<init> ([BII)V
<init> ([B)V
<init> (Ljava/lang/StringBuffer;)V
<init> (Ljava/lang/StringBuilder;)V
<init> ([CZ)V
length ()I
isEmpty ()Z
charAt (I)C
codePointAt (I)I
codePointBefore (I)I
codePointCount (II)I
offsetByCodePoints (II)I
getChars ([CI)V
getChars (II[CI)V
getBytes (II[BI)V
getBytes (Ljava/lang/String;)[B
getBytes (Ljava/nio/charset/Charset;)[B
getBytes ()[B
equals (Ljava/lang/Object;)Z
contentEquals (Ljava/lang/StringBuffer;)Z
nonSyncContentEquals (Ljava/lang/AbstractStringBuilder;)Z
contentEquals (Ljava/lang/CharSequence;)Z
equalsIgnoreCase (Ljava/lang/String;)Z
compareTo (Ljava/lang/String;)I
compareToIgnoreCase (Ljava/lang/String;)I
regionMatches (ILjava/lang/String;II)Z
regionMatches (ZILjava/lang/String;II)Z
startsWith (Ljava/lang/String;I)Z
startsWith (Ljava/lang/String;)Z
endsWith (Ljava/lang/String;)Z
hashCode ()I
indexOf (I)I
indexOf (II)I
indexOfSupplementary (II)I
lastIndexOf (I)I
lastIndexOf (II)I
lastIndexOfSupplementary (II)I
indexOf (Ljava/lang/String;)I
indexOf (Ljava/lang/String;I)I
indexOf ([CIILjava/lang/String;I)I
indexOf ([CII[CIII)I
lastIndexOf (Ljava/lang/String;)I
lastIndexOf (Ljava/lang/String;I)I
lastIndexOf ([CIILjava/lang/String;I)I
lastIndexOf ([CII[CIII)I
substring (I)Ljava/lang/String;
substring (II)Ljava/lang/String;
subSequence (II)Ljava/lang/CharSequence;
concat (Ljava/lang/String;)Ljava/lang/String;
replace (CC)Ljava/lang/String;
matches (Ljava/lang/String;)Z
contains (Ljava/lang/CharSequence;)Z
replaceFirst (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
replaceAll (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
replace (Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
split (Ljava/lang/String;I)[Ljava/lang/String;
split (Ljava/lang/String;)[Ljava/lang/String;
join (Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String;
join (Ljava/lang/CharSequence;Ljava/lang/Iterable;)Ljava/lang/String;
toLowerCase (Ljava/util/Locale;)Ljava/lang/String;
toLowerCase ()Ljava/lang/String;
toUpperCase (Ljava/util/Locale;)Ljava/lang/String;
toUpperCase ()Ljava/lang/String;
trim ()Ljava/lang/String;
toString ()Ljava/lang/String;
toCharArray ()[C
format (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
format (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
valueOf (Ljava/lang/Object;)Ljava/lang/String;
valueOf ([C)Ljava/lang/String;
valueOf ([CII)Ljava/lang/String;
copyValueOf ([CII)Ljava/lang/String;
copyValueOf ([C)Ljava/lang/String;
valueOf (Z)Ljava/lang/String;
valueOf (C)Ljava/lang/String;
valueOf (I)Ljava/lang/String;
valueOf (J)Ljava/lang/String;
valueOf (F)Ljava/lang/String;
valueOf (D)Ljava/lang/String;
intern ()Ljava/lang/String;
compareTo (Ljava/lang/Object;)I
<clinit> ()V
Process finished with exit code
今天的分享就到这了。