Object-Oriented Programming (Java/Python)

Chapter 8
Exceptions, Contracts, and Resource Management

Bachelors Program in Full Stack Development
Object-Oriented Programming (Java/Python)   •   Bachelor

Exceptions, Contracts, and Resource Management

Introduction

Reliable software is not defined only by the successful execution of its main path. Real programs encounter invalid input, missing files, unavailable networks, exhausted memory, violated business rules, and failures in external services. Object-oriented programs must therefore represent failure explicitly, preserve useful diagnostic information, protect object invariants, and release resources safely.

This chapter develops a systematic approach to exceptions, contracts, and resource management in Java and Python. It distinguishes failures that callers can reasonably recover from those that should be reported or allowed to terminate an operation. It also examines defensive programming: anticipating misuse and environmental uncertainty without producing code that silently conceals defects.

An exception is a mechanism for transferring control from the point where an abnormal condition is detected to code capable of handling it. A contract specifies what a method or class promises, what it requires from callers, and what it guarantees after successful completion. Resource management ensures that files, database connections, locks, sockets, and similar resources are released even when an operation fails.

Learning Objectives

By the end of this chapter, you should be able to:

  • explain the role and anatomy of exceptions;
  • distinguish checked and unchecked exceptions in Java and compare them with Python’s exception model;
  • design meaningful exception hierarchies;
  • express and validate preconditions, postconditions, and invariants;
  • decide whether an error should be handled, translated, propagated, or reported;
  • manage files and other resources safely;
  • apply defensive programming without hiding failures;
  • evaluate exception-handling designs in professional software systems.

1. Exceptions as a Control-Flow and Communication Mechanism

A conventional method communicates through its return value. This is appropriate when all outcomes belong to the method’s ordinary domain. For example, a search operation may return a matching object or an empty result. Exceptions are appropriate when normal continuation is impossible, when a caller must make a distinct decision, or when the failure is too unusual to encode clearly as an ordinary value.

An exception normally contains:

  1. a type identifying the category of failure;
  2. a message providing human-readable context;
  3. a stack trace showing the path of execution;
  4. optionally, a cause representing a lower-level failure.

The stack trace is diagnostic evidence, not merely an error message. It should be preserved when an exception is propagated or translated. Replacing a useful exception with a generic message such as “Something went wrong” discards information needed for debugging and operations.

Normal flow and exceptional flow

Consider a file-reading operation. Its normal flow is:

  1. locate the file;
  2. open it;
  3. read bytes or characters;
  4. parse the content;
  5. return a result.

Possible exceptional conditions include a missing file, inadequate permissions, malformed content, or an interrupted read. These conditions do not all have the same meaning. A missing optional configuration file might have a documented default. A malformed security policy should usually stop startup rather than be silently ignored.

Exceptions should therefore communicate a failure to the layer that has sufficient information to decide what to do. A low-level file-access method may know that an operating-system error occurred, but a higher-level configuration service may know whether a default is safe.

2. Java and Python Exception Models

Java

Java exceptions are objects derived ultimately from Throwable. The principal branches are Error and Exception. Error generally represents serious conditions that application code should not attempt to recover from, such as virtual-machine failure. Application code normally uses subclasses of Exception.

Java distinguishes checked and unchecked exceptions:

  • Checked exceptions are subclasses of Exception that are not subclasses of RuntimeException. A method must catch them or declare them with throws.
  • Unchecked exceptions include RuntimeException and its subclasses. They commonly represent programming errors, invalid arguments, or violated assumptions, although the distinction is not a perfect classification of recoverability.

For example:

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

The declaration tells callers that file access may fail and forces them to choose a response. A caller may handle the exception:

try {
    String text = loader.loadText(path);
    process(text);
} catch (IOException ex) {
    logger.warn("Could not read configuration: {}", path, ex);
    useDocumentedDefault();
}

Java’s try, catch, and finally statements support explicit handling. Modern Java normally uses try-with-resources for closeable resources:

try (BufferedReader reader = Files.newBufferedReader(path)) {
    return reader.readLine();
} catch (IOException ex) {
    throw new ConfigurationException("Unable to read " + path, ex);
}

The original IOException is supplied as the cause. If closing also fails, try-with-resources preserves the closing exception as a suppressed exception rather than discarding it.

Python

Python exceptions are instances of classes derived from BaseException. Application exceptions normally derive from Exception; classes such as SystemExit, KeyboardInterrupt, and GeneratorExit are intentionally outside that branch and should generally not be caught by broad application handlers.

Python uses try, except, else, and finally:

try:
    with path.open(encoding="utf-8") as file:
        content = file.read()
except FileNotFoundError as exc:
    raise ConfigurationError(f"Missing configuration: {path}") from exc
else:
    return parse(content)

The with statement uses the context-manager protocol. An object supplies __enter__() and __exit__() methods, allowing cleanup to occur even when the body raises an exception. The raise ... from exc syntax explicitly records exception causation.

Python also supports else, whose block executes only if the protected body succeeds. This can make the scope of exception handling precise:

try:
    value = int(text)
except ValueError:
    report_invalid_value(text)
else:
    store(value)

Only conversion is protected, so an unexpected error in store is not incorrectly interpreted as an invalid integer.

Comparison

ConcernJavaPython
Main application baseExceptionException
Compile-time checked exceptionsYesNo
Resource constructtry-with-resourceswith context manager
Explicit cause chainingconstructor cause, initCauseraise ... from ...
Broad catch riskcatch (Exception)except Exception

Checked exceptions provide compiler-enforced documentation, but they can encourage unhelpful wrapping or excessive declarations. Python’s flexibility reduces syntactic burden but places greater responsibility on documentation, tests, and design discipline.

3. Exception Hierarchies and Taxonomy

An exception hierarchy should reflect distinctions that matter to callers. A useful hierarchy is neither a mirror of every low-level technical detail nor a single universal exception type. It groups failures according to the decisions a caller may need to make.

For an order-processing application, a domain hierarchy might be:

ApplicationError
├── ValidationError
│   ├── InvalidAddressError
│   └── InvalidQuantityError
├── ResourceUnavailableError
│   ├── InventoryUnavailableError
│   └── PaymentServiceUnavailableError
└── PersistenceError
    ├── OrderNotFoundError
    └── OrderStoreFailure

The hierarchy distinguishes invalid data, temporary unavailability, and persistence problems. A user interface may display validation errors directly, retry a temporary service failure, and log a persistence failure for operational investigation.

Java example:

public class OrderException extends Exception {
    public OrderException(String message) { super(message); }
    public OrderException(String message, Throwable cause) {
        super(message, cause);
    }
}

public final class InvalidQuantityException extends OrderException {
    private final int quantity;

    public InvalidQuantityException(int quantity) {
        super("Quantity must be positive: " + quantity);
        this.quantity = quantity;
    }

    public int quantity() { return quantity; }
}

Python equivalent:

class OrderError(Exception):
    """Base class for order-domain failures."""

class InvalidQuantityError(OrderError):
    def __init__(self, quantity: int):
        super().__init__(f"Quantity must be positive: {quantity}")
        self.quantity = quantity

A custom exception should add semantic value. It may carry a field such as an order identifier, error code, invalid field name, or retryability classification. It should not merely rename a generic exception without improving communication.

Translating exceptions at architectural boundaries

A repository may raise SQLException or OSError, while the application service should not expose database implementation details to every caller. Translation is appropriate at a boundary:

try {
    return jdbc.loadOrder(id);
} catch (SQLException ex) {
    throw new PersistenceException("Could not load order " + id, ex);
}

Translation preserves the cause while presenting an abstraction meaningful to the next layer. It is not appropriate to translate every exception indiscriminately. A programming error such as a null dereference should not be disguised as a persistence failure.

4. Contracts: Preconditions, Postconditions, and Invariants

A contract describes correctness obligations between a method and its callers.

  • A precondition must be true before an operation begins.
  • A postcondition must be true after successful completion.
  • A class invariant must remain true for every observable valid state of an object.
  • A failure condition describes what the operation does when it cannot satisfy its postcondition.

Suppose a BankAccount has a balance that must never be negative. Its invariant is:

balance ≥ 0

A withdrawal method may require:

amount > 0 and amount ≤ balance

Its postcondition is that the new balance equals the old balance minus the amount. The method should validate the precondition before mutation and preserve the invariant afterward.

public void withdraw(BigDecimal amount) {
    if (amount == null || amount.signum() <= 0) {
        throw new IllegalArgumentException("Amount must be positive");
    }
    if (amount.compareTo(balance) > 0) {
        throw new InsufficientFundsException(amount, balance);
    }
    balance = balance.subtract(amount);
}

The example uses BigDecimal rather than binary floating-point arithmetic because monetary values require decimal-aware representation and explicit rounding policy. The contract does not by itself decide whether insufficient funds is a user-correctable validation problem or a domain exception; that decision belongs to the application design.

Python implementation:

from decimal import Decimal

class Account:
    def __init__(self, balance: Decimal):
        if balance < 0:
            raise ValueError("Initial balance cannot be negative")
        self._balance = balance

    def withdraw(self, amount: Decimal) -> None:
        if amount <= 0:
            raise ValueError("Amount must be positive")
        if amount > self._balance:
            raise InsufficientFundsError(amount, self._balance)
        self._balance -= amount

Assertions and validation are not interchangeable

An assertion expresses an assumption that indicates a programming defect if false. Input validation handles conditions that can legitimately arise in production. Assertions may be disabled in Java at runtime and should not be used for security checks or required input validation.

assert internalState != null;       // development invariant
if (request == null) {              // required runtime validation
    throw new IllegalArgumentException("request is required");
}

Python’s assert has the same general limitation: optimized execution can remove assertions. Use explicit exceptions for externally supplied data and security-relevant conditions.

Maintaining invariants during mutation

An object should not expose a partially updated state. Validate all relevant conditions before changing fields, or use a transaction-like sequence that can roll back on failure. A method that debits an account and then fails while recording an audit event raises a design question: must the debit be undone, or can the audit event be retried? The answer depends on the system’s consistency contract and often requires a transaction or durable messaging mechanism.

5. Handling, Propagating, and Recovering

Exception handling has four broad actions:

  1. Handle: take a meaningful corrective action.
  2. Propagate: pass the exception to a caller with better context.
  3. Translate: replace a low-level abstraction with a domain-level exception while preserving the cause.
  4. Recover or compensate: restore a valid state, retry, use a documented fallback, or reverse a completed partial action.

A handler is justified only when it can do something useful. This is poor practice:

try:
    process_payment(request)
except Exception:
    pass

It hides failures, may falsely signal success, and can leave state inconsistent. A better handler catches a narrow category and records appropriate context:

try:
    process_payment(request)
except PaymentServiceUnavailable as exc:
    logger.warning("Payment provider unavailable", exc_info=exc)
    raise RetryableCheckoutError("Please try again") from exc

Recoverable versus unrecoverable failures

“Recoverable” is contextual. A missing optional avatar is recoverable by using a placeholder. A missing cryptographic key is not safely recoverable by using a default key. A network timeout may be retryable, but repeated retries can amplify overload.

Retry policies should consider:

  • whether the operation is idempotent;
  • whether the failure is transient;
  • maximum attempts and elapsed time;
  • exponential backoff and jitter;
  • cancellation and deadlines;
  • whether duplicate effects are possible.

Never retry arbitrary exceptions automatically. Retrying a validation error wastes resources; retrying a non-idempotent payment request may charge a customer twice unless the operation has an idempotency key.

Catch narrow, log once, preserve context

Catch the narrowest exception type that supports a decision. Broad handlers at a process boundary may be necessary to prevent an entire worker from crashing, but they should log structured diagnostic information, mark the operation as failed, and avoid continuing as if it succeeded.

Logging should include a correlation identifier, operation, relevant resource identifier, exception type, and stack trace where appropriate. Do not log passwords, access tokens, payment-card data, or other sensitive personal information.

6. Resource Management

Resources are entities that must be acquired and released: files, sockets, database connections, locks, temporary files, and operating-system handles. Garbage collection does not provide a reliable general resource-management strategy. An object can become unreachable while its external resource remains allocated, and collection timing is nondeterministic.

The essential pattern is:

acquire → use → release

Release must occur on both successful and exceptional paths.

Java try-with-resources

A resource used in try-with-resources must implement AutoCloseable:

try (Connection connection = dataSource.getConnection();
     PreparedStatement statement = connection.prepareStatement(sql)) {
    statement.setLong(1, orderId);
    try (ResultSet result = statement.executeQuery()) {
        return map(result);
    }
} catch (SQLException ex) {
    throw new PersistenceException("Order lookup failed", ex);
}

Resources are closed in reverse order of acquisition. This reduces nesting and handles exceptions during closing. A manually written finally block is still useful when a resource does not implement AutoCloseable, but it is easier to get wrong.

Python context managers

Python’s context-manager pattern is concise:

with open(path, "r", encoding="utf-8") as file:
    data = file.read()

For multiple resources:

with open(source, encoding="utf-8") as input_file, \
     open(destination, "w", encoding="utf-8") as output_file:
    output_file.write(input_file.read())

A custom context manager can be created with a class or contextlib.contextmanager:

from contextlib import contextmanager

@contextmanager
def managed_lock(lock):
    lock.acquire()
    try:
        yield
    finally:
        lock.release()

The finally block is essential. If yield resumes with an exception, cleanup still occurs.

Atomicity and temporary files

Writing directly to a critical configuration file can leave it truncated if the process fails midway. A safer pattern is to write and flush a temporary file, then replace the target using an operating-system operation designed for replacement. The exact durability guarantee depends on filesystem and platform behavior; flushing a language buffer is not always equivalent to forcing data to stable storage.

Resource management therefore includes more than closing handles. It includes ownership, lifetime, cleanup ordering, transaction boundaries, and failure semantics.

7. Defensive Programming Without Hiding Failures

Defensive programming anticipates invalid inputs, environmental failures, and misuse. It should make incorrect states difficult to create and failures easy to diagnose. It should not convert every failure into a default value.

Useful practices include:

  • validate at trust boundaries such as HTTP requests, file imports, and message queues;
  • use immutable values where mutation is unnecessary;
  • make illegal states unrepresentable through types and constructors;
  • apply least privilege to files, databases, and services;
  • bound input sizes, recursion, time, and resource consumption;
  • use explicit timeouts rather than indefinite blocking;
  • preserve exception causes and contextual identifiers;
  • test both success and failure paths;
  • distinguish expected user errors from programmer defects;
  • fail closed for security-sensitive decisions.

Defensive code must also avoid over-validation. A lower-level utility should not impose business rules that belong to a domain service. For example, a generic string parser may validate syntax, while the application service decides whether a parsed date is acceptable for a particular workflow.

Anti-patterns

Swallowing exceptions hides defects and produces misleading success.

Catching Throwable in Java or BaseException in Python can intercept system-level termination and interruption signals.

Using exceptions for ordinary branching can make normal logic difficult to read. Exceptions are appropriate for exceptional or failure conditions, not as a substitute for a predictable boolean or optional result.

Throwing overly generic exceptions such as Exception loses semantic information.

Logging and rethrowing at every layer duplicates alerts and produces noisy logs. Add context at a meaningful boundary and preserve the cause.

Returning null or sentinel values after failure forces callers to guess whether the result is valid. Use a documented result type or propagate a clear exception.

8. Applied Case: A Reliable Document Import Service

The following is an illustrative case, not a description of a particular commercial system.

Background

A university administration system imports student records from uploaded CSV files. The service must validate records, avoid partial database updates, release file resources, and provide useful feedback to administrators.

Problem

An initial implementation catches every exception, logs only “Import failed,” and continues processing. It also opens files without deterministic cleanup. When one record contains an invalid student identifier, earlier records have already been stored, and administrators cannot determine which rows failed.

Analysis

Several contracts are needed:

  • the upload must be within the configured size limit;
  • each row must contain required fields;
  • identifiers must satisfy the institution’s format;
  • a batch must either commit according to its defined policy or report exactly which records were rejected;
  • the file must be closed regardless of parsing outcome.

The team distinguishes errors as follows:

ConditionCategoryResponse
File does not existoperational/inputreport upload failure
Malformed CSV rowvalidationreject row with line number
Invalid student identifierdomain validationreject row with field message
Database temporarily unavailabletransient infrastructureretry transaction under policy
Programming defectdefectfail operation, alert developers

Approach and solution

A domain hierarchy is introduced:

ImportError
├── ImportFormatError
├── RowValidationError
└── ImportPersistenceError

Python-like processing illustrates the boundary:

def import_students(path, repository):
    rejected = []
    accepted = []

    try:
        with open(path, newline="", encoding="utf-8") as file:
            for line_number, row in enumerate(read_rows(file), start=1):
                try:
                    student = Student.from_row(row)
                    accepted.append(student)
                except RowValidationError as exc:
                    rejected.append({"line": line_number, "error": str(exc)})
    except OSError as exc:
        raise ImportFormatError(f"Cannot read upload {path}") from exc

    try:
        repository.save_batch(accepted)
    except DatabaseUnavailable as exc:
        raise ImportPersistenceError("Student batch was not saved") from exc

    return ImportReport(saved=len(accepted), rejected=rejected)

The parser catches only row-validation failures because those are expected and reportable. An unexpected exception is not mistaken for bad user input. The file is managed by with. Persistence occurs after parsing, so the repository can apply a transaction to the accepted batch. An alternative design could save valid rows incrementally, but then its contract must explicitly document partial success.

Outcome

Administrators receive line-specific feedback. Database failures are visible to monitoring and do not appear as successful imports. File handles are released reliably. Developers retain the original causes of infrastructure failures. The service is also easier to test because parsing, validation, persistence, and reporting have clearer responsibilities.

Learning points

  1. Exception categories should correspond to operational decisions.
  2. Resource lifetime should be represented structurally by with or try-with-resources.
  3. Partial success is a business decision, not an accidental side effect.
  4. A handler should recover only when it can establish a valid outcome.
  5. Cause chaining preserves the relationship between technical failure and domain meaning.

9. Testing and Observability

Exception behavior is part of a public contract and deserves tests. Tests should verify:

  • the exception type for invalid arguments;
  • meaningful fields and messages, without depending unnecessarily on exact wording;
  • preservation of object invariants after failure;
  • resource closure when the body succeeds and when it fails;
  • transaction rollback or documented partial-commit behavior;
  • retry limits and non-retryable classifications;
  • exception causes at architectural boundaries.

In Java, testing frameworks commonly provide assertions such as assertThrows. In Python, pytest.raises verifies expected exceptions. Tests should also check that a broad handler does not intercept unrelated defects.

Production observability complements tests. Structured logs, metrics for exception categories, traces across service boundaries, and correlation IDs help distinguish user mistakes from system failures. Error messages shown to users should be safe and actionable; internal logs may contain technical context but must respect privacy and security requirements.

10. Contemporary and Professional Considerations

Modern applications often cross process and language boundaries. Exceptions cannot simply travel across an HTTP or message boundary as language-specific objects. They must be translated into stable error representations containing a status or category, safe message, correlation identifier, and optionally structured field errors. Internal stack traces should not be exposed to untrusted clients.

In distributed systems, a timeout does not prove that an operation was not performed. A client may time out after a server committed the request. Idempotency keys, durable status queries, and carefully designed retry policies are therefore more important than merely catching timeout exceptions.

AI-assisted programming tools can generate exception handlers rapidly, but generated code frequently uses broad catches, ignores causes, or returns unsafe defaults. Developers remain responsible for checking contracts, resource ownership, security implications, and failure semantics. Static analysis, code review, mutation testing, and fault-injection tests can reveal hidden failure paths.

From a governance perspective, software should preserve auditability. Security events, authorization failures, and data-integrity violations may require distinct logging and retention policies. Error handling must comply with privacy obligations by minimizing sensitive data in messages and logs.

Chapter Summary

Exceptions provide structured communication for abnormal conditions and transfer control to code capable of making a meaningful decision. Java enforces checked-exception declarations for selected exception classes, whereas Python relies primarily on runtime conventions and disciplined design. In both languages, custom hierarchies should reflect distinctions that matter to callers, and exception translation should preserve the original cause.

Contracts express preconditions, postconditions, and invariants. Runtime validation is necessary for external input and security-sensitive conditions; assertions are better suited to internal assumptions and must not be relied upon for required validation. A method should validate before mutation and preserve object invariants even when it fails.

Good handlers catch narrow categories, recover only when a valid outcome can be established, and otherwise propagate or translate the failure. Broad swallowing of exceptions hides defects and creates false success. Files, database connections, locks, sockets, and similar resources require deterministic cleanup through Java try-with-resources or Python context managers. Finally, reliable systems combine exception design with testing, observability, safe logging, transaction boundaries, and explicit retry and idempotency policies.

Key Terms

Exception: An object representing an abnormal condition and transferring control to a handler.

Checked exception: In Java, an exception that must be caught or declared by a method.

Unchecked exception: An exception, typically a RuntimeException in Java, not subject to compile-time catch-or-declare rules.

Exception hierarchy: A structured set of exception classes expressing meaningful categories and relationships.

Cause chaining: Recording the lower-level exception that explains why a higher-level exception occurred.

Precondition: A condition required before a method begins successfully.

Postcondition: A condition guaranteed after successful method completion.

Invariant: A condition that remains true for every valid observable state of an object.

Assertion: A statement expressing an internal assumption, often intended to detect programming defects.

Propagation: Passing an exception to a caller rather than handling it locally.

Translation: Replacing a lower-level exception with an abstraction appropriate to a higher layer while preserving its cause.

Recoverable error: A failure for which the current system or caller has a defined safe response.

Resource: An external or limited entity, such as a file, lock, socket, or database connection, requiring release.

Context manager: A Python protocol that defines acquisition and cleanup around a with block.

Try-with-resources: Java syntax that closes AutoCloseable resources automatically.

Suppressed exception: An exception recorded when cleanup fails while another exception is already being propagated.

Defensive programming: Designing software to anticipate invalid inputs, environmental failures, and misuse.

Idempotency: A property whereby repeating an operation has the same intended effect as performing it once.

Fail closed: Choosing a safe denial or shutdown rather than granting access or continuing under uncertain conditions.

Review Questions

5 Short Answer Questions

  1. What information is commonly carried by an exception besides its type?
  2. Why should assertions not replace runtime input validation?
  3. What is the difference between a precondition and an invariant?
  4. Why is cause chaining important when translating exceptions?
  5. How does a Python context manager support resource cleanup?

5 Long Answer Questions

  1. Compare checked and unchecked exceptions in Java with Python’s exception model. Discuss benefits and risks of each approach.
  2. Explain how to design an exception hierarchy for a multi-layer application. Include criteria for translating low-level exceptions.
  3. Discuss the relationship between contracts and object invariants, using a mutable domain object as an example.
  4. Explain why broad exception handlers and swallowed exceptions are dangerous. Identify situations in which a broad boundary handler may nevertheless be justified.
  5. Compare Java try-with-resources and Python with statements, including cleanup ordering, failure during cleanup, and ownership responsibilities.

3 Analytical / Application Questions

  1. A payment request times out after the server may have processed it. Design an error-handling and retry strategy that prevents duplicate charges.
  2. An import service can either reject an entire file when one row is invalid or save valid rows and report rejected rows. Analyze the consistency, usability, and operational trade-offs of both contracts.
  3. Review a function that catches Exception, logs “failed,” returns an empty list, and leaves a database connection open on some paths. Identify the defects and propose a corrected design in either Java or Python.

Multiple Choice Questions with Answers

Question 1

Which statement best describes a postcondition?

A. A condition that must hold before a method begins

B. A condition guaranteed after successful method completion

C. A superclass shared by all exception types

D. A resource that must be closed after use

Correct Answer: B. A condition guaranteed after successful method completion

Question 2

What is the primary purpose of exception cause chaining?

A. To suppress all diagnostic information

B. To convert every exception into a checked exception

C. To preserve the lower-level reason for a higher-level failure

D. To make an exception impossible to catch

Correct Answer: C. To preserve the lower-level reason for a higher-level failure

Question 3

Which Java construct is designed to close AutoCloseable resources automatically?

A. An if statement

B. A switch expression

C. A try-with-resources statement

D. An assertion

Correct Answer: C. A try-with-resources statement

Question 4

In Python, what is the principal purpose of a with statement when used with a file?

A. It disables file errors

B. It provides deterministic acquisition and cleanup

C. It converts all errors into warnings

D. It makes the file globally accessible

Correct Answer: B. It provides deterministic acquisition and cleanup

Question 5

Which condition is most appropriate for a retry, assuming the operation is idempotent?

A. A permanently invalid email address

B. A syntax error in source code

C. A transient service timeout

D. A violated object invariant caused by a bug

Correct Answer: C. A transient service timeout

Question 6

Why should assertions not be used for required input validation?

A. Assertions cannot contain Boolean expressions

B. Assertions may be disabled at runtime

C. Assertions always create checked exceptions

D. Assertions automatically close resources

Correct Answer: B. Assertions may be disabled at runtime

Question 7

A repository catches SQLException and raises PersistenceException with the original exception as its cause. What design principle does this demonstrate?

A. Exception translation at an architectural boundary

B. Silent failure recovery

C. Use of exceptions for ordinary iteration

D. Removal of all abstraction layers

Correct Answer: A. Exception translation at an architectural boundary

Question 8

Which practice most directly avoids hiding an unexpected programming defect?

A. Catching every exception and returning an empty result

B. Catching only the narrow exception category that can be handled

C. Replacing every exception with a generic message and no cause

D. Ignoring exceptions after logging a warning

Correct Answer: B. Catching only the narrow exception category that can be handled

Question 9

What is an object invariant?

A. A condition that remains true for every valid observable object state

B. A temporary value used only during exception construction

C. A Java-only requirement for checked exceptions

D. A rule that resources must be acquired twice

Correct Answer: A. A condition that remains true for every valid observable object state

Question 10

Why can automatically retrying a non-idempotent payment request be dangerous?

A. It guarantees that the payment will be rejected

B. It may produce duplicate side effects

C. It prevents all network timeouts

D. It changes a checked exception into an assertion

Correct Answer: B. It may produce duplicate side effects

Object-Oriented Programming (Java/Python)
Chapter 8 of 15