案例介绍
本案例主要介绍通过java代码从class文件中解析;class文件、常量池、属性表;
作为类(或者接口)信息的载体,每个class文件都完整地定义了一个类。为了使java程序可以“编写一次,处处运行”,Java虚拟机规范对class文件格式进行了严格的规定。但是另外一方面,对于从哪里加载class文件,给了足够多的自由。Java虚拟机实现可以从文件系统读取和从JAR(或ZIP)压缩包中提取clss文件。除此之外,也可以通过网络下载、从数据库加载,甚至是在运行中直接生成class文件。Java虚拟机规范中所指的class文件,并非特指位于磁盘中的.class文件,而是泛指任何格式符号规范的class数据。
环境准备
1、jdk 1.8.0
2、IntelliJ IDEA Community Edition 2018.3.1 x64
配置信息
1、调试配置
2.1、配置位置:Run/Debug Configurations -> program arguments
2.2、配置内容:-Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" 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 │ │ ├── ClassReader.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; /** * http://www.itstack.org * create by fuzhengwei on 2019/4/26 */ public interface AttributeInfo { void readInfo(ClassReader reader); static AttributeInfo[] readAttributes(ClassReader reader, ConstantPool constantPool) { int attributesCount = reader.readU2ToInt(); AttributeInfo[] attributes = new AttributeInfo[attributesCount]; for (int i = 0; i < attributesCount; i++) { attributes[i] = readAttribute(reader, constantPool); } return attributes; } static AttributeInfo readAttribute(ClassReader reader, ConstantPool constantPool) { int attrNameIdx = reader.readU2ToInt(); String attrName = constantPool.getUTF8(attrNameIdx); int attrLen = reader.readU4ToInt(); 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.*; /** * http://www.itstack.org * create by fuzhengwei on 2019/4/26 */ public interface ConstantInfo { int CONSTANT_TAG_CLASS = 7; int CONSTANT_TAG_FIELDREF = 9; int CONSTANT_TAG_METHODREF = 10; int CONSTANT_TAG_INTERFACEMETHODREF = 11; int CONSTANT_TAG_STRING = 8; int CONSTANT_TAG_INTEGER = 3; int CONSTANT_TAG_FLOAT = 4; int CONSTANT_TAG_LONG = 5; int CONSTANT_TAG_DOUBLE = 6; int CONSTANT_TAG_NAMEANDTYPE = 12; int CONSTANT_TAG_UTF8 = 1; int CONSTANT_TAG_METHODHANDLE = 15; int CONSTANT_TAG_METHODTYPE = 16; int CONSTANT_TAG_INVOKEDYNAMIC = 18; void readInfo(ClassReader reader); int tag(); static ConstantInfo readConstantInfo(ClassReader reader, ConstantPool constantPool) { int tag = reader.readU1ToInt(); 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_UTF8: return new ConstantUtf8Info(); 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; /** * http://www.itstack.org * create by fuzhengwei on 2019/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.readU2ToInt(); this.thisClassIdx = reader.readU2ToInt(); this.supperClassIdx = reader.readU2ToInt(); this.interfaces = reader.readUInt16s(); 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.readU4ToHexStr(); if (!"cafebabe".equals(magic)) { throw new ClassFormatError("magic!"); } } private void readAndCheckVersion(ClassReader reader) { this.minorVersion = reader.readU2ToInt(); this.majorVersion = reader.readU2ToInt(); switch (this.majorVersion) { case 45: return; case 46: case 47: case 48: case 49: case 50: case 51: case 52: if (this.minorVersion == 0) 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 <= 0) return ""; return this.constantPool.getClassName(this.supperClassIdx); } public String[] interfaceNames() { String[] interfaceNames = new String[this.interfaces.length]; for (int i = 0; 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; /** * http://www.itstack.org * create by fuzhengwei on 2019/5/13 * <p> * java虚拟机定义了u1、u2、u4三种数据类型来表示;1字节、2字节、4字节,无符号整数。 * 在如下实现中,用增位方式表示无符号类型: * u1、u2可以用int类型存储,因为int类型是4字节 * u4 需要用long类型存储,因为long类型是8字节 */ public class ClassReader { private byte[] data; public ClassReader(byte[] data) { this.data = data; } //u1 public int readUint8() { byte[] val = readByte(1); return byte2int(val); } //u2 public int readUint16() { byte[] val = readByte(2); return byte2int(val); } //u4 public long readUint32() { byte[] val = readByte(4); String str_hex = new BigInteger(1, val).toString(16); return Long.parseLong(str_hex, 16); } public float readUint64TFloat() { byte[] val = readByte(8); return new BigInteger(1, val).floatValue(); } public long readUint64TLong() { byte[] val = readByte(8); return new BigInteger(1, val).longValue(); } public double readUint64TDouble() { byte[] val = readByte(8); return new BigInteger(1, val).doubleValue(); } public int[] readUint16s() { int n = this.readUint16(); int[] s = new int[n]; for (int i = 0; i < n; i++) { s[i] = this.readUint16(); } return s; } public byte[] readBytes(int n) { return readByte(n); } private byte[] readByte(int length) { byte[] copy = new byte[length]; System.arraycopy(data, 0, copy, 0, length); System.arraycopy(data, length, data, 0, data.length - length); return copy; } private int byte2int(byte[] val) { String str_hex = new BigInteger(1, val).toString(16); return Integer.parseInt(str_hex, 16); } }
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; /** * http://www.itstack.org * create by fuzhengwei on 2019/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.readU2ToInt(); this.nameIdx = reader.readU2ToInt(); this.descriptorIdx = reader.readU2ToInt(); this.attributes = AttributeInfo.readAttributes(reader, constantPool); } public static MemberInfo[] readMembers(ClassReader reader, ConstantPool constantPool) { int fieldCount = reader.readU2ToInt(); MemberInfo[] fields = new MemberInfo[fieldCount]; for (int i = 0; i < fieldCount; i++) { fields[i] = new MemberInfo(reader, constantPool); } return fields; } public int accessFlags() { return this.accessFlags; } public String name() { return this.constantPool.getUTF8(this.nameIdx); } public String descriptor() { return this.constantPool.getUTF8(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; /** * http://www.itstack.org * create by fuzhengwei on 2019/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) { //注意案例测试都是基于1.8,另外jdk1.9以后使用模块化没有rt.jar System.out.println("java version \"1.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:%s\n", 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:0x%x\n", 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(" %s\n", memberInfo.name()); } System.out.println("methods count: " + cf.methods().length); for (MemberInfo memberInfo : cf.methods()) { System.out.format(" %s\n", memberInfo.name()); } } }
测试结果
"C:\Program Files\Java\jdk1.8.0_161\bin\java.exe" "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.3.1\lib\idea_rt.jar=61458:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_161\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\rt.jar;E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-03\target\classes;D:\Program Files (x86)\apache-maven-2.2.1\repository\com\beust\jcommander\1.72\jcommander-1.72.jar;D:\Program Files (x86)\apache-maven-2.2.1\repository\org\projectlombok\lombok\1.18.0\lombok-1.18.0.jar;D:\Program Files (x86)\apache-maven-2.2.1\repository\com\alibaba\fastjson\1.2.40\fastjson-1.2.40.jar" org.itstack.demo.jvm.Main -Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" java.lang.String classpath:org.itstack.demo.jvm.classpath.Classpath@4bf558aa class:java.lang.String args:null version: 52.0 constants count:540 access flags:0x31 this class:java/lang/String super class:java/lang/Object interfaces:[java/io/Serializable, java/lang/Comparable, java/lang/CharSequence] fields count:5 value [C hash I serialVersionUID J serialPersistentFields [Ljava/io/ObjectStreamField; CASE_INSENSITIVE_ORDER Ljava/util/Comparator; methods count: 94 <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 0