Exceptions signal errors. Checked exceptions (such as IOException) must be caught or declared with throws — the compiler enforces it. Unchecked exceptions (subclasses of RuntimeException, such as IllegalArgumentException) usually indicate programming errors or invalid input and don't need declaring.
Catch specific exceptions you can handle; let others propagate. Multi-catch (catch (A | B e)) handles several types the same way. finally always runs.
Try-with-resources (try (var reader = ...)) closes files, connections and streams automatically, even when an exception occurs. Define custom exceptions for your domain and keep the original exception as the cause.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.Map;
public class Errors {
static class StudentNotFoundException extends RuntimeException {
StudentNotFoundException(String id) { super("Student " + id + " not found"); }
}
static class InvalidScoreException extends Exception { // checked
InvalidScoreException(String message, Throwable cause) { super(message, cause); }
}
static final Map<String, String> STUDENTS = Map.of("S1", "Amina");
static String findStudent(String id) {
String name = STUDENTS.get(id);
if (name == null) throw new StudentNotFoundException(id);
return name;
}
static int parseScore(String text) throws InvalidScoreException {
try {
int score = Integer.parseInt(text.trim());
if (score < 0 || score > 100) throw new InvalidScoreException("Out of range: " + score, null);
return score;
} catch (NumberFormatException e) {
throw new InvalidScoreException("Not a number: '" + text + "'", e);
}
}
public static void main(String[] args) {
for (String raw : new String[]{"88", "abc", "150"}) {
try {
System.out.println("OK " + parseScore(raw));
} catch (InvalidScoreException e) {
System.out.println("Invalid: " + e.getMessage()
+ (e.getCause() != null ? " (cause: " + e.getCause().getClass().getSimpleName() + ")" : ""));
} finally {
System.out.println(" checked " + raw);
}
}
try {
findStudent("S9");
} catch (StudentNotFoundException e) {
System.out.println("404: " + e.getMessage());
}
String csv = "Amina,88\nJuma,42\n";
try (BufferedReader reader = new BufferedReader(new StringReader(csv))) { // closed automatically
String line;
while ((line = reader.readLine()) != null) {
System.out.println("Read: " + line);
}
} catch (IOException e) {
System.out.println("Could not read: " + e.getMessage());
}
}
}Key points
- Checked exceptions must be handled or declared; unchecked ones signal bugs or bad input.
- Catch only what you can handle, and keep the cause when wrapping exceptions.
- Use try-with-resources for anything that must be closed.
Exercise
Write parseLine(String line) that turns "Amina,88" into a Result record, throwing a custom checked InvalidLineException for missing commas, blank names or bad scores. Process several lines and report how many failed.
Show solution
Try the exercise yourself first — then compare your approach with this one.
InvalidLineException extends Exception, so it's checked: parseLine must declare it and callers must handle it — the compiler won't let anyone forget. The original NumberFormatException is kept as the cause.
import java.util.ArrayList;
import java.util.List;
public class ParseLines {
record Result(String name, int score) {}
static class InvalidLineException extends Exception {
InvalidLineException(String message, Throwable cause) { super(message, cause); }
InvalidLineException(String message) { this(message, null); }
}
static Result parseLine(String line) throws InvalidLineException {
int comma = line.indexOf(',');
if (comma < 0) throw new InvalidLineException("missing comma in '" + line + "'");
String name = line.substring(0, comma).strip();
if (name.isEmpty()) throw new InvalidLineException("blank name in '" + line + "'");
try {
int score = Integer.parseInt(line.substring(comma + 1).strip());
if (score < 0 || score > 100) throw new InvalidLineException("score out of range in '" + line + "'");
return new Result(name, score);
} catch (NumberFormatException e) {
throw new InvalidLineException("bad score in '" + line + "'", e);
}
}
public static void main(String[] args) {
List<Result> valid = new ArrayList<>();
int failed = 0;
for (String line : List.of("Amina,88", "Juma 42", " ,70", "Neema,abc", "Ali,150", "Rehema, 71")) {
try {
valid.add(parseLine(line));
} catch (InvalidLineException e) {
failed++;
System.out.println("Skipped: " + e.getMessage());
}
}
System.out.println(valid);
System.out.println("Failed: " + failed); // Failed: 4
}
}