Programming Java

Tasks studies - laboratory


Project maintained by dawidolko Hosted on GitHub Pages — Theme by dawidolko

OBJECT-ORIENTED PROGRAMMING JAVA - LABORATORY

EXCEPTIONS

An exception is a special class in Java. It is specific because it inherits from the java.lang.Throwable class. Instances that have this class in their inheritance chain can be “thrown,” interrupting the standard program execution.

Exception Hierarchy in Java


lab6

The Throwable class is the base class in the hierarchy and is divided into Error and Exception.

Error - Represents irreversible conditions, e.g., out-of-memory errors, memory leaks, stack overflow errors, library mismatches, infinite recursion, etc.

Exception - These can be caught and handled by the program. When an exception occurs in a method, an object is created, which is referred to as an exception object. It contains information about the exception, such as its name, description, and the program state where the exception occurred.

An example might be validating method arguments. Suppose our method takes the number of hours as an argument and returns the number of seconds corresponding to the provided argument. We can assume that only positive or 0 arguments are acceptable. In other words, if the method is called with a negative argument, we can consider this an invalid invocation and signal this situation by throwing an exception.

package PO_UR.Lab08;

public class ExceptionExample01 {
    public int getNumberOfSeconds(int hour) {
        if (hour < 0) {
            throw new IllegalArgumentException("Hour must be >= 0: " + hour);
        }
        return hour * 60 * 60;
    }
}

In the above example, a standard Java library exception java.lang.IllegalArgumentException is used. To throw an exception, the throw keyword is used.

Throwing an Exception - What Happens Next?

Let’s analyze the following example:

package PO_UR.Lab08;

public class StackTraceExample {
    public static void main(String[] args) {
        StackTraceExample example = new StackTraceExample();
        example.method1();
    }

    public void method1() {
        method2();
    }

    public void method2() {
        method3();
    }

    public void method3() {
        throw new RuntimeException("BUM! BUM! BUM!");
    }
}

In the main method, we create an instance of the StackTraceExample class and call the method1 method on it. This method calls method2, which in turn calls method3. method3 throws a java.lang.RuntimeException (another standard library exception). This chain of method calls is referred to as the call stack. In our case, the stack looks like this:

  1. main
  2. method1
  3. method2
  4. method3

When this program is executed, the exception is thrown, and the programmer will see a stack trace of the methods, as shown below:

Exception in thread "main" java.lang.RuntimeException: BUM! BUM! BUM!
    at PO_UR.Lab08.StackTraceExample.method3(StackTraceExample.java:19)
    at PO_UR.Lab08.StackTraceExample.method2(StackTraceExample.java:15)
    at PO_UR.Lab08.StackTraceExample.method1(StackTraceExample.java:11)
    at PO_UR.Lab08.StackTraceExample.main(StackTraceExample.java:7)

In programming, the ability to read this type of message is very important. The stack trace is nothing more than a reversed call stack, from the program’s start to the point where the exception was thrown.

Handling Exceptions

An exception is handled when we respond to its occurrence and attempt to “fix” the program during its execution. Handling exceptions is done using the try/catch block. Refer to the example below:

try {
    // code
} catch (Exception e) {
    // code
}

Example 1

package PO_UR.Lab08;

public class ExceptionExample02 {
    public static void main(String[] args) {
        try {
            // code that generates exception
            int divideByZero = 5 / 0;
            System.out.println("Rest of code in try block");
        } catch (ArithmeticException e) {
            System.out.println("ArithmeticException => " + e.getMessage());
        }
    }
}

In the above example, we try to divide a number by 0. To handle the exception, the division code is placed inside a try block. If an exception occurs, the remaining part of the try block is skipped. The catch block catches the exception and executes the instructions within it. If no instructions in the try block generate an exception, the catch block is skipped.

The finally Block

In Java, the finally block is always executed, regardless of whether an exception occurs or not. It is optional, and for every try block, there can only be one finally block. Basic syntax:

try {
    // code
} catch (ExceptionType1 e1) {
    // catch block
} finally {
    // finally block always executes
}

Example 2

package PO_UR.Lab08;

public class ExceptionExample02 {
    public static void main(String[] args) {
        try {
            // code that generates exception
            int divideByZero = 5 / 0;
            System.out.println("Rest of code in try block");
        } catch (ArithmeticException e) {
            System.out.println("ArithmeticException => " + e.getMessage());
        } finally {
            System.out.println("This is the finally block");
        }
    }
}

In the above example, we divide a number by 0 inside the try block. This code generates an ArithmeticException, which is caught by the catch block. Then, the finally block is executed.


Tasks

Task 1

Write a program that takes a number from the user and displays its square root. Use the existing method java.lang.Math.sqrt() to calculate the square root. If the user enters a negative number, throw an IllegalArgumentException. Handle cases where the user inputs a string that is not a number.

Task 2

Write a method that returns the factorial of the number provided as an argument. The method should throw a Checked exception defined by you, InvalidFactorialValueException, if the argument is negative. Use this method in the main method, handling any potential exceptions.

Task 3

Write a program with a Address class that includes the following fields set in the constructor. The constructor should validate all provided values and throw a Checked exception InvalidAddressException if any values are invalid.

Note: The exception message should include information about all invalid values passed to the constructor. For example, if both the street and city are null, the exception message should be: “Street cannot be null. City cannot be null.”

Class fields: