انتقل إلى المحتوى
أمين أبوزيد
منشورBEG-029-L01

ما الذي يسوء أثناء التشغيل

  • الدرس 1 من 8
  • 12 دقيقة قراءة
  • مبتدئ
في هذا الدرس 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 errorRuntime error
When it appearsWhile you compileWhile the program runs
Who finds itThe compilerThe JVM
Typical causeWrong types, missing semicolon, unknown nameBad input, missing file, wrong index, a value that is not what you assumed
ResultNo program is producedThe program starts, then fails part-way

A compile-time example, using a string where an int is required:

text
D.java:1: error: incompatible types: String cannot be converted to int

The 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:

  1. Its type, such as ArithmeticException. The type is the name of the problem.
  2. A message, a short text such as / by zero. The message may be missing for some exceptions.
  3. 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:

  1. The method where it was thrown stops immediately. No further lines in it run.
  2. Each calling method stops too, as the exception passes through.
  3. When the exception reaches the top, the JVM prints the exception type, message, and stack trace to the error output.
  4. 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):

text
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 at line is one call, most recent first. The first at line is where the exception was created. The last is the oldest call, usually main.
  • 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:

ExceptionTypical situation
ArrayIndexOutOfBoundsExceptionUsing an index outside the array
ArithmeticExceptionInteger division by zero
NullPointerExceptionUsing a null reference as if it were an object
NumberFormatExceptionText such as "3 pens" converted to a number
IllegalArgumentExceptionA method received an argument it cannot accept
IllegalStateExceptionA method was called when the object's state does not allow it
java.io.IOException and its familyReading 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)

java
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:

text
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)

java
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:

text
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:

java
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:

text
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 main and walk down.
  • Correct: start at the first line (type and message), then the first at line (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:

text
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?

java
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:

java
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:

  1. Run it and copy the full output.
  2. Underline the type, the message, and the line where it happened.
  3. Add one System.out.println after the failing call, and confirm it never runs.
  4. 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 at line (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

  1. What is the difference between a compile-time error and a runtime error?
  2. What three things does an exception carry?
  3. When an exception is not handled in a method, where does Java look next?
  4. In a stack trace, which at line is where the exception was created?
  5. Why is "removing the stack trace" not a fix?
أظهر الإجابات
  1. 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.
  2. A type, a message (which can be missing), and a stack trace.
  3. In the method that called it, then its caller, and so on up the call stack to main.
  4. The first (top) one.
  5. 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.

التطبيق

طبّق هذا الدرس بمختبر الوحدة وتمارينها واختبارها.

المختبر والتمارين الاختبار

تعليم الدرس كمكتمل اختياري ويمكنك التراجع عنه في أي وقت. يبقى تقدمك محفوظًا في هذا المتصفح.

اكتب للبحث في المستويات والمراحل والوحدات والدروس.