JAVA / GETTING STARTED
Finding help in error messages and the standard library docs
Turn a javac error or a stack trace into a precise location and claim, then confirm the fix against the JDK API docs for the method that failed.
What you will learn
- Split a javac error into file, line, caret column, and the symbol it could not resolve
- Fix the first error and recompile; later errors are usually cascade damage
- Find the topmost stack frame in your own file to locate the caller that failed
- Read a method's @throws and @since entries before assuming your own code is wrong
Understanding Finding help in error messages and the standard library docs
javac reports the first point at which its model of your program stopped making sense, which is not always where you typed the mistake. Each report has the shape File.java:LINE: error: CLAIM, followed by the offending source line and a caret under the exact column, and for unresolved names two extra lines such as `symbol: variable conut` and `location: class Main`. Those two lines are the useful part, because they say what javac looked for and in which scope it looked, so `cannot find symbol` on a name you did declare normally means wrong case, wrong scope, or a missing import. A stray closing brace or a missing semicolon can produce twenty errors, so fix the first one and recompile before reading the rest: most of the others exist only because the parser lost its place.
A runtime failure prints one header line and then a list of frames. The header is `Exception in thread "main"`, then the fully qualified exception class, then the message after the colon: the class is the searchable name and the category of failure, while the message carries your actual values. The frames are the call chain captured when the exception object was constructed, innermost first, so a top frame like `at java.base/java.lang.Integer.parseInt(...)` does not mean the JDK is broken, it means parseInt was handed something its contract forbids; scan down to the first frame naming one of your files and start there. When a library wraps a failure, the original one is in the `Caused by:` block near the bottom, so read that block before the top one.
Every class page in the standard library docs is laid out the same way: package and type hierarchy at the top, a method summary table, then one detail entry per method with @param, @return and @throws. The @throws prose is what you need while reading a stack trace, because unchecked exceptions are often absent from the signature and only the text states the condition: parseInt refuses a leading or trailing space, and substring throws once the index passes the string's length. The docs are versioned, so check `java -version` and open the matching API page; the @since tag explains why an example copied from elsewhere fails with `cannot find symbol`, since String.repeat needs Java 11 and List.of needs Java 9. With no browser, `javap java.lang.Integer` prints the signatures straight out of the class file.
public class ErrorMessages {
public static void main(String[] args) {
int[] ports = {80, 443};
try {
System.out.println(ports[2]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("class : " + e.getClass().getName());
System.out.println("message: " + e.getMessage());
}
try {
System.out.println(Integer.parseInt("8o80"));
} catch (NumberFormatException e) {
System.out.println("class : " + e.getClass().getName());
System.out.println("message: " + e.getMessage());
System.out.println("parent : " + e.getClass().getSuperclass().getName());
}
}
}Compiler errors and stack traces are structured data, a location plus a claim plus a class name you can look up, not prose to skim.
Worked examples
Reading a stack trace as data
Inspects the frames of a caught exception to show which method threw it and where the call chain began.
public class TraceReading {
public static void main(String[] args) {
try {
loadConfig("timeout=abc");
} catch (IllegalArgumentException e) {
StackTraceElement[] frames = e.getStackTrace();
System.out.println("message: " + e.getMessage());
System.out.println("top frame: " + frames[0].getMethodName() + " line " + frames[0].getLineNumber());
System.out.println("depth: " + frames.length);
System.out.println("bottom frame: " + frames[frames.length - 1].getMethodName());
}
}
static void loadConfig(String line) {
parseTimeout(line.substring(line.indexOf('=') + 1));
}
static void parseTimeout(String text) {
for (int i = 0; i < text.length(); i++) {
if (!Character.isDigit(text.charAt(i))) {
throw new IllegalArgumentException("timeout must be digits, got '" + text + "'");
}
}
}
}Example explained
Line 1getStackTrace() hands you the same frames the JVM would print, so you can pick them apart instead of eyeballing text.
Line 2frames[0] is parseTimeout because the array is filled in when the exception object is constructed, not when it is caught.
Line 3Line 21 is the throw statement in the file exactly as shown above; edit the layout and the reported number moves, which is why traces from stale class files mislead you.
Line 4depth 3 and a bottom frame of main match the chain main -> loadConfig -> parseTimeout.
What a signature does and does not tell you
Compares two library methods to show that unchecked exceptions never appear in the signature, only in the documentation.
import java.lang.reflect.Method;
public class SignatureLookup {
public static void main(String[] args) throws NoSuchMethodException {
Method parseInt = Integer.class.getMethod("parseInt", String.class);
Method substring = String.class.getMethod("substring", int.class);
System.out.println("parseInt returns " + parseInt.getReturnType().getName());
System.out.println("parseInt declares " + parseInt.getExceptionTypes().length + " exception type(s)");
System.out.println("substring returns " + substring.getReturnType().getName());
System.out.println("substring declares " + substring.getExceptionTypes().length + " exception type(s)");
}
}Example explained
Line 1getMethod("parseInt", String.class) selects the one-argument overload; parameter types are also how you pick the right row in the docs' method summary.
Line 2parseInt counts one exception type because its source really is declared `throws NumberFormatException`, even though that exception is unchecked.
Line 3substring counts zero, yet it still throws StringIndexOutOfBoundsException at runtime, because unchecked exceptions need no throws clause.
Line 4That gap is the reason the @throws prose, not the signature, is the authoritative list of ways a call can fail.
Important notes
Message wording is an implementation detail, not API: charAt's out-of-range text was reworded between releases, and the helpful NullPointerException text that names an expression arrived only in recent JDKs and shows locals as <local1> unless you compile with javac -g. Branch on the exception type, never on getMessage() content.
`Could not find or load main class X` and `Main method not found in class X` come from the java launcher, not javac, so compilation already succeeded and the problem is the class name, the working directory, or the classpath.
Common mistakes
Starting with the last javac error: one unbalanced brace makes every following method look broken, so those errors disappear once the first is fixed, and the edits made to silence them stay behind as new bugs.
Treating the top frame as the bug: with `at java.base/java.lang.Integer.parseInt` on top, beginners rewrite parsing logic instead of printing the string being parsed, so the real cause, a trailing space or an empty field, survives the fix.
Searching the message with your own values still in it, or reading docs for a different release: `Index 7 out of bounds for length 3` matches nothing useful, and a method marked @since 11 looks like a typo (`cannot find symbol`) when compiled on Java 8.
Try it yourself
Change, predict, then run
In a browser editor, compile a class that contains both a misspelled type such as `Sting name = "x";` and the call `Integer.parseInt("7 ")`. Fix the typo using only the symbol/location lines, then add a comment naming the exception class of the remaining failure and the sentence in parseInt's documentation that predicts it.
Open the Java workspaceCheck your understanding
A program dies with this output: Exception in thread "main" java.lang.NumberFormatException: For input string: "12 " at java.base/java.lang.Integer.parseInt(Integer.java:652) at Report.readCount(Report.java:14) at Report.main(Report.java:5) Which line do you inspect first, and why?
- Report.java line 14, because it is the highest frame in code you own and it supplied the argument parseInt rejected.
- Integer.java line 652, because the topmost frame is where the exception was created.
- Report.java line 5, because main started the call chain that ended in the failure.
- None of them: the message shows the JDK cannot parse "12", so the JDK install is broken.
Show answer
Frames are listed innermost first, so Integer.java:652 is merely where the exception object was constructed; parseInt is documented to reject any string that is not all digits, including the trailing space in "12 ", so it behaved correctly. Line 14 is the first frame you can actually change and the place that string was handed over, whereas main at line 5 sits at the bottom of every trace and identifies nothing specific.