Javap Tool in Java

Naveen AnamaneniUpdated · 9 min read

javap is the class file disassembler that comes with the JDK. You give it a compiled class, and it prints what is inside the .class file in readable form: the fields and methods, their exact types, the constant pool, and the bytecode instructions of each method. It doesn't give you back the Java source; it shows what the compiler actually produced. That makes it the best tool for answering questions like "what does a record really contain?", "what does + on strings compile to?" or "which Java version built this file?". In this lesson, you will learn the options of javap and how to read each part of its output.

"Bytecode and Platform Independence" used javap -c once. This lesson covers the tool completely.

Syntax

Terminal
javap [options] ClassName
javap [options] path/to/File.class
javap -cp classes [options] com.school.Student

javap takes a class name (looked up on the class path, like java does) or a path to a .class file. It also works on JDK classes: javap java.lang.Runnable.

Options

OptionDescription
(none)It prints the public, protected and package-private members.
-p (-private)It is used to show all members, including private ones.
-cIt prints the bytecode of every method.
-v (-verbose)It prints everything: version, flags, constant pool, stack sizes, attributes.
-sIt prints the internal type descriptors of members.
-lIt prints line number and local variable tables (use with -c).
-constantsIt shows the values of static final constants.
-cp pathIt sets where to look for classes.
-sysinfoIt shows the file's path, size, date and checksum.

Example: The Class We Will Inspect

Student.java
public class Student {
    public static final int MAX_MARKS = 100;
    private final String name;
    private int marks;
 
    public Student(String name, int marks) {
        this.name = name;
        this.marks = marks;
    }
 
    public boolean passed() {
        return marks >= 35;
    }
 
    private String label() {
        return name + ": " + marks;
    }
 
    static double average(int[] all) {
        long sum = 0;
        for (int m : all) {
            sum += m;
        }
        return (double) sum / all.length;
    }
}

Compile it with javac Student.java. Every output below is from that class file.

The Default Output and -p

javap Student
Compiled from "Student.java"
public class Student {
  public static final int MAX_MARKS;
  public Student(java.lang.String, int);
  public boolean passed();
  static double average(int[]);
}

Private members are hidden: this is the class as other classes see it. Add -p to see everything:

javap -p Student
Compiled from "Student.java"
public class Student {
  public static final int MAX_MARKS;
  private final java.lang.String name;
  private int marks;
  public Student(java.lang.String, int);
  public boolean passed();
  private java.lang.String label();
  static double average(int[]);
}

Descriptors with -s

Inside a class file, types are written in a compact form called a descriptor:

javap -s -p Student (trimmed)
  public static final int MAX_MARKS;
    descriptor: I
  private final java.lang.String name;
    descriptor: Ljava/lang/String;
  public Student(java.lang.String, int);
    descriptor: (Ljava/lang/String;I)V
  public boolean passed();
    descriptor: ()Z
  static double average(int[]);
    descriptor: ([I)D
DescriptorType
B, C, S, I, J, F, D, Zbyte, char, short, int, long, float, double, boolean
Vvoid
Ljava/lang/String;The class java.lang.String (L, the name with slashes, then ;)
[Iint[]; each [ adds one array dimension
(Ljava/lang/String;I)VA method taking a String and an int, returning void

Explanation: A method descriptor lists the parameter types inside the parentheses, with no separators, then the return type. ([I)D reads "takes an int[], returns a double". J is long (because L is taken) and Z is boolean (because B is byte). You saw these same strings in method handle types and in Class.getName() for arrays.

Bytecode with -c

javap -c Student (the passed method)
  public boolean passed();
    Code:
         0: aload_0
         1: getfield      #13                 // Field marks:I
         4: bipush        35
         6: if_icmplt     13
         9: iconst_1
        10: goto          14
        13: iconst_0
        14: ireturn

Explanation: aload_0 pushes this. getfield #13 replaces it with the value of this.marks; #13 is an entry in the constant pool, and the comment shows what it means. bipush 35 pushes 35. if_icmplt 13 jumps to position 13 if marks < 35. Otherwise iconst_1 pushes 1 (true) and goto 14 skips ahead. Position 13 pushes 0 (false). ireturn returns the value. The JVM has no boolean instructions: true and false are the int values 1 and 0. "Bytecode Basics in Java" explains the instructions in detail.

Everything with -v

javap -v Student (start, trimmed)
  Compiled from "Student.java"
public class Student
  minor version: 0
  major version: 69
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #8                          // Student
  super_class: #2                         // java/lang/Object
  interfaces: 0, fields: 3, methods: 4, attributes: 3
Constant pool:
   #1 = Methodref          #2.#3          // java/lang/Object."<init>":()V
   #2 = Class              #4             // java/lang/Object
   #3 = NameAndType        #5:#6          // "<init>":()V
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Fieldref           #8.#9          // Student.name:Ljava/lang/String;
   ...
  #13 = Fieldref           #8.#14         // Student.marks:I
  #17 = InvokeDynamic      #0:#18         // #0:makeConcatWithConstants:(Ljava/lang/String;I)Ljava/lang/String;
  #23 = Integer            100
PartMeaning
major version: 69The class file version; 69 is Java 25 (the version is the Java release plus 44)
flagsThe class's modifiers as bits: ACC_PUBLIC, ACC_FINAL, ACC_INTERFACE, ACC_ABSTRACT and so on
this_class, super_classConstant pool entries naming the class and its parent
Constant poolA numbered table of every name, type, string and number the class refers to

Reading the constant pool: entries refer to each other. #13 is a Fieldref made of #8 (the class Student) and #14 (a name-and-type pair, marks:I). Instructions carry only the small number #13, and the JVM looks up the rest. Utf8 entries are the raw text. Entry #17 shows that name + ": " + marks in label() became an invokedynamic call to makeConcatWithConstants, and #23 is the constant 100.

For each method, -v also shows a line such as:

javap -v Student (the passed method)
    descriptor: ()Z
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1

stack=2 is the deepest the operand stack gets, locals=1 is the number of local variable slots, and args_size=1 counts the arguments, where the hidden this is the one argument of an instance method.

Line Numbers and Local Variables with -l

javap -l -c Student (compiled with javac -g, the average method)
      LineNumberTable:
        line 20: 0
        line 21: 2
        line 22: 24
        line 21: 30
        line 24: 36
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           24       6     6     m   I
            0      43     0   all   [I
            2      41     1   sum   J

Explanation: The LineNumberTable maps bytecode positions to source lines; it is where the line numbers in stack traces come from. The LocalVariableTable appears only when the class is compiled with javac -g; debuggers use it to show variable names. Notice the slots: all is in slot 0 (a static method has no this), sum is in slot 1, and because a long takes two slots, the next variables start later.

What Javap Reveals

javap is the quickest way to see what the compiler adds for you. For record Point(int x, int y) { }:

javap -p Point
Compiled from "Point.java"
final class Point extends java.lang.Record {
  private final int x;
  private final int y;
  Point(int, int);
  public final java.lang.String toString();
  public final int hashCode();
  public final boolean equals(java.lang.Object);
  public int x();
  public int y();
}

One line of source produced a final class extending Record, two private final fields, a constructor, accessors, and toString, hashCode and equals. The same approach shows the hidden values() method of enums, bridge methods in generic classes, the synthetic methods behind lambdas, and how a switch on strings uses hashCode.

Common Mistakes

Adding .java or forgetting to compile. javap reads .class files; run javac first.

Expecting source code back. javap is a disassembler, not a decompiler. Tools such as CFR or the IDE's built-in decompiler try to rebuild source.

Forgetting -p. Private fields and methods, including compiler-generated ones, stay hidden without it.

Using -l alone. It needs -c, and local variable names need a class compiled with -g.

Interview Corner

What is javap and what is it used for?

javap is the JDK's class file disassembler. It prints a class's members (-p includes private ones), the bytecode of its methods (-c), and with -v the full contents of the class file: version, access flags, constant pool, descriptors, stack and local sizes, and attributes such as line number tables. It is used to learn how language features compile, verify which Java version a class targets, check what the compiler generated (bridge methods, synthetic members, record methods), and investigate performance or compatibility questions.

What is a method descriptor?

The JVM's internal text form of a method's parameter and return types, such as (Ljava/lang/String;I)V for a method taking a String and an int and returning void. Primitives are single letters (I, J for long, Z for boolean), classes are L + the slash-separated name + ;, and arrays are prefixed with [.

Practice

  1. Easy: Run javap -p java.lang.String | grep -c "private" (or count by eye) to see how many private members String has.
  2. Harder: Write an enum Level { LOW, HIGH }, compile it, and use javap -p Level to list every member the compiler generated. Explain what $VALUES and values() are for.

Quick check: javap

4 questions
1Which option prints bytecode?
2What does the descriptor ([I)D mean?
3Which letter is the descriptor for long?
4Where do stack trace line numbers come from?

Key Takeaways

Was this tutorial helpful?

Lesson 472 of 482