Enums, Exceptions, and Robust Error Handling

Exceptions and Failure Handling

ReadingPreview

আপনি একটি free preview lesson দেখছেন।

Lesson Overview

Software সবসময় happy path অনুযায়ী চলে না।

Examples:

Course not found
Invalid learner ID
Duplicate enrollment
File missing
Database unavailable
Payment failed
Invalid number format
Null object access

একটি program failure encounter করলে তাকে decide করতে হয়:

Can the current method handle the problem?
Should it report the problem to its caller?
Should execution continue?
Should the operation stop?

Java failures represent করার জন্য primarily ব্যবহার করে:

Exception

Exception normal return value থেকে আলাদা একটি failure path তৈরি করে।

এই lesson-এ আমরা শিখব:

  • Normal flow এবং exceptional flow
  • Exception কী
  • Exception object কী information বহন করে
  • Exception hierarchy
  • Checked এবং unchecked exceptions
  • throw এবং throws-এর high-level meaning
  • Stack trace
  • Exceptions কীভাবে method calls-এর মধ্য দিয়ে propagate করে
  • Failure swallow করা dangerous কেন
  • কখন exception এবং কখন normal result ব্যবহার করা উচিত
  • Domain failure, programming error, এবং infrastructure failure-এর পার্থক্য

Detailed try, catch, finally, custom exceptions, এবং robust boundaries পরবর্তী lessons-এ শেখানো হবে।


Learning Objectives

এই lesson শেষে আপনি পারবেন:

  • Exception-এর purpose explain করতে
  • Normal এবং exceptional control flow distinguish করতে
  • Basic Java exception hierarchy বুঝতে
  • Checked এবং unchecked exception identify করতে
  • throw এবং throws distinguish করতে
  • Stack trace-এর গুরুত্বপূর্ণ অংশ পড়তে
  • Exception propagation explain করতে
  • Swallowed exception identify করতে
  • Expected business rejection এবং exceptional failure distinguish করতে
  • Failure contract design-এর basic decisions নিতে

Normal Program Flow

Consider:

public static int calculateTotal(
        int first,
        int second
) {
    int total =
            first + second;

    return total;
}

Call:

int result =
        calculateTotal(
                10,
                20
        );

System.out.println(
        result
);

Flow:

Method called
↓
Values calculated
↓
Result returned
↓
Caller continues

এটি normal flow।


A Failure During Normal Flow

int result =
        10 / 0;

Integer division by zero possible নয়।

Java creates an exception:

ArithmeticException

Normal calculation complete হয় না।

Execution jumps into exceptional flow।


What Is an Exception?

Exception একটি object যা execution-এর সময় একটি problem represent করে।

It can contain:

  • Exception type
  • Failure message
  • Cause
  • Stack trace
  • Additional custom data

Example exception:

java.lang.ArithmeticException: / by zero

এখানে:

ArithmeticException → Problem type
/by zero            → Failure message

Exception Is Not Just an Error Message

Weak mental model:

Exception = Some text printed to console

Better mental model:

Exception is a typed failure object that can travel through method calls until suitable code handles it.

Because exceptions have types, caller can distinguish:

Invalid input
Missing file
Network failure
Illegal state
Database failure

Creating an Exception Object

IllegalArgumentException exception =
        new IllegalArgumentException(
                "Course ID must be positive."
        );

এটি শুধু object তৈরি করে।

Object তৈরি করলেই method stop হয় না।

To signal the failure:

throw exception;

Usually written directly:

throw new IllegalArgumentException(
        "Course ID must be positive."
);

throw

throw একটি specific exception objectকে exceptional flow-এ পাঠায়।

public static void validateCourseId(
        long courseId
) {
    if (courseId <= 0) {
        throw new IllegalArgumentException(
                "Course ID must be positive."
        );
    }
}

Call:

validateCourseId(
        -10L
);

The method does not continue after throw


Code After throw

if (courseId <= 0) {
    throw new IllegalArgumentException(
            "Course ID must be positive."
    );

    System.out.println(
            "Invalid ID"
    );
}

The print statement is unreachable।

Compiler rejects unreachable code।

Once an exception is thrown, normal flow does not continue from the next statement।


Return vs Throw

A method normally returns:

return value;

A failure may exit through:

throw exception;

Conceptually:

Successful path → return
Failure path    → throw

But not every unsuccessful business outcome should become an exception।

We will discuss this distinction later in the lesson।


Automatic Exceptions

Some exceptions are thrown automatically by Java or libraries।

Example:

String name =
        null;

name.toUpperCase();

Java throws:

NullPointerException

Example:

List<String> names =
        List.of(
                "Subu"
        );

names.get(
        10
);

Java throws:

IndexOutOfBoundsException

Example:

Integer.parseInt(
        "Java"
);

Java throws:

NumberFormatException

You do not explicitly write throw, but the called operation does।


The Exception Hierarchy

Java throwable hierarchy-এর simplified form:

Throwable
├── Error
└── Exception
    ├── RuntimeException
    │   ├── IllegalArgumentException
    │   ├── IllegalStateException
    │   ├── NullPointerException
    │   ├── IndexOutOfBoundsException
    │   └── NumberFormatException
    └── Other checked exceptions
        ├── IOException
        └── SQLException

Throwable

Only objects derived from:

Throwable

can be thrown।

Two major branches:

Error
Exception

Application code সাধারণত Exception family নিয়ে কাজ করে।


Error

Examples:

OutOfMemoryError
StackOverflowError
LinkageError

Error often represents serious JVM or environment-level problems।

Application code সাধারণত এগুলোকে routine business failure হিসেবে catch করে recover করার চেষ্টা করবে না।

Example:

catch (
        OutOfMemoryError error
) {
}

usually a bad recovery strategy।


Exception

Exception represents conditions applications may report or handle।

Examples:

Invalid argument
Illegal state
Missing file
Database access problem
Parsing failure

Exception family দুইটি broad group-এ ভাগ করা হয়:

Checked exceptions
Unchecked exceptions

Unchecked Exceptions

Unchecked exceptions are usually subclasses of:

RuntimeException

Examples:

IllegalArgumentException
IllegalStateException
NullPointerException
IndexOutOfBoundsException
NumberFormatException

Compiler callerকে এগুলো catch বা declare করতে বাধ্য করে না।


Example of an Unchecked Exception

public static void validatePrice(
        long priceInPaisa
) {
    if (priceInPaisa < 0) {
        throw new IllegalArgumentException(
                "Price cannot be negative."
        );
    }
}

Caller can simply call:

validatePrice(
        -100L
);

Compiler does not require:

try
catch

or method declaration।


What Unchecked Exceptions Commonly Represent

Unchecked exceptions often represent:

  • Invalid method arguments
  • Invalid object state
  • Broken programming assumptions
  • Contract violations
  • Impossible or unexpected internal conditions

Examples:

IllegalArgumentException

means caller supplied invalid argument।

IllegalStateException

means current object state does not allow the operation।


Checked Exceptions

Checked exceptions are subclasses of Exception but not RuntimeException

Examples:

IOException
SQLException
ClassNotFoundException

Compiler requires the method to either:

Catch the exception

or:

Declare it using throws

A Checked Exception Example

Files.readString(
        path
);

This operation may throw:

IOException

A method using it must handle or declare the failure।

Example declaration:

public static String readContent(
        Path path
) throws IOException {
    return Files.readString(
            path
    );
}

Detailed handling will be covered later।


Checked vs Unchecked: Beginner Mental Model

Checked

The compiler forces explicit acknowledgement।

File access
Some network/library operations
Database APIs

Unchecked

The compiler does not force acknowledgement।

Invalid argument
Invalid state
Programming mistakes

This is a useful starting model, but real-world exception design has trade-offs।


throws

throws method declaration-এ বলে:

This method may complete exceptionally with this exception type, and the caller must deal with that contract where required.

Example:

public static String readFile(
        Path path
) throws IOException {
    return Files.readString(
            path
    );
}

throws does not itself create or throw an exception।

It declares a possible failure।


throw vs throws

throw

Used inside method body:

throw new IllegalArgumentException(
        "Invalid ID."
);

Meaning:

Throw this specific exception object now

throws

Used in method signature:

public void load()
        throws IOException {
}

Meaning:

This method may propagate this exception type

Example Together

public static void validateTitle(
        String title
) throws IllegalArgumentException {
    if (
            title == null
            || title.isBlank()
    ) {
        throw new IllegalArgumentException(
                "Title is required."
        );
    }
}

Here:

throws → Declaration
throw  → Actual failure

Because IllegalArgumentException is unchecked, declaring it with throws is optional।

Often it is omitted:

public static void validateTitle(
        String title
) {
    if (
            title == null
            || title.isBlank()
    ) {
        throw new IllegalArgumentException(
                "Title is required."
        );
    }
}

Exception Propagation

Suppose:

public static void levelThree() {
    throw new IllegalStateException(
            "Publishing is not allowed."
    );
}
public static void levelTwo() {
    levelThree();
}
public static void levelOne() {
    levelTwo();
}

Call:

levelOne();

If no method handles the exception:

levelThree throws
↓
levelTwo exits exceptionally
↓
levelOne exits exceptionally
↓
Caller exits exceptionally

This is exception propagation।


Stack Unwinding

As an exception propagates, active method calls are exited।

This process is often called:

Stack unwinding

Normal statements after the failed call do not execute।

public static void levelTwo() {
    levelThree();

    System.out.println(
            "Finished level two"
    );
}

If levelThree() throws and nothing handles it inside levelTwo(), the print statement does not run।


The Call Stack

When methods call other methods:

main()
  └── publishCourse()
        └── validateCourse()
              └── validateLessons()

Java tracks active calls in the call stack।

If validateLessons() throws, stack trace shows the route through these method calls।


Reading a Stack Trace

Example:

Exception in thread "main" java.lang.IllegalStateException: Course has no lessons.
    at io.liveklass.course.Course.publish(Course.java:52)
    at io.liveklass.service.CourseService.publish(CourseService.java:28)
    at io.liveklass.Main.main(Main.java:15)

Read from top:

Exception type:
IllegalStateException

Message:
Course has no lessons.

Original failure location:
Course.java:52

Caller:
CourseService.java:28

Application entry:
Main.java:15

Start with the First Relevant Application Frame

A long stack trace may include framework and library calls।

Focus first on:

  • Exception type
  • Message
  • First line from your application package
  • Root cause section
  • Method and line number

Do not read only the final line।

The top often shows where the failure originated।


Caused by

Exceptions can wrap another exception।

Example:

java.lang.IllegalStateException: Could not load course.
    at CourseLoader.load(CourseLoader.java:30)
Caused by: java.io.IOException: File not found
    at ...

Meaning:

Higher-level failure:
Could not load course

Underlying cause:
File not found

The cause chain preserves context across abstraction layers।


Exception Message Quality

Weak:

throw new IllegalArgumentException(
        "Invalid"
);

Better:

throw new IllegalArgumentException(
        "Course ID must be positive."
);

Stronger when context is safe and useful:

throw new IllegalArgumentException(
        "Course ID must be positive, but was: "
        + courseId
);

Avoid sensitive data in exception messages:

Passwords
Access tokens
Private personal data
Payment card details

Messages may appear in logs।


Exceptions Should Explain the Broken Contract

Good messages answer:

What was wrong?
What was expected?
Which operation failed?

Examples:

Lesson ID must be positive.
Course cannot be published without lessons.
Enrollment already exists for learner 1001 and course JAVA-OOP.

Avoid vague messages:

Something went wrong.
Error occurred.
Invalid data.

Do Not Use Exceptions for Every Negative Outcome

Suppose:

boolean added =
        course.addLesson(
                lesson
        );

Returning false may be reasonable if duplicate addition is an expected business rejection।

Another design:

throw new DuplicateLessonException(...);

Which is better depends on the contract।


Expected Outcome vs Exceptional Failure

Expected Business Outcome

Examples:

Course code already registered
Learner already enrolled
Search found no match
Coupon not applicable
Login credentials invalid

These may be represented with:

boolean
null
Optional
Result object
Status enum

depending on context।

Exceptional Failure

Examples:

Database unavailable
Corrupted file
Impossible internal state
Required configuration missing
Programming contract violated

Exceptions are more natural।


A Useful Decision Question

Ask:

Is this outcome a normal, expected possibility that callers regularly branch on?

If yes, a result value may be clearer।

Ask:

Does this failure prevent the method from fulfilling its contract and require special handling or propagation?

If yes, an exception may be clearer।


Example: Search Result

Course findByCode(
        CourseCode code
)

Course not found may be a normal lookup result।

Possible representations:

return null;

or later:

Optional<Course>

Throwing an exception for every missing lookup may be excessive if absence is common and expected।


Example: Invalid Argument

findByCode(
        null
)

Null may violate the method contract।

Possible:

throw new IllegalArgumentException(
        "Course code is required."
);

Here the caller supplied an invalid argument rather than a legitimate missing code।


Example: Duplicate Registration

boolean registered =
        catalog.register(
                course
        );

Duplicate registration is an expected domain rejection।

Boolean may be enough if caller only needs success/failure।

If caller needs reason:

RegistrationResult

may be stronger।

An exception may be appropriate at a boundary where duplicate registration violates a required command contract, but it should be deliberate।


Return Values Can Become Ambiguous

Course findCourse(
        CourseCode code
)

Returns null

Does null mean:

Course not found?
Invalid code?
Database failure?
Permission denied?

One sentinel value cannot represent many distinct failures clearly।

Exceptions or structured result types help when failure categories matter।


Boolean Can Lose Failure Details

boolean publish()

false may mean:

Already published
No lessons
Review not approved
Payment configuration missing
Instructor suspended

If caller needs specific action, boolean is insufficient।

Possible improvement:

PublishResult

with status enum।

Exceptions are not the only alternative।


Example Result Type

public enum PublishStatus {

    PUBLISHED,
    ALREADY_PUBLISHED,
    NO_LESSONS,
    NOT_APPROVED
}

Method:

public PublishStatus publish() {
    if (published) {
        return PublishStatus.ALREADY_PUBLISHED;
    }

    if (lessons.isEmpty()) {
        return PublishStatus.NO_LESSONS;
    }

    if (!approved) {
        return PublishStatus.NOT_APPROVED;
    }

    published = true;

    return PublishStatus.PUBLISHED;
}

This is useful when each result is part of normal business flow।


Programming Errors Should Not Be Hidden

Suppose internal code passes an impossible negative ID।

Weak:

if (id <= 0) {
    return false;
}

Caller may ignore false and continue with broken assumptions।

Stronger:

if (id <= 0) {
    throw new IllegalArgumentException(
            "ID must be positive."
    );
}

This fails close to the contract violation।


IllegalArgumentException

Use when a method argument violates required rules।

public Course(
        long id,
        String title
) {
    if (id <= 0) {
        throw new IllegalArgumentException(
                "Course ID must be positive."
        );
    }

    if (
            title == null
            || title.isBlank()
    ) {
        throw new IllegalArgumentException(
                "Course title is required."
        );
    }
}

IllegalStateException

Use when arguments may be valid, but current object state does not allow the operation।

public void publish() {
    if (lessons.isEmpty()) {
        throw new IllegalStateException(
                "Course cannot be published without lessons."
        );
    }
}

Difference:

IllegalArgumentException → Input is invalid
IllegalStateException    → Current object state is invalid for this operation

NullPointerException

Java may throw it automatically when null is dereferenced।

Some APIs intentionally use:

Objects.requireNonNull(...)

Example:

this.courseCode =
        Objects.requireNonNull(
                courseCode,
                "Course code is required."
        );

This throws NullPointerException with a clear message।

For public domain validation, some teams prefer IllegalArgumentException for null arguments।

Consistency matters।


Failure Categories

A useful classification:

Validation Failure

Invalid course ID
Blank title
Negative price

Usually detected immediately।

Business Rejection

Duplicate enrollment
Course not ready to publish
Coupon not applicable

Often expected and representable as a result।

Programming Error

Impossible null
Invalid internal state
Wrong method usage

Usually unchecked exception।

Infrastructure Failure

Database unavailable
Network timeout
File read failure

Often propagated or translated at boundaries।

External Input Failure

Malformed JSON
Unknown enum value
Invalid number

Should become a clear client-facing validation response, not raw stack trace।


Do Not Expose Raw Exceptions to Users

Stack trace:

java.sql.SQLException
at ...

is useful for developers, not end users।

External response should be translated:

Unable to load the course right now.

or:

Course code is invalid.

Internal logs can preserve technical details।


Logging and User Messages Are Different

Internal log:

Failed to load course JAVA-OOP because database connection timed out.

User-facing message:

We could not load the course. Please try again.

Do not leak:

  • Internal class names
  • SQL queries
  • Stack traces
  • File paths
  • Credentials
  • Infrastructure details

Swallowing an Exception

Weak:

try {
    loadCourse();
} catch (
        Exception exception
) {
}

The failure disappears।

Caller may believe operation succeeded।

This is called swallowing the exception।


Why Swallowed Exceptions Are Dangerous

Consequences:

  • No error signal
  • No useful log
  • Partial state may remain
  • Debugging becomes difficult
  • Caller continues with invalid assumptions
  • Data may be silently lost

At minimum, code should have an intentional handling strategy।

Detailed catch design comes next।


Logging and Continuing Can Still Be Wrong

try {
    saveCourse();
} catch (
        Exception exception
) {
    exception.printStackTrace();
}

return true;

The exception is printed, but method reports success।

This is logically inconsistent।

Handling means deciding what the caller should observe, not merely printing the error।


Catching Too Broadly

catch (
        Exception exception
) {
}

This may combine unrelated failures:

Invalid input
Database error
NullPointerException
Programming bug

Broad catches can hide defects and make handling imprecise।

Catch only what the current layer can meaningfully handle।


Catching Throwable

catch (
        Throwable throwable
) {
}

This also catches serious errors।

Routine application code should almost never do this।


Exception Translation

Lower-level exception:

IOException

Higher-level method may translate it:

CourseContentLoadException

Example concept:

try {
    return fileStorage.read(
            path
    );
} catch (
        IOException exception
) {
    throw new CourseContentLoadException(
            "Could not load course content.",
            exception
    );
}

The original exception becomes the cause।

Custom exceptions will be covered later।


Preserve the Original Cause

Weak:

catch (
        IOException exception
) {
    throw new IllegalStateException(
            "Load failed."
    );
}

Original cause is lost।

Better:

catch (
        IOException exception
) {
    throw new IllegalStateException(
            "Load failed.",
            exception
    );
}

Now stack trace includes:

Caused by: IOException

Do Not Use Exceptions as Loop Control

Weak:

for (
        int index = 0;
        ;
        index++
) {
    try {
        System.out.println(
                values.get(
                        index
                )
        );
    } catch (
            IndexOutOfBoundsException exception
    ) {
        break;
    }
}

Better:

for (
        int index = 0;
        index < values.size();
        index++
) {
    System.out.println(
            values.get(
                    index
            )
    );
}

Exceptions should not replace predictable conditions।


Do Not Use Exception for Simple Membership

Weak:

try {
    Course course =
            coursesByCode.get(
                    code
            );

    if (course == null) {
        throw new Exception();
    }
} catch (
        Exception exception
) {
    // Not found
}

Better:

Course course =
        coursesByCode.get(
                code
        );

if (course == null) {
    // Normal not-found handling
}

Fail Fast

Fail fast means detecting invalid state close to where it enters the system।

Weak:

this.title = title;

Failure appears much later:

title.toUpperCase();

Better:

if (
        title == null
        || title.isBlank()
) {
    throw new IllegalArgumentException(
            "Course title is required."
    );
}

Invalid object is never created।


Fail Fast Does Not Mean Crash Carelessly

Fail fast at an internal contract boundary।

Then an outer boundary can:

  • Catch appropriately
  • Log context
  • Translate to an API response
  • Reject a command
  • Roll back a transaction

The goal is not uncontrolled termination।

The goal is early, clear failure detection।


Complete Example: Course Publication Failure

Course.java

package io.liveklass.course;

import java.util.ArrayList;
import java.util.List;

public final class Course {

    private final long id;
    private final String title;
    private final List<String> lessons;

    private CourseStatus status;

    public Course(
            long id,
            String title
    ) {
        if (id <= 0) {
            throw new IllegalArgumentException(
                    "Course ID must be positive."
            );
        }

        if (
                title == null
                || title.isBlank()
        ) {
            throw new IllegalArgumentException(
                    "Course title is required."
            );
        }

        this.id = id;
        this.title = title.strip();

        this.lessons =
                new ArrayList<>();

        this.status =
                CourseStatus.DRAFT;
    }

    public void addLesson(
            String lessonTitle
    ) {
        if (!status.isEditable()) {
            throw new IllegalStateException(
                    "Lessons cannot be changed while course status is "
                    + status.name()
                    + "."
            );
        }

        if (
                lessonTitle == null
                || lessonTitle.isBlank()
        ) {
            throw new IllegalArgumentException(
                    "Lesson title is required."
            );
        }

        lessons.add(
                lessonTitle.strip()
        );
    }

    public void submitForReview() {
        if (
                status
                != CourseStatus.DRAFT
        ) {
            throw new IllegalStateException(
                    "Only a draft course can be submitted for review."
            );
        }

        if (lessons.isEmpty()) {
            throw new IllegalStateException(
                    "Course must contain at least one lesson before review."
            );
        }

        status =
                CourseStatus.REVIEW;
    }

    public void publish() {
        if (
                status
                != CourseStatus.REVIEW
        ) {
            throw new IllegalStateException(
                    "Only a reviewed course can be published."
            );
        }

        status =
                CourseStatus.PUBLISHED;
    }

    public long getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public CourseStatus getStatus() {
        return status;
    }

    public List<String> getLessons() {
        return List.copyOf(
                lessons
        );
    }
}

Calling the Course

public class Main {

    public static void main(
            String[] args
    ) {
        Course course =
                new Course(
                        1L,
                        "Java and OOP Foundation"
                );

        course.submitForReview();

        System.out.println(
                "Course submitted."
        );
    }
}

Because the course has no lesson, exception occurs:

IllegalStateException:
Course must contain at least one lesson before review.

The print statement does not execute।


Stack Trace Example

Exception in thread "main" java.lang.IllegalStateException: Course must contain at least one lesson before review.
    at io.liveklass.course.Course.submitForReview(Course.java:58)
    at io.liveklass.Main.main(Main.java:15)

Interpretation:

Failure type:
IllegalStateException

Reason:
Course has no lesson

Failure origin:
Course.submitForReview()

Caller:
Main.main()

Exception or Result for Publication?

Previous module used:

boolean publish()

Current example uses:

void publish()

and throws on invalid state।

Both designs can be valid।

Boolean Design

boolean published =
        course.publish();

Good when rejection is expected and caller only needs yes/no।

Exception Design

course.publish();

Good when invalid transition violates the method contract and caller expects publication to complete or fail exceptionally।

Structured Result Design

PublishResult result =
        course.publish();

Good when multiple expected rejection reasons need handling।

The important point is consistency and a clear contract।


Document the Failure Contract

A method API should make failure behavior understandable।

Example:

/**
 * Publishes a reviewed course.
 *
 * @throws IllegalStateException
 *         when the course is not in review.
 */
public void publish() {
}

Documentation is especially useful for unchecked exceptions because compiler does not force caller acknowledgement।


Common Mistakes

Throwing Generic Exception

throw new Exception(
        "Invalid course."
);

Too broad and often checked unnecessarily।

Use a meaningful type।


Returning False for Every Programming Error

Can hide contract violations।


Throwing for Every Normal Not-Found Result

Can make expected control flow noisy।


Catching and Ignoring

Failure disappears।


Printing Stack Trace and Reporting Success

Caller receives contradictory result।


Catching Exception Everywhere

Unrelated failures become mixed together।


Catching Throwable

May hide serious JVM errors।


Losing the Original Cause

Makes debugging harder।


Using Exceptions Instead of Conditions

Predictable loops and membership checks should use normal control flow।


Exposing Stack Traces to Users

Leaks implementation details and creates poor user experience।


Vague Failure Messages

Do not explain the broken contract।


Treating Checked as Recoverable and Unchecked as Unrecoverable

The distinction is primarily compiler enforcement, not a complete recovery model।


Practice Exercises

Exercise 1: Invalid Price

Create:

static void validatePrice(
        long priceInPaisa
)

Throw:

IllegalArgumentException

when price is negative।


Exercise 2: Invalid Publication State

Create a course that can publish only from REVIEW

Throw:

IllegalStateException

for every other state।


Exercise 3: Read a Stack Trace

Given:

java.lang.IllegalArgumentException: Learner ID must be positive.
    at EnrollmentRegistry.enroll(EnrollmentRegistry.java:28)
    at EnrollmentService.register(EnrollmentService.java:41)
    at Main.main(Main.java:12)

Identify:

  • Exception type
  • Message
  • Origin
  • Immediate caller
  • Entry point

Exercise 4: Result or Exception?

Choose a representation for each:

  1. Course not found in optional search
  2. Negative course ID
  3. Learner already enrolled
  4. Database connection failed
  5. Course publish rejected for multiple expected reasons
  6. Internal impossible null state

Choose among:

null or Optional
boolean
status enum/result object
unchecked exception
infrastructure exception

Explain each decision।


Exercise 5: Improve Messages

Rewrite these:

Invalid
Error
Failed
Bad value

for:

  • Blank course title
  • Negative price
  • Publishing an empty course
  • Duplicate enrollment

Exercise 6: Preserve Cause

Write a method that catches:

IOException

and throws:

IllegalStateException

while preserving the original exception as cause।


Exercise 7: Find the Swallowed Exception

Explain what is wrong:

try {
    saveCourse();
} catch (
        Exception exception
) {
}

return true;

Predict the Result

Question 1

public static void validate(
        int value
) {
    if (value < 0) {
        throw new IllegalArgumentException(
                "Negative value."
        );
    }

    System.out.println(
            "Valid"
    );
}

validate(
        -1
);

System.out.println(
        "Finished"
);

Answer

An IllegalArgumentException is thrown।

Neither:

Valid

nor:

Finished

is printed, unless an outer caller handles the exception।


Question 2

public static void first() {
    second();

    System.out.println(
            "First complete"
    );
}

public static void second() {
    throw new IllegalStateException(
            "Failure"
    );
}

What happens when first() is called without a handler?

Answer

second() throws।

first() exits exceptionally।

First complete

is not printed।


Question 3

CourseStatus status =
        null;

if (
        status
        == CourseStatus.PUBLISHED
) {
    System.out.println(
            "Published"
    );
}

Answer

No exception।

Condition is false।


Question 4

throw new IllegalArgumentException(
        "Invalid course ID."
);

Does this return an exception object to the caller?

Answer

Not through normal return flow।

It transfers control through exceptional flow।


Question 5

Does a method declaring:

throws IOException

always throw an IOException?

Answer

No।

It declares that the method may propagate that exception।

The method can still complete normally।


Knowledge Check

Question 1

What is an exception?

Question 2

What happens to normal flow after throw?

Question 3

What is the difference between throw and throws?

Question 4

What are the two main branches under Throwable?

Question 5

What is an unchecked exception?

Question 6

What is a checked exception?

Question 7

What is exception propagation?

Question 8

What is stack unwinding?

Question 9

What information should you read first in a stack trace?

Question 10

What does Caused by indicate?

Question 11

Why is swallowing an exception dangerous?

Question 12

When may a result value be better than an exception?

Question 13

When is IllegalArgumentException appropriate?

Question 14

When is IllegalStateException appropriate?

Question 15

Why should the original cause be preserved?


Knowledge Check Answers

Answer 1

A typed object representing a failure during execution।

Answer 2

The current method exits exceptionally unless the failure is handled locally।

Answer 3

throw signals a specific exception now; throws declares a possible propagated exception in the method signature।

Answer 4

Error and Exception

Answer 5

An exception under RuntimeException that the compiler does not force callers to catch or declare।

Answer 6

An exception the compiler requires callers to catch or declare।

Answer 7

An unhandled exception moving through caller methods।

Answer 8

Active method calls exiting as an exception propagates।

Answer 9

Exception type, message, first relevant application frame, and root cause।

Answer 10

A lower-level exception caused the higher-level exception।

Answer 11

The caller may assume success while the real failure disappears।

Answer 12

When the outcome is expected and callers regularly branch on it।

Answer 13

When a supplied method or constructor argument violates the contract।

Answer 14

When the current object state does not allow an operation।

Answer 15

It retains the technical origin and full debugging context।


Lesson Summary

এই lesson-এ আমরা শিখেছি:

  • Normal flow এবং exceptional flow আলাদা
  • Exception একটি typed failure object
  • Exception message, type, cause, এবং stack trace রাখতে পারে
  • throw current operation exceptionally stop করে
  • throws possible propagated failure declare করে
  • Java এবং libraries automatically exceptions throw করতে পারে
  • Throwable থেকে Error এবং Exception derive করে
  • Error সাধারণত serious JVM/environment problem represent করে
  • Checked exceptions compiler-enforced
  • Unchecked exceptions RuntimeException family-এর
  • IllegalArgumentException invalid inputs-এর জন্য useful
  • IllegalStateException invalid operation state-এর জন্য useful
  • Unhandled exceptions caller methods-এর মধ্য দিয়ে propagate করে
  • Propagation-এর সময় stack unwinding হয়
  • Stack trace method-call path এবং failure location দেখায়
  • Caused by underlying failure preserve করে
  • Clear exception messages broken contract explain করে
  • Sensitive information exception messages-এ রাখা উচিত নয়
  • Expected business outcomes সবসময় exceptions হওয়া উচিত নয়
  • Boolean simple হলেও failure details হারাতে পারে
  • Structured result normal multi-outcome workflows-এর জন্য useful
  • Programming contract violations fail fast করা উচিত
  • Raw exceptions end users-এর কাছে expose করা উচিত নয়
  • Swallowed exceptions silent incorrect behavior তৈরি করে
  • Broad exception catches important failures hide করতে পারে
  • Original cause preserve করা debugging-এর জন্য essential
  • Predictable conditions control করতে exceptions ব্যবহার করা উচিত নয়
  • Failure contracts consistent এবং deliberate হওয়া উচিত

Next Lesson

পরবর্তী lesson:

try, catch, finally, throw, and throws

আমরা শিখব:

  • Writing a try block
  • Catching specific exceptions
  • Multiple catch blocks
  • Catch ordering
  • finally
  • Returning from try and catch
  • Exception propagation
  • Rethrowing
  • Wrapping exceptions
  • Resource cleanup
  • Common handling mistakes