CLASS File Documentation


Summary

A CLASS file is compiled Java bytecode, the output the Java compiler (javac) produces from a .java source file. It is not meant to be read directly: it is loaded and run by the Java Virtual Machine (JVM), so you need Java installed. Every .class begins with the magic number CA FE BA BE. Real programs ship as a JAR (a ZIP of many .class files), and to recover source you must decompile it. Its MIME type is application/java-vm.

Technical details

FeatureValue
Full nameJava Bytecode Class File
File extension.class
MIME typeapplication/java-vm
Format typeCompiled Java bytecode (binary)
DeveloperSun Microsystems (now Oracle); part of the Java Platform
Introduced1995–1996 (Java 1.0)
StandardThe Java Virtual Machine Specification, Chapter 4
Open standardYes — the class file format is publicly specified
Byte orderBig-endian
Magic numberCA FE BA BE (offset 0)
Version stamp2-byte minor + 2-byte major after the magic
Major 52 / 61 / 65Java 8 / Java 17 / Java 21
Executed byThe Java Virtual Machine (JVM)
Run withjava MyClass (needs a JDK/JRE)
Read sourceDecompile (JD-GUI, CFR) or disassemble (javap -c)
Typical packagingMany .class files inside a .jar (a ZIP)
Related extensions.java, .jar, .war, .jmod, .dex
Specificationdocs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html
File signature (magic bytes)
CA FE BA BE

Offset 0, 4 bytes: the literal “CAFEBABE” magic number that identifies every Java class file. It is immediately followed by a 2-byte minor_version and a 2-byte major_version; the major tells you which Java release compiled the file (52 = Java 8, 61 = Java 17, 65 = Java 21). All multi-byte values are big-endian. Note: macOS Mach-O fat binaries also start with CA FE BA BE, but a Java class is distinguished by the sensible version words that follow.

What is a CLASS file?

A .class file is the compiled form of a Java program. When you compile a .java source file with javac, the compiler emits one .class file per class or interface, containing Java bytecode: a compact, platform-independent instruction set executed by the Java Virtual Machine (JVM). The format has been part of Java since version 1.0 in 1995–1996 and is defined precisely in Chapter 4 of the Java Virtual Machine Specification. “Compile once, run anywhere” is exactly this file: the same .class runs unchanged on any operating system that has a JVM. Its MIME type is application/java-vm.

A .class is not meant to be opened like a document. To run it you need Java installed and a main method (java MyClass); to read what it does you must decompile or disassemble it, because bytecode is not human-readable and the compiler discards comments and most local-variable names. You also rarely deal with just one: real applications, libraries and most Minecraft mods ship as a JAR, which is a ZIP of many .class files plus a manifest. Everything below is the internal structure the JVM reads, in the order the bytes appear.

The header: magic, minor and major version

Every class file opens with a fixed 8-byte header. The specification defines the whole file as a single big-endian structure, and the first fields identify it and stamp its version.

ClassFile {
    u4  magic;             // 0xCAFEBABE
    u2  minor_version;     // usually 0
    u2  major_version;     // 52 = Java 8, 61 = Java 17, 65 = Java 21
    u2  constant_pool_count;
    cp_info constant_pool[constant_pool_count - 1];
    u2  access_flags;      // public / final / abstract / interface ...
    u2  this_class;        // index into constant_pool
    u2  super_class;       // index into constant_pool (0 only for Object)
    u2  interfaces_count;  u2 interfaces[];
    u2  fields_count;      field_info fields[];
    u2  methods_count;     method_info methods[];
    u2  attributes_count;  attribute_info attributes[];
}

The u1/u2/u4 notation means unsigned 1-, 2- and 4-byte big-endian integers. The magic is always 0xCAFEBABE, chosen by the format’s designers as a memorable hexadecimal word. The major_version is the field behind one of the most common Java errors: it records which JDK compiled the file, and if you try to run a class on an older JVM than the one that built it, the JVM throws UnsupportedClassVersionError. The numbering increments by one per release — 52 for Java 8, 61 for Java 17, 65 for Java 21 — and a minor_version of 65535 marks a class that uses that release’s preview features.

The constant pool: the file’s symbol table

Directly after the header comes the constant pool, and it is the structural heart of the class file. It is a table of entries that every other part of the file refers to by 1-based index rather than storing literals inline. A method that calls System.out.println("hi") does not embed those strings; it holds constant-pool indices that chain to the class name, the field, the method name and signature, and the string literal.

TagConstant kind
1Utf8 — a modified-UTF-8 string (names, signatures, literals)
3 / 4Integer / Float — 4-byte numeric constants
5 / 6Long / Double — 8-byte constants (take two pool slots)
7Class — a class or interface, by name index
8String — a string literal, by Utf8 index
9/10/11Fieldref / Methodref / InterfaceMethodref
12NameAndType — a name plus a type descriptor

Two quirks trip up parsers. The pool is indexed from 1, not 0, and Long and Double entries each occupy two consecutive slots, so the next usable index jumps by two. All symbolic references — the names of classes, methods and fields the code touches — are Utf8 strings in this pool, which is precisely why bytecode retains enough information to be decompiled: the human-meaningful names survive here even though the source text does not.

Fields, methods and the Code attribute

After the class-level metadata (access_flags, this_class, super_class and the interface list) come the fields and methods tables. Each method_info records the method’s name and a descriptor — a compact type signature such as (Ljava/lang/String;)I, meaning “takes a String, returns an int” — plus a list of attributes. The important one is the Code attribute, which carries the actual executable bytecode.

Code_attribute {
    u2 max_stack;         // operand-stack depth this method needs
    u2 max_locals;        // local-variable slots
    u4 code_length;
    u1 code[code_length]; // the bytecode instructions
    ... exception_table, attributes (LineNumberTable, etc.)
}

The code array is a stream of one-byte opcodes, each optionally followed by operands. The JVM is a stack machine: instructions push and pop operands rather than using registers. So iload_1 pushes local variable 1, iadd pops two ints and pushes their sum, invokevirtual #7 calls a method named by constant-pool entry 7, and ireturn returns an int. The max_stack and max_locals fields let the verifier check the method before it ever runs. Optional attributes like LineNumberTable map bytecode offsets back to source line numbers, which is what makes a stack trace readable.

How the JVM loads and verifies a class

Running a .class is not a simple read. When the JVM first needs a class it goes through loading, linking and initialisation. Loading reads the bytes and builds an internal representation. Linking has three steps: verification checks that the bytecode is well-formed and type-safe (the stack never underflows, jumps land on real instructions, types match), preparation allocates static fields with default values, and resolution turns the symbolic constant-pool references into direct ones the first time each is used. Initialisation finally runs the static initialisers. The verification step is a real security boundary: it is why the JVM can load code from untrusted sources without letting malformed bytecode corrupt the runtime, and it is why a hand-edited or truncated .class is usually rejected with a VerifyError rather than crashing.

Reading a class: decompilers and javap

Because the constant pool preserves names and the method structure is intact, a .class can be reconstructed into approximate Java source. A decompiler such as JD-GUI (a GUI), or CFR, Fernflower and Procyon (command-line), reads the bytecode and rebuilds readable Java. What you get back is close to the original but not identical: comments are gone, original local-variable names are usually lost (replaced by generated ones unless a LocalVariableTable attribute was kept), and deliberately obfuscated code decompiles into near-gibberish. For a lower-level view, the JDK ships javap: javap -c -p MyClass disassembles the class into readable bytecode mnemonics and the constant pool without reconstructing source. Opening the raw .class in a text editor just shows binary noise; these tools are the way to inspect it.

Frequently asked questions

Why do I get UnsupportedClassVersionError?

The class was compiled by a newer Java than the JVM you are running. The class file’s major_version encodes the JDK that built it (52 = Java 8, 61 = Java 17, 65 = Java 21), and an older JVM refuses a higher number. Install a JDK at least as new as the one that compiled the class, or recompile the source targeting your version.

What is the difference between a .class and a .jar?

A .class is one compiled class. A JAR is a ZIP archive bundling many .class files plus a manifest that can name the entry-point class, used to ship a whole program or library. Loose .class files mostly appear inside a project’s build folder or inside an unzipped JAR; applications are distributed as JARs, not as bare classes.

References