Java

Final vs Finally vs Finalize in Java

Final vs Finally vs Finalize in Java: The Ultimate Guide to Avoid Confusion

Java developers often get confused between final, finally, and finalize. These three terms might sound similar, but they serve completely different purposes. If you’ve ever struggled to understand their differences, this guide is for you!

By the end of this post, you’ll have a clear understanding of final vs finally vs finalize in Java and how to use each one correctly. Let’s dive in!

1. What is final in Java?

The final keyword in Java is used for constants, method restrictions, and inheritance control. It can be applied to variables, methods, and classes.

a) final with Variables (Constant Values)

When a variable is declared final, its value cannot be changed once assigned.

Java
public class FinalVariableExample {
    final int MAX_VALUE = 100; // Constant value
    
    void display() {
        // MAX_VALUE = 200; // This will cause a compilation error
        System.out.println("Max Value: " + MAX_VALUE);
    }
}

The MAX_VALUE variable is declared as final, so its value cannot be modified.

b) final with Methods (Prevent Overriding)

A final method cannot be overridden by subclasses.

Java
class Parent {
    final void show() {
        System.out.println("This is a final method.");
    }
}

class Child extends Parent {
    // void show() { // This will cause a compilation error
    //     System.out.println("Cannot override a final method");
    // }
}

The show() method in the Parent class is marked final, preventing the Child class from overriding it.

c) final with Classes (Prevent Inheritance)

A class declared as final cannot be extended.

Java
final class FinalClass {
    void display() {
        System.out.println("This is a final class.");
    }
}

// class SubClass extends FinalClass { // This will cause a compilation error
// }

The FinalClass cannot be extended by any subclass.

2. What is finally in Java?

The finally block in Java is used to ensure that important code executes, regardless of exceptions. It is primarily used with try-catch blocks to handle exceptions.

Java
public class FinallyExample {
    public static void main(String[] args) {
        try {
            int result = 10 / 0; // This will cause an exception
        } catch (ArithmeticException e) {
            System.out.println("Exception caught: " + e.getMessage());
        } finally {
            System.out.println("Finally block executed!");
        }
    }
}

Output:

Java
Exception caught: / by zero
Finally block executed!

The finally block runs no matter what happens in the try-catch block. This is useful for closing resources like database connections or file streams.

3. What is finalize() in Java?

The finalize() method is used for garbage collection. It is called by the Garbage Collector before an object is destroyed to perform cleanup operations.

Java
class FinalizeExample {
    protected void finalize() {
        System.out.println("Finalize method called before garbage collection.");
    }

    public static void main(String[] args) {
        FinalizeExample obj = new FinalizeExample();
        obj = null; // Making object eligible for garbage collection
        System.gc(); // Requesting garbage collection
        System.out.println("End of main method.");
    }
}

Output (may vary depending on JVM execution):

Java
End of main method.
Finalize method called before garbage collection.
  • The finalize() method is called before an object is garbage collected but not guaranteed to execute immediately or at all.
  • Calling System.gc() only suggests garbage collection to the JVM, but it does not force it.
  • Due to unpredictability and performance issues, finalize() has been deprecated in Java 9 and removed (marked as remove) in Java 18.
Alternatives to finalize():
  • Try-with-resources (AutoCloseable) – For handling resources like files, sockets, and streams.
  • java.lang.ref.Cleaner (Java 9+) – A more reliable way to register cleanup actions when objects become unreachable.
Important Note:

The use of finalize() is strongly discouraged in modern Java programming. Developers should use explicit resource management instead of relying on garbage collection for cleanup.

Final vs Finally vs Finalize in Java: Key Differences

Featurefinalfinallyfinalize()
UsageVariable, method, or class modifierBlock in exception handlingMethod in garbage collection
EffectRestricts variable reassignment, method overriding, and class inheritanceEnsures execution of critical codeAllows cleanup before object removal
ExecutionCompile-timeAlways runs after try-catchCalled by garbage collector
PurposeRestrictionCode execution assuranceCleanup

When to Use Final, Finally, and Finalize?

  • Use final when you want to create constants, prevent method overriding, or restrict class inheritance.
  • Use finally when you need to execute important code regardless of exceptions, like closing resources.
  • Use finalize() only if you need to clean up resources before garbage collection, though it is now discouraged.

Conclusion

Understanding final vs finally vs finalize in Java is crucial for writing efficient and error-free Java programs. While final is used for constants, method restrictions, and inheritance prevention, finally ensures essential code execution, and finalize() helps with garbage collection (though deprecated in Java 9+).

custom exceptions

Custom Exceptions vs. Standard Exceptions in Java: When to Extend and When Not To

Java provides a robust exception handling mechanism that helps developers write reliable and maintainable code. While Java’s standard exceptions cover many common error scenarios, sometimes you need something more specific to your application’s needs. This is where custom exceptions in Java come into play. But when should you create a custom exception, and when is it unnecessary? Let’s explore this in depth.

What Are Standard Exceptions in Java?

Java has a rich hierarchy of built-in exceptions that developers can use to handle different errors. These standard exceptions fall into two main categories:

1. Checked Exceptions — Must be handled using try-catch or declared using throws.

  • Example: IOException, SQLException

2. Unchecked Exceptions (Runtime Exceptions) — Do not require explicit handling.

  • Example: NullPointerException, IndexOutOfBoundsException

Using standard exceptions is often the best choice because they are well-documented and understood by developers. However, they might not always convey specific application-related issues effectively.

When to Use Custom Exceptions in Java

Custom exceptions are useful when you need to represent domain-specific errors that are not covered by standard exceptions. Here are some scenarios where custom exceptions make sense:

1. When Standard Exceptions Are Too Generic

Standard exceptions may not always provide enough clarity. For instance, if your application processes payments, throwing a generic Exception or IllegalArgumentException isn’t informative. A PaymentProcessingException makes the error clearer.

2. When You Need to Add Extra Information

A custom exception allows you to include additional details about an error, such as error codes, messages, or even metadata.

3. When You Want to Enforce Business Rules

Custom exceptions help enforce specific business logic. For example, if a user tries to withdraw more money than available, you might throw an InsufficientFundsException instead of a generic RuntimeException.

4. When You Need to Handle Exceptions Differently

If your application has a centralized error-handling mechanism, custom exceptions can be helpful in distinguishing different types of errors.

How to Create a Custom Exception in Java

Creating a custom exception in Java is simple. You can extend either Exception (for checked exceptions) or RuntimeException (for unchecked exceptions).

Creating a Checked Custom Exception

Java
class InvalidAgeException extends Exception {
    public InvalidAgeException(String message) {
        super(message);
    }
}

public class CustomExceptionExample {
    public static void validateAge(int age) throws InvalidAgeException {
        if (age < 18) {
            throw new InvalidAgeException("Age must be 18 or above.");
        }
    }
    public static void main(String[] args) {
        try {
            validateAge(16);
        } catch (InvalidAgeException e) {
            System.out.println("Caught exception: " + e.getMessage());
        }
    }
}

Here,

  • InvalidAgeException extends Exception, making it a checked exception.
  • The constructor passes a custom message to the superclass (Exception).
  • The validateAge method throws InvalidAgeException if age is below 18.
  • The exception is caught in main and handled gracefully.

Creating an Unchecked Custom Exception

Java
class DatabaseConnectionException extends RuntimeException {
    public DatabaseConnectionException(String message) {
        super(message);
    }
}

public class UncheckedCustomExceptionExample {
    public static void connectToDatabase(boolean connectionStatus) {
        if (!connectionStatus) {
            throw new DatabaseConnectionException("Failed to connect to the database.");
        }
    }
    public static void main(String[] args) {
        connectToDatabase(false);
    }
}

Here,

  • DatabaseConnectionException extends RuntimeException, making it unchecked.
  • No need to declare it using throws since unchecked exceptions don’t require explicit handling.
  • If connectToDatabase(false) is called, an exception is thrown.

When NOT to Use Custom Exceptions

While custom exceptions in Java are useful, overusing them can lead to unnecessary complexity. Here are cases where they may not be needed:

1. When a Standard Exception Suffices

If a standard exception like IllegalArgumentException or NullPointerException properly conveys the issue, using a custom exception is redundant.

Java
public void setAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("Age cannot be negative.");
    }
}

There’s no need for a NegativeAgeException when IllegalArgumentException works perfectly.

2. When They Add Unnecessary Complexity

If an exception doesn’t add meaningful information or handling logic, it might not be worth creating.

3. When Logging and Debugging Are Not Improved

If a custom exception doesn’t make debugging easier or doesn’t offer additional insights, it may not be necessary.

Best Practices for Custom Exceptions

  1. Keep Custom Exceptions Specific — Avoid generic names like MyAppException; use names that reflect the issue, such as UserNotFoundException.
  2. Extend the Right Class — Use Exception for checked exceptions and RuntimeException for unchecked exceptions.
  3. Include Helpful Messages — Provide meaningful messages to help with debugging.
  4. Document Your Exceptions — Ensure other developers understand when and why to use them.
  5. Avoid Creating Too Many Exceptions — Use them only when they add real value.

Conclusion

Custom exceptions in Java are powerful when used appropriately. They provide clarity, enforce business logic, and enhance maintainability. However, standard exceptions should be preferred when they adequately describe an error. The key is to strike the right balance — use custom exceptions only when they genuinely improve code readability, debugging, and error handling.

Checked Exceptions

Checked Exceptions in Java: What They Are and How They Work

When writing Java programs, handling errors is an essential part of creating robust and reliable applications. One important concept in Java’s error-handling mechanism is checked exceptions. If you’re new to Java or need a refresher, this guide will walk you through what checked exceptions are, how they work, and how to handle them effectively. What...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Unchecked Exceptions in Java

Unchecked Exceptions in Java: What They Are

Java is a powerful programming language that provides robust error handling mechanisms through exceptions. Exceptions in Java are classified into checked exceptions and unchecked exceptions. In this blog post, we’ll dive deep into unchecked exceptions in java, focusing on RuntimeException, and explore how they work, when to use them, and best practices.

What Are Unchecked Exceptions in Java?

Unchecked exceptions in Java are exceptions that occur during the execution of a program and do not need to be explicitly declared or handled. They are subclasses of RuntimeException, which itself extends Exception. Unlike checked exceptions, the compiler does not force you to handle unchecked exceptions, giving developers more flexibility.

Imagine you are driving a car:

  • If you run out of fuel before starting, you already know you’ll need to refill (like a checked exception, where Java warns you in advance).
  • If you suddenly get a flat tire while driving, it’s unexpected (like an unchecked exception, because Java doesn’t force you to check for it).

Unchecked exceptions usually happen due to coding mistakes like dividing by zero, accessing an invalid index, or dereferencing null.

Key Characteristics of Unchecked Exceptions:

  • They occur at runtime.
  • They are not required to be handled using try-catch or declared with throws
  • They indicate programming errors, such as logical flaws or improper API usage.
  • Examples include NullPointerException, ArrayIndexOutOfBoundsException, and IllegalArgumentException.

Common Causes of Unchecked Exceptions

Unchecked exceptions often arise from:

  1. Null references — Trying to access methods or fields of a null object leads to a NullPointerException.
  2. Invalid array access — Accessing an index beyond the array’s length results in ArrayIndexOutOfBoundsException.
  3. Illegal operations — Dividing by zero throws an ArithmeticException.
  4. Invalid casting — Trying to cast an object to an incompatible type leads to ClassCastException.
  5. Improper argument usage — Passing an invalid argument to a method can trigger IllegalArgumentException.

How to Handle Unchecked Exceptions in Java?

Although unchecked exceptions don’t require explicit handling, it is good practice to write defensive code to avoid them. Here are some best practices:

1. Use Null Checks

Before using an object, always ensure it is not null to avoid NullPointerException.

2. Validate Input Arguments

Check method parameters before processing them.

3. Use Try-Catch Blocks Sparingly

Try-catch blocks should not be overused for unchecked exceptions but can be useful in specific cases.

Difference Between Checked and Unchecked Exceptions

Understanding the distinction between checked and unchecked exceptions is crucial for writing efficient Java code.

FeatureChecked ExceptionsUnchecked Exceptions
InheritanceExtends Exception (except RuntimeException)Extends RuntimeException
Compile-time CheckingChecked by the compilerNot checked by the compiler
Handling RequirementMust be handled or declaredNo mandatory handling
Use CaseRepresent recoverable conditions (e.g., IOException)Indicate programming errors (e.g., NullPointerException)

Should You Catch Unchecked Exceptions in Java?

Generally, it’s best to avoid catching unchecked exceptions unless you’re implementing a global exception handler. Instead, focus on writing clean, error-free code by using input validation and proper null checks. However, in web applications or frameworks, handling unchecked exceptions globally can enhance user experience by providing clear error messages rather than allowing the application to crash.

Conclusion

Unchecked exceptions in Java, particularly those derived from RuntimeException, provide flexibility but also require responsible usage. They indicate programming mistakes that should be fixed rather than caught. By following best practices like validating inputs, using meaningful messages, and logging exceptions properly, developers can write robust and maintainable Java applications.

How to create threads in java

How to Create Threads in Java (The Easy Way): From Thread Class to Runnable Explained

If you’re diving into Java programming, one of the most empowering features to understand is multithreading. It allows you to run multiple tasks concurrently, making your application more efficient and responsive. In this post, we’ll walk you through how to create threads in Java, using both the Thread class and the Runnable interface.

Why Multithreading Matters in Java

Multithreading helps your program perform multiple operations simultaneously. Think of downloading files, processing data, and updating the UI all at once. Without multithreading, these tasks would run one after the other, slowing everything down.

Java makes multithreading easy thanks to built-in support via the Thread class and the Runnable interface.

Method 1: Using the Thread Class

This is the most direct way to create a thread in Java.

Step-by-step:

  1. Extend the Thread class.
  2. Override the run() method.
  3. Create an object of your class.
  4. Call the start() method.
Java
class MyThread extends Thread {
    public void run() {
        System.out.println("Thread is running...");
    }

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();
    }
}

What Happens Here?

  • run() defines the code that runs in the new thread.
  • start() creates a new thread and invokes run().
  • If you call run() directly, it runs in the main thread, not a new one.

This method is simple but has a limitation: your class can’t extend any other class since Java doesn’t support multiple inheritance.

Method 2: Implementing the Runnable Interface

A more flexible way to create threads in Java is by implementing the Runnable interface.

Step-by-step:

  1. Implement Runnable.
  2. Override the run() method.
  3. Pass the object to a Thread constructor.
  4. Start the thread.
Java
class MyRunnable implements Runnable {
    public void run() {
        System.out.println("Runnable thread is running...");
    }

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable);
        t1.start();
    }
}

Why Use Runnable?

  • It allows your class to extend another class while still supporting threads.
  • It separates the task (logic in run()) from the thread execution (managed by Thread).

Thread Class vs Runnable Interface: Quick Comparison

FeatureThread ClassRunnable Interface
Inheritance LimitationYes (extends Thread)No (implements Runnable)
Separation of ConcernsNoYes
Recommended ForSimple one-off threadsBetter architecture & reuse

Pro Tip: Use Anonymous Classes or Lambdas (Java 8+)

If you’re working with short-lived tasks, you don’t need to write a separate class.

Anonymous Runnable:

Java
Thread t = new Thread(new Runnable() {
    public void run() {
        System.out.println("Anonymous thread running");
    }
});
t.start();

Lambda Runnable (Java 8+):

Java
Thread t = new Thread(() -> {
    System.out.println("Lambda thread running");
});
t.start();

Best Practices for Creating Threads in Java

  • Avoid calling run() directly. Use start() to ensure a new thread is created.
  • Use Runnable when possible. It offers better design flexibility.
  • Name your threads. This makes debugging easier: Thread t = new Thread(runnable, "WorkerThread");
  • Use thread pools for many threads. For heavy-duty multithreading, look into ExecutorService.

Want more details? Check out the full guide: [Main Article URL]

Conclusion

Learning how to create threads in Java isn’t just about writing concurrent code. It’s about writing efficient, clean, and scalable applications.

Start with Thread if you’re just experimenting. Move to Runnable for better design. Embrace lambdas and anonymous classes for quick jobs.

Multithreading is a key skill in Java. Master it, and you unlock a whole new level of performance for your applications.

FAQ: How to Create Threads in Java

Q: Can I start a thread without creating a class?
A: Yes, use anonymous classes or lambdas.

Q: What happens if I call run() instead of start()?
A: The code runs in the main thread, not in a new thread.

Q: Is Runnable better than Thread?
A: Usually yes. It gives more design flexibility and aligns with best practices.

Multithreading in Java

What Is Multithreading in Java? A Beginner-Friendly Guide with Real Examples

If you’re learning Java, you’ve probably come across the term multithreading. It may sound complicated at first, but it’s one of the most powerful features Java offers for building fast and responsive applications.

This guide will break down multithreading in Java. You’ll learn what it is, why it matters, how it works, and how to use it with real examples.

What Is Multithreading?

Multithreading is a way to run multiple tasks (called threads) at the same time within a single Java program.

Think of a thread as a lightweight process. Instead of running your code line-by-line, you can split it into independent tasks that run concurrently. This can lead to better performance, especially on multi-core processors.

Use Cases:

  • Loading data in the background while the UI stays responsive
  • Performing calculations without freezing the main program
  • Downloading files or accessing a database while doing other tasks

Why Use Multithreading in Java?

Java was designed with multithreading in mind. The java.lang.Thread class and java.util.concurrent package give you robust tools to build concurrent applications.

Benefits of using multithreading in Java:

  • Improved performance on multi-core systems
  • Better user experience in desktop and mobile apps
  • Efficient resource utilization

Understanding the Basics: Threads and the JVM

In Java, every application starts with a main thread — the one that runs your main() method. When you create additional threads, you’re allowing your program to do more than one thing at once.

You can create threads in two common ways:

  1. Extending the Thread class
  2. Implementing the Runnable interface

Let’s look at both approaches.

Method 1: Extending the Thread Class

Java
class MyThread extends Thread {
    public void run() {
        System.out.println("Thread is running");
    }

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start(); // starts a new thread
    }
}
  • MyThread is a class that extends Thread.
  • The run() method holds the code that the thread will execute.
  • Calling start() creates a new thread and executes the run() method.

Note: Calling run() directly won’t create a new thread. Always use start().

Method 2: Implementing Runnable Interface

Java
class MyRunnable implements Runnable {
    public void run() {
        System.out.println("Runnable thread is running");
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunnable());
        t1.start();
    }
}
  • MyRunnable implements Runnable, which has a single method run().
  • We pass an instance of MyRunnable to the Thread constructor.
  • Then, we call start() to launch the new thread.

This method is preferred if your class needs to inherit from another class, since Java doesn’t support multiple inheritance.

Multithreading with Sleep and Multiple Threads

Let’s run two threads to see how they operate concurrently:

Java
class MultiThreadDemo extends Thread {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println(Thread.currentThread().getName() + " - " + i);
            try {
                Thread.sleep(500); // pauses for 500 milliseconds
            } catch (InterruptedException e) {
                System.out.println(e);
            }
        }
    }
    
    public static void main(String[] args) {
        MultiThreadDemo t1 = new MultiThreadDemo();
        MultiThreadDemo t2 = new MultiThreadDemo();
        t1.start();
        t2.start();
    }
}

Here,

  • Two threads (t1 and t2) run the same code.
  • They execute concurrently, each printing numbers 1 to 5.
  • Thread.sleep() simulates a delay to better observe the switching.

You might see interleaved output like:

Java
Thread-1 - 1
Thread-0 - 1
Thread-1 - 2
Thread-0 - 2
Thread-1 - 3
Thread-0 - 3
Thread-1 - 4
Thread-0 - 4
Thread-1 - 5
Thread-0 - 5

Thread Lifecycle

A thread in Java has several states:

  1. New — created but not started
  2. Runnable — ready to run
  3. Running — currently executing
  4. Blocked/Waiting — paused due to IO or sleep
  5. Terminated — finished execution

Understanding the lifecycle helps in debugging and optimizing performance.

Best Practices for Multithreading in Java

  1. Avoid shared data conflicts using synchronization
  2. Use higher-level concurrency APIs like ExecutorService for managing multiple threads
  3. Keep threads short-lived when possible
  4. Handle exceptions inside threads to prevent silent failures
  5. Use meaningful thread names to simplify debugging

Conclusion

Multithreading in Java is a core skill for building efficient, high-performance applications. Once you grasp the basics of threads, Runnable, and Thread.sleep(), you can explore more advanced tools like Callable, Future, and thread pools.

Start small, experiment, and practice writing thread-safe code. Java makes it easier than you might think — and the benefits are well worth it.

finalize() Method

Java finalize() Method Explained: Is It Still Relevant in 2025?

Java developers have used the finalize() method for years to handle cleanup operations before an object is garbage collected. But in 2025, does the finalize() method in Java still hold any relevance? With advancements in garbage collection and alternative resource management techniques, many developers question its necessity. In this blog post, we’ll explore the finalize()...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
throw and throws

How to Use throw and throws Effectively in Java

When working with Java, handling exceptions properly is crucial to writing robust and maintainable applications. Two essential keywords in Java’s exception-handling mechanism are throw and throws. While they may look similar, they serve different purposes. In this guide, we will explore how to use throw and throws effectively in Java, ensuring clarity and proper exception...

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Try-Catch-Finally in Java

Try-Catch-Finally in Java: How Multiple Catch Blocks Fit into Java’s Exception Handling Flow

Try-Catch-Finally in Java is a crucial part of Java programming. It helps prevent programs from crashing due to unexpected errors. Java provides a structured way to handle exceptions using try, catch, and finally blocks. In this post, we’ll explore how multiple catch blocks work in Java’s exception handling flow and how they improve code reliability....

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here
Java Exception Hierarchy

Java Exception Hierarchy Explained: A Complete Guide

Java is a powerful, object-oriented programming language that provides a structured way to handle errors using exceptions. Understanding the Java Exception Hierarchy is crucial for writing robust, error-free code. In this guide, we’ll break down Java’s exception system, explore its hierarchy, and show you how to use it effectively.

Java Exception Hierarchy

In Java’s Exception Hierarchy, the Throwable class serves as the root. This class defines two main child classes:

Exception: Exceptions primarily arise from issues within our program and are typically recoverable.

Example:

Java
try {
    // Read data from the remote file located in London
} catch (FileNotFoundException e) {
    // Use a local file and continue the rest of the program normally
}

Error: Errors, on the other hand, are non-recoverable. For instance, if an OutOfMemoryError occurs, programmers are generally powerless to address it, leading to the abnormal termination of the program. It becomes the responsibility of system administrators or server administrators to tackle issues like increasing heap memory.

Java
Throwable
├── Exception
│   ├── RuntimeException
│   │   ├── ArithmeticException
│   │   ├── NullPointerException
│   │   ├── ClassCastException
│   │   ├── IndexOutOfBoundsException
│   │   │   ├── ArrayIndexOutOfBoundsException
│   │   │   └── StringIndexOutOfBoundsException
│   │   └── IllegalArgumentException
│   │       └── NumberFormatException
│   ├── IOException
│   │   ├── EOFException
│   │   ├── FileNotFoundException
│   │   └── InterruptedIOException
│   └── ServletException
└── Error
    ├── VirtualMachineError
    │   ├── StackOverflowError
    │   └── OutOfMemoryError
    ├── AssertionError
    └── ExceptionInInitializerError

Let’s explore each of these in detail.

Exception: Recoverable Issues

Exceptions are events that disrupt the normal flow of a program but are recoverable. These are further categorized into checked and unchecked exceptions.

Checked Exceptions

Checked exceptions must be handled using a try-catch block or declared in the method signature using throws. The compiler ensures they are properly managed.

Common Checked Exceptions

IOException — Related to input/output operations.

  • EOFException: Thrown when an unexpected end of a file or stream is reached.
  • FileNotFoundException: Occurs when a specified file is missing.
  • InterruptedIOException: Thrown when an I/O operation is interrupted.

ServletException — Occurs when an error happens in a Java Servlet.

Unchecked Exceptions (Runtime Exceptions)

Unchecked exceptions are subclasses of RuntimeException and are not checked at compile time. They occur due to programming logic errors and can usually be avoided with proper coding practices.

Common Unchecked Exceptions

ArithmeticException — Thrown when illegal arithmetic operations occur (e.g., division by zero).

NullPointerException — Occurs when trying to access an object reference that is null.

ClassCastException — Happens when an object is cast to an incompatible type.

IndexOutOfBoundsException — Thrown when trying to access an index beyond valid bounds.

  • ArrayIndexOutOfBoundsException: Raised when an array index is out of range.
  • StringIndexOutOfBoundsException: Raised when a string index is invalid.

IllegalArgumentException — Thrown when an invalid argument is passed to a method.

  • NumberFormatException: A specific subclass that occurs when attempting to convert a non-numeric string into a number.

Error: Unrecoverable Issues

Errors represent serious problems that occur at the system level and are usually beyond the control of the application. They typically indicate problems related to the Java Virtual Machine (JVM) or the system itself.

Common Errors in Java

VirtualMachineError — Errors occurring due to resource exhaustion.

  • StackOverflowError: Happens when the call stack overflows due to deep or infinite recursion.
  • OutOfMemoryError: Raised when the JVM runs out of memory and cannot allocate more objects.

AssertionError — Thrown when an assertion fails in Java (assert statement used for debugging).

ExceptionInInitializerError — Occurs when an exception happens inside a static initializer block or a static variable initialization.

Unlike exceptions, errors are not meant to be caught or handled in most cases. Instead, they indicate fundamental issues that require fixing at a deeper level (e.g., optimizing memory usage).

Key Differences Between Exceptions and Errors

Exception Vs. Error

Conclusion

Understanding the Java Exception Hierarchy is key to writing reliable applications. Java categorizes exceptions into checked and unchecked types, each serving a distinct purpose. By handling exceptions effectively, you can prevent crashes, improve debugging, and ensure your application runs smoothly.

Happy Exception Handling..!

error: Content is protected !!