ما الذي يسوء أثناء التشغيل
في هذا الدرس 11 موضوعات
هذا الدرس متاح حاليًا باللغة الإنجليزية فقط، ولم تُنشر نسخته العربية بعد.
Lesson Overview
Until now, most of the errors you met were found by the compiler before the program ran. But some problems cannot be seen in advance: the user types letters where a number was expected, a file has gone missing, a list is shorter than you assumed, a customer asks for more money than the account holds. These problems appear only while the program runs.
When one happens, Java does not quietly carry on. It creates an exception, an object that describes what went wrong, and "raises the alarm". Think of a fire alarm in a building: it does not put the fire out, but it stops everyone pretending that nothing is happening. Whether the building then has an emergency plan is up to you. This module teaches you to write that plan.
This first lesson is about the alarm itself: what an exception is, what happens when nobody responds to it, and how to read what the program prints when it stops.
Why this matters: every backend service meets bad input, missing files, and failed connections every day. A service that crashes on the first one is useless, and one that hides them is dangerous.
This lesson builds on "Reading stack traces" (BEG-018) and on the IllegalStateException we threw from withdraw in the encapsulation module. Now we find out what really happens after a throw.
Learning Objectives
By the end of this lesson you will be able to:
- Explain the difference between a compile-time error and a runtime error.
- Say what an exception is and what Java does when one occurs.
- Predict what happens to the rest of the program when an exception is not handled.
- Read the three parts of a stack trace: type, message, and call chain.
- Name common runtime exceptions and the situation behind each.
- Explain why an exception is not the same as a bug, and why it is also not something to hide.
Written Explanation
Two moments when things go wrong
| Compile-time error | Runtime error | |
|---|---|---|
| When it appears | While you compile | While the program runs |
| Who finds it | The compiler | The JVM |
| Typical cause | Wrong types, missing semicolon, unknown name | Bad input, missing file, wrong index, a value that is not what you assumed |
| Result | No program is produced | The program starts, then fails part-way |
A compile-time example, using a string where an int is required:
D.java:1: error: incompatible types: String cannot be converted to intThe compiler can see this from the code alone. It cannot see what a user will type next Tuesday, so a failing Integer.parseInt(userText) is a runtime problem.
What is an exception?
An exception is an object that represents a problem which occurred while the program was running. The Oracle Java tutorial defines it as an event that disrupts the normal flow of the program's instructions. When a method hits a problem it cannot continue past, Java throws the exception: it stops the normal flow, packages the details into an object, and starts looking for code that knows how to deal with it.
That object holds three useful things:
- Its type, such as
ArithmeticException. The type is the name of the problem. - A message, a short text such as
/ by zero. The message may be missing for some exceptions. - A stack trace: a snapshot of the chain of method calls at the moment the exception was created.
Some exceptions are thrown by Java itself (dividing an int by zero, an index outside an array). Others are thrown by code, including code you write: in the encapsulation module you wrote throw new IllegalStateException(...) when a withdrawal was too large. Both kinds behave identically.
What happens when nobody handles it
When a method throws an exception, Java looks at the method that called it. If that method has no plan, Java looks at its caller, and so on down the call stack (BEG-014) to main. This search is called propagation. If nobody along the way handles the exception, then:
- The method where it was thrown stops immediately. No further lines in it run.
- Each calling method stops too, as the exception passes through.
- When the exception reaches the top, the JVM prints the exception type, message, and stack trace to the error output.
- The thread ends. For a simple program with one thread, the program ends.
So an unhandled exception means: everything after the failing line is skipped. Our first example makes this visible.
Reading the trace: top first
A stack trace has a fixed shape (BEG-018 covered the details):
Exception in thread "main" java.lang.ArithmeticException: / by zero
at B.average(B.java:3)
at B.report(B.java:7)
at B.main(B.java:12)- The first line names the exception type and, after the colon, the message.
- Each
atline is one call, most recent first. The firstatline is where the exception was created. The last is the oldest call, usuallymain. - The file name and line number let you jump to the exact line.
Read it from the top: what happened (first line), where (first at line), and how we got there (the lines below).
A short field guide
You will meet these constantly. Each names one kind of runtime problem:
| Exception | Typical situation |
|---|---|
ArrayIndexOutOfBoundsException | Using an index outside the array |
ArithmeticException | Integer division by zero |
NullPointerException | Using a null reference as if it were an object |
NumberFormatException | Text such as "3 pens" converted to a number |
IllegalArgumentException | A method received an argument it cannot accept |
IllegalStateException | A method was called when the object's state does not allow it |
java.io.IOException and its family | Reading or writing files or the network failed |
Not all of these have the same cause. Some point to a bug in the program (a wrong index), some to bad outside data (text from a user), and some to the world misbehaving (a disk that is full). The next lesson sorts them into families, and later lessons show that each family has a different best response.
An exception is not a failure of the program
An exception is Java's way of saying "the normal path cannot continue here". A program that reports a problem clearly is working correctly. The failures to worry about are the hidden ones: they leave the program running in a wrong state with nobody told.
Examples
Example 1: an index that does not exist (simple)
public class A {
public static void main(String[] args) {
int[] nums = new int[3];
System.out.println("Before");
System.out.println(nums[5]);
System.out.println("After");
}
}Real output:
Before
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 3
at A.main(A.java:5)Valid indexes are 0, 1, and 2, so nums[5] cannot be read. The line "Before" was printed, because it ran before the failure. "After" never appears: the exception left main and the program ended. The message gives the bad index (5) and the array length (3), so you do not need a debugger to see what went wrong.
Example 2: a failure three calls deep (practical)
public class B {
static int average(int total, int count) {
return total / count;
}
static void report(int total, int count) {
System.out.println("Average: " + average(total, count));
}
public static void main(String[] args) {
report(90, 3);
report(0, 0);
System.out.println("Report finished");
}
}Real output:
Average: 30
Exception in thread "main" java.lang.ArithmeticException: / by zero
at B.average(B.java:3)
at B.report(B.java:7)
at B.main(B.java:12)The first report call works. The second asks for 0 / 0, and integer division by zero throws ArithmeticException. The trace has three at lines, which is the call stack at that moment: main called report (line 12), which called average (line 7), where the division is (line 3). "Report finished" is never printed.
Example 3: a rule broken in the bank (realistic)
Here is the withdraw check from the encapsulation module, run without any plan to handle it:
public class C {
static class Account {
private double balance;
Account(double balance) { this.balance = balance; }
void withdraw(double amount) {
if (amount > balance) {
throw new IllegalStateException("Insufficient funds: balance " + balance + ", requested " + amount);
}
balance -= amount;
}
}
public static void main(String[] args) {
Account a = new Account(120);
a.withdraw(50);
System.out.println("First withdrawal ok");
a.withdraw(500);
System.out.println("Second withdrawal ok");
}
}Real output:
First withdrawal ok
Exception in thread "main" java.lang.IllegalStateException: Insufficient funds: balance 70.0, requested 500.0
at C$Account.withdraw(C.java:7)
at C.main(C.java:17)The rule worked: the balance was protected and the message names both numbers. But the whole program ended just because one customer asked for too much. A real bank would show an error to that customer and carry on with everyone else. That is a plan to handle the exception, which is what the next lessons teach. (The C$Account form is how Java names a class declared inside another class.)
Backend Insight
A backend service runs for days and serves thousands of people. If one request triggers an exception that nothing handles, you need that to stay contained: one failed request, not a dead service. Frameworks you will use later catch exceptions at the edge, turn them into an error response such as "400 Bad Request" or "500 Internal Server Error", and write the stack trace to a log. In both cases the trace you learned to read today is the first thing an engineer opens when something breaks in production.
Two habits start here: read the first line and the first at line before touching anything, and treat the message as part of your design, because a tired person will read it at night and it should say what happened and with which values.
Common Mistakes
Mistake 1: Reading the trace from the bottom
- Incorrect: start at
mainand walk down. - Correct: start at the first line (type and message), then the first
atline (the place of failure), and only then look further down for who called it.
Mistake 2: Assuming the line after the failure still runs
In Example 1, "After" is never printed. Code following a line that throws an unhandled exception does not run. Do not expect cleanup or a final message to happen on its own.
Mistake 3: Treating the crash as the problem
The crash is the symptom. In Example 2, the real problem is that a count of zero reached average; ask where the bad value came from.
Mistake 4: Hiding exceptions to make the output look clean
Removing a stack trace without fixing or reporting the cause turns a visible problem into an invisible one. The last lesson of this module returns to this.
Exercises
Try each exercise yourself before opening the solutions.
Level A: Foundation
A1. Classify each as compile-time or runtime: (a) a missing semicolon; (b) reading scores[10] when the array has 5 elements; (c) calling an undefined method name; (d) Integer.parseInt("12.5").
أظهر الحل A1
A1. (a) Compile-time. (b) Runtime. (c) Compile-time. (d) Runtime: the text is only known when the program runs, and "12.5" is not a whole number.
A2. Read this first line of a trace: Exception in thread "main" java.lang.NumberFormatException: For input string: "abc". What is the type, and what is the message?
أظهر الحل A2
A2. The type is java.lang.NumberFormatException; the message is For input string: "abc".
A3. An exception is thrown at line 4 of a method. Do lines 5 and 6 of the same method run?
أظهر الحل A3
A3. No. When the exception is thrown the method stops immediately, so lines 5 and 6 are skipped (unless a handler exists, which we have not written yet).
Level B: Application
B1. Given this trace, name the method where the exception was created, the method that called it, and the file line to open first:
Exception in thread "main" java.lang.IllegalArgumentException: Quantity must be positive: -3
at Cart.addItem(Cart.java:12)
at Shop.checkout(Shop.java:30)
at Shop.main(Shop.java:8)أظهر الحل B1
B1. Created in Cart.addItem, line 12 of Cart.java. It was called by Shop.checkout, line 30 of Shop.java. Open Cart.java line 12 first, because that is where it was thrown, then check line 30 in Shop.java to find out why the quantity was negative.
B2. What is printed by this program, and why?
public class T {
public static void main(String[] args) {
String text = "7";
System.out.println("Start");
System.out.println(10 / (Integer.parseInt(text) - 7));
System.out.println("Done");
}
}أظهر الحل B2
B2. Output: Start, then the exception java.lang.ArithmeticException: / by zero with a trace pointing at the println line. Integer.parseInt("7") - 7 is 0, so 10 / 0 throws. "Done" is never printed.
B3. Give one runtime exception from the field guide that most likely points to a bug in the code, and one that most likely points to bad outside data. Justify each in one sentence.
أظهر الحل B3
B3. ArrayIndexOutOfBoundsException (or NullPointerException) usually means the code assumed something false, so it is a bug. NumberFormatException usually means the text came from outside (a user or a file) and was not in the expected form.
Level C: Challenge
C1. Write a program with three methods (a calls b, b calls c) where c triggers an ArithmeticException. Run it, copy the trace, and mark which at line corresponds to each method.
أظهر الحل C1
C1. Example solution:
public class Chain {
static int c(int x) { return 100 / x; }
static int b(int x) { return c(x) + 1; }
static int a(int x) { return b(x) + 1; }
public static void main(String[] args) { System.out.println(a(0)); }
}The trace has at Chain.c, then at Chain.b, then at Chain.a, then at Chain.main, in that order, newest first.
C2. "An exception means my program is broken." Write three sentences agreeing or disagreeing, using the bank example.
أظهر الحل C2
C2. Disagree. The exception in Example 3 shows a rule being enforced: the account refused an overdraw and protected the balance. What is missing is a plan for the caller, so the program does not stop for everyone. Reporting a problem is correct behaviour; ignoring it is the real danger.
Mini Challenge
Create a small program of your own (about 15 lines) that has a main and at least two helper methods, and deliberately triggers a runtime exception two calls deep. Then:
- Run it and copy the full output.
- Underline the type, the message, and the line where it happened.
- Add one
System.out.printlnafter the failing call, and confirm it never runs. - In two sentences, say whether the cause was a bug, bad outside data, or the world misbehaving, and what you would change.
Check your work: you identified the type, message, and first at line correctly, and your program does not print the line after the failure.
Summary & Next Lesson
Key takeaways
- Compile-time errors are caught before the program runs; runtime errors appear only while it runs.
- An exception is an object describing a runtime problem. It has a type, usually a message, and a stack trace.
- If nothing handles it, the exception propagates up the call stack, each method stops, and the program ends with a printed stack trace.
- Read the first line (what), the first
atline (where), and then the rest (how we got there). - Exceptions come from bugs, bad outside data, or the environment. Reporting them is correct; hiding them is the real danger.
Next lesson
You now know that exceptions exist and what an unhandled one does. The next lesson, "The exception hierarchy", sorts them into families: checked exceptions, unchecked exceptions, and errors, and explains why Java treats each family differently.
Self-check
- What is the difference between a compile-time error and a runtime error?
- What three things does an exception carry?
- When an exception is not handled in a method, where does Java look next?
- In a stack trace, which
atline is where the exception was created? - Why is "removing the stack trace" not a fix?
أظهر الإجابات
- A compile-time error is found by the compiler before the program runs; a runtime error appears while it runs, often because of data the compiler cannot know.
- A type, a message (which can be missing), and a stack trace.
- In the method that called it, then its caller, and so on up the call stack to
main. - The first (top) one.
- The problem still exists and now nobody knows about it; the program may carry on in a wrong state.
References
- Oracle, The Java Tutorials, What Is an Exception?. Fetched. Supports the definition of an exception as an event that disrupts normal flow, and the search back through the call stack for a handler, ending the program if none is found.
- Oracle, The Java Tutorials, The Catch or Specify Requirement. Fetched. Supports the description of runtime exceptions as internal problems such as programming bugs, and errors as external problems.
- Oracle, Java SE 21 API, Throwable. Fetched. Supports that a throwable holds a snapshot of the call stack taken when it is created, with index 0 the most recent call.
- All output in the examples was produced by compiling and running the code with JDK 21.0.10.
التطبيق
طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.
تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.