Table 4-1: The 16 parts of a .class file
Field | Width (bytes) | Meaning |
---|---|---|
magic number | 4 | This identifies the class file format. It should be 0xCAFEBABE. If it's anything else, you're dealing with a format more recent than this book. |
minor version | 2 | The minor version of the compiler |
major version | 2 | The major version of the compiler |
number of constants | 2 | the number of entries in the constant pool that follows |
constant pool | variable | The constant pool is a table of constant values used by this class. As many bytes as are necessary to fill the number of entries specified by the constant pool count are read. |
access flags | 2 | These bit flags tell you whether the class is public, final, abstract, an interface, and a few other things. |
This class | 2 | This tells you which entry in the constant pool holds this class's class information. |
superclass | 2 | If this is zero, then this class's only superclass is java.lang.Object. Otherwise this is an index into the constant pool for the superclass class info. |
number of interfaces | 2 | the number of entries in the interfaces table that follows |
interfaces table | 2 * the number of interfaces | The interface table holds two byte indices into the constant pool table, one for each interface that this class implements. |
number of fields | 2 | the number of entries in the fields table that follows |
fields table | variable | The fields table includes one variable length field info structure for each field in the class |
number of methods | 2 | the number of entries in the methods table that follows |
methods table | variable | The method table contains the byte codes for each method in the class, the return type of the method, and the types of each argument to the method, combined in a method info structure |
number of attributes | 2 | the number of entries in the attributes table that follows |
attributes table | variable | the attributes of the class. Attributes have no predetermined length |
pp. 148-153: The PoolEntry
class doesn't properly handle negative byte values;
that is, bytes with their high bit set. After converting them to an int
before bit-shifting,
they must be bitwise anded with 0xFF (255) to ensure that their three high order bytes
are filled with zeroes instead of ones. Here's a corrected PoolEntry
class:
import java.io.*;
public class PoolEntry {
public final static int cUTF8 = 1;
public final static int cInteger = 3;
public final static int cFloat = 4;
public final static int cLong = 5;
public final static int cDouble = 6;
public final static int cClassInfo = 7;
public final static int cString = 8;
public final static int cFieldRef = 9;
public final static int cMethodRef = 10;
public final static int cInterfaceMethodRef = 11;
public final static int cNameAndType = 12;
int tag;
byte[] data;
public PoolEntry(DataInputStream dis) throws IOException {
tag = dis.readUnsignedByte();
int bytesToRead;
switch (tag) {
case cLong:
case cDouble:
bytesToRead = 8;
break;
case cInteger:
case cFloat:
case cFieldRef:
case cMethodRef:
case cNameAndType:
case cInterfaceMethodRef:
bytesToRead = 4;
break;
case cClassInfo:
case cString:
bytesToRead = 2;
break;
case cUTF8:
bytesToRead = dis.readUnsignedShort();
break;
default:
throw new ClassFormatError("Unrecognized Constant Type " + tag);
}
data = new byte[bytesToRead];
int check = dis.read(data);
if (check != data.length) {
throw new ClassFormatError("Not enough data to fill array");
}
}
public String readUTF8() {
if (tag != cUTF8) {
throw new ClassFormatError
("This is not a UTF8 string ");
}
try {
// first put length of string back in string
int len = data.length;
byte[] newdata = new byte[len+2];
newdata[0] = (byte) (len >>> 8);
newdata[1] = (byte) len;
System.arraycopy(data, 0, newdata, 2, data.length);
ByteArrayInputStream bis = new ByteArrayInputStream(newdata);
DataInputStream dis = new DataInputStream(bis);
return dis.readUTF();
}
catch (IOException e) {
throw new ClassFormatError(e + " Bad UTF8 string");
}
}
public int readInteger() {
if (tag != cInteger) {
throw new ClassFormatError("This is not an integer.");
}
return bsl(data[0], 24) | bsl(data[1], 16) | bsl(data[2], 0) | bsl(data[3], 0);
}
public int readLong() {
if (tag != cLong) {
throw new ClassFormatError("This is not a long.");
}
return bsl(data[0], 56) | bsl(data[1], 48) | bsl(data[2], 40) |
bsl(data[3], 32) | bsl(data[4], 24) | bsl(data[5], 16) |
bsl(data[6], 8) | bsl(data[7], 0);
}
public float readFloat() {
if (tag != cFloat) {
throw new ClassFormatError("This is not a float");
}
int bits = bsl(data[0], 24) | bsl(data[1], 16) | bsl(data[2], 8) | bsl(data[3], 0);
return Float.intBitsToFloat(bits);
}
public double readDouble() {
if (tag != cDouble) {
throw new ClassFormatError("This is not a double");
}
long bits = (long) data[0] << 56 | (long) data[1] << 48
| (long) data[2] << 40 | (long) data[3] << 32 | (long) data[4] << 24
| (long) data[5] << 16 | (long) data[6] << 8 | (long) data[7];
return Double.longBitsToDouble(bits);
}
public ClassInfo readClassInfo() {
if (tag != cClassInfo) {
throw new ClassFormatError("This is not a ClassInfoStructure");
}
return new ClassInfo(tag, bsl(data[0], 8) | bsl(data[1], 0));
}
public RefInfo readFieldRef() {
if (tag != cFieldRef) {
throw new ClassFormatError("This is not a FieldRefStructure");
}
return new RefInfo(tag, bsl(data[1], 0) | bsl(data[1], 0),
bsl(data[2], 8) | bsl(data[3], 0));
}
public RefInfo readMethodRef() {
if (tag != cMethodRef) {
throw new ClassFormatError("This is not a methodRef");
}
return new RefInfo(tag, bsl(data[0], 8) | bsl(data[1], 0),
bsl(data[1], 8) | bsl(data[3], 0));
}
public RefInfo readInterfaceMethodRef() {
if (tag != cInterfaceMethodRef) {
throw new ClassFormatError("This is not an InterfaceMethodRef");
}
return new RefInfo(tag, bsl(data[0], 8) | bsl(data[1], 0),
bsl(data[2], 8) | bsl(data[3], 0));
}
public NameAndType readNameAndType() {
if (tag != cNameAndType) {
throw new ClassFormatError("This is not a Name and Type structure");
}
return new NameAndType(tag, bsl(data[0], 8) | bsl(data[1], 0),
bsl(data[2], 8) | bsl(data[3], 0));
}
public int readString() {
if (tag != cString) {
throw new ClassFormatError("This is not a String");
}
return bsl(data[0], 8) | bsl(data[1], 0);
}
public int tag() {
return tag;
}
// shift unsigned byte left
private static final int bsl(byte b, int toShift) {
return (b & 0xFF) << toShift;
}
public String toString() {
switch (tag) {
case cLong:
return "long " + String.valueOf(readLong());
case cDouble:
return "double " + String.valueOf(readDouble());
case cInteger:
return "int " + String.valueOf(readInteger());
case cFloat:
return "float " + String.valueOf(readFloat());
case cFieldRef:
return "FieldRef " + String.valueOf(readFieldRef());
case cMethodRef:
return "MethodRef " + String.valueOf(readMethodRef());
case cNameAndType:
return "NameAndType " + String.valueOf(readNameAndType());
case cInterfaceMethodRef:
return "InterfaceMethodRef " + String.valueOf(readInterfaceMethodRef());
case cClassInfo:
return "ClassInfo " + String.valueOf(readClassInfo());
case cString:
return "String " + String.valueOf(readString());
case cUTF8:
return "UTF8 " + readUTF8();
default:
throw new ClassFormatError("Unrecognized Constant Type");
}
}
}
p. 156: Listing 4-16 uses incorrect bit flags for isInterface, isAbstract, and isSpecial.
The correct listing reads:
short access_flags;
boolean isPublic;
boolean isFinal;
boolean isInterface;
boolean isAbstract;
boolean isSpecial;
void readAccessFlags() throws IOException {
access_flags = theInput.readShort();
isPublic = (access_flags & 0x0001) == 0 ? false : true;
isFinal = (access_flags & 0x0010) == 0 ? false : true;
isSpecial = (access_flags & 0x0020) == 0 ? false : true;
isInterface = (access_flags & 0x0200) == 0 ? false : true;
isAbstract = (access_flags & 0x0400) == 0 ? false : true;
if (isAbstract && isFinal) {
throw new ClassFormatError("This class is abstract and final!");
}
if (isInterface && !isAbstract) {
throw new ClassFormatError("This interface is not abstract!");
}
if (isFinal && isInterface) {
throw new ClassFormatError("This interface is final!");
}
}
p. 159: In Listing 4-20 change "Insufficioent" to "Insufficient".