Java Exception Handling, Custom Exceptions & File I/O

Java Exception Handling and File I/O

Multiple Catch Statements

In Java, multiple catch statements allow a program to handle different types of exceptions separately. When we write a try block, it may throw different exceptions at runtime. To handle each specific exception, Java provides multiple catch blocks arranged one after another. Multiple catch statements mean writing more than one catch block after a single try block so that each block handles a different type of exception. This helps in writing reliable and error-free programs.

Need for Multiple Catch Blocks

  • A single try block may contain multiple risky statements.
  • Different statements may generate different exceptions.
  • Handling each exception separately helps give accurate error messages.
  • It improves program safety and debugging.

Important Rules for Catch Blocks

  1. Order matters — catch blocks should go from more specific to more general. For example, ArithmeticException should come before Exception.
  2. Only one catch block executes — if an exception matches one catch block, the others are skipped.
  3. No unreachable catch — generic exceptions (like Exception) must be written at the end.

Example: Multiple Catch

try {
    int a = 10 / 0;
    int arr[] = new int[5];
    arr[10] = 50;
} catch (ArithmeticException e) {
    System.out.println("Divide by zero error");
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("Array index error");
} catch (Exception e) {
    System.out.println("General exception");
}

Benefits

  • Handles multiple errors cleanly
  • Prevents program crash
  • Helps in debugging
  • Makes code more readable and structured

Custom (User-Defined) Exceptions

In Java, exceptions are used to handle runtime errors. Along with built-in exceptions (like ArithmeticException, NullPointerException), Java also allows programmers to create their own custom exceptions. These are called user-defined exceptions or custom exceptions. User-defined exceptions are helpful when we want to handle application-specific errors that are not covered by Java’s built-in exceptions. A user-defined exception is a custom class created by the programmer that extends the Exception class or RuntimeException class. It represents an error condition that is specific to the program’s requirements.

Steps to Create User-Defined Exceptions

  1. Create a new class that extends Exception — this class represents the custom exception.
  2. Provide a constructor — the constructor passes an error message to the parent Exception class.
  3. Throw the exception — use the throw keyword to manually throw the exception.
  4. Handle it using try-catch block — the thrown exception should be caught and handled.

Example: AgeException

class AgeException extends Exception {
    AgeException(String msg) {
        super(msg);
    }
}

public class Test {
    public static void main(String[] args) {
        try {
            int age = 15;
            if (age < 18) {
                throw new AgeException("Age must be 18 or above");
            }
            System.out.println("Eligible for voting");
        } catch (AgeException e) {
            System.out.println("Custom Exception Caught: " + e.getMessage());
        }
    }
}

Explanation of Example:

  • A custom exception AgeException is created.
  • If age is less than 18, the program throws this exception.
  • The catch block handles the message and prevents program crash.

Benefits of Custom Exceptions

  • Makes error messages more meaningful
  • Helps implement program-specific rules
  • Improves reliability and readability
  • Allows better control over error handling

Byte Streams and File Handling

In Java, file handling is used to read data from files and write data into files. Java provides two main types of streams: byte streams and character streams. The byte stream classes are used to handle binary data such as images, audio, video, PDF, or any raw data. Byte streams work with 8-bit bytes, so they are suitable for transferring all types of data, not only text.

Main Byte Stream Classes

Java provides two important abstract classes for byte stream operations:

  • InputStream — used to read data from files in the form of bytes.
  • OutputStream — used to write data to files in bytes.

Some commonly used subclasses: FileInputStream, FileOutputStream, BufferedInputStream, BufferedOutputStream

FileInputStream Example: Reading from a file

import java.io.*;

class ReadFile {
    public static void main(String args[]) {
        try {
            FileInputStream fin = new FileInputStream("input.txt");
            int ch;
            while ((ch = fin.read()) != -1) {
                System.out.print((char) ch);
            }
            fin.close();
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

FileOutputStream Example: Writing into a file

import java.io.*;

class WriteFile {
    public static void main(String args[]) {
        try {
            FileOutputStream fout = new FileOutputStream("output.txt");
            String data = "Hello Java File Handling";
            fout.write(data.getBytes());
            fout.close();
            System.out.println("Data written successfully");
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

How Byte Streams Work

  • Data is transferred one byte at a time.
  • Suitable for binary files (images, videos, etc.).
  • read() returns an integer value (0–255) or -1 at end of stream.
  • write() writes binary data to the file.
  • Always close the stream using close() to prevent memory leaks.

Advantages of Byte Streams

  • Can work with all types of files
  • Efficient for binary data
  • Faster when used with buffered streams

Character Streams in Java

Character streams in Java are used to read and write text data (characters). Unlike byte streams that work with 8-bit bytes, character streams work with 16-bit Unicode characters, making them ideal for handling text files, alphabets, symbols, and languages. Character streams automatically handle character encoding, which makes them suitable for reading and writing human-readable text.

Main Character Stream Classes

Java provides two abstract classes for character stream operations:

  • Reader — used to read characters from a file or input source.
  • Writer — used to write characters to a file or output destination.

Common subclasses: FileReader, FileWriter, BufferedReader, BufferedWriter, CharArrayReader, CharArrayWriter

FileReader Example: Reading a text file

import java.io.*;

class ReadChar {
    public static void main(String args[]) {
        try {
            FileReader fr = new FileReader("data.txt");
            int ch;
            while ((ch = fr.read()) != -1) {
                System.out.print((char) ch);
            }
            fr.close();
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

FileWriter Example: Writing text into a file

import java.io.*;

class WriteChar {
    public static void main(String args[]) {
        try {
            FileWriter fw = new FileWriter("output.txt");
            String text = "Character stream example in Java";
            fw.write(text);
            fw.close();
            System.out.println("File written successfully");
        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

How Character Streams Work

  • Work with Unicode characters (16-bit).
  • Automatically handle character encoding and decoding.
  • Suitable for text files like .txt, .html, .xml etc.
  • read() returns a single character or -1 at end of file.
  • write() writes characters or strings directly.

Advantages of Character Streams

  • Best choice for handling text data
  • Encoding/decoding handled automatically
  • Easier to use for reading/writing characters and strings
  • Better readability for text-related operations

File I/O (Input/Output) in Java

File I/O in Java refers to the process of reading data from files and writing data into files. Java provides a powerful and flexible I/O system that works through streams. A stream is a sequence of data that flows from a source to a destination. File I/O is mainly used to store data permanently, transfer data, create logs, and read/write documents.

Types of Streams in Java

  • Input Stream — used to read data from a file or other source.
  • Output Stream — used to write data to a file.

There are two major categories:

  • Byte Streams — used for reading and writing 8-bit binary data like images, audio, video, etc. Main classes: FileInputStream, FileOutputStream
  • Character Streams — used for reading and writing text data (16-bit Unicode characters). Main classes: FileReader, FileWriter

Example: FileInputStream fin = new FileInputStream(“abc.txt”);

int data = fin.read();

Basic Steps in File I/O

  • Open the file — using a stream class like FileInputStream or FileWriter.
  • Perform operation — read using read(), write using write()
  • Close the file — always use close() to free memory.

Common Methods

  • read() → reads one byte/character
  • write() → writes one byte/character
  • close() → closes the stream
  • flush() → forces data to be written to file

Advantages of File I/O

  • Stores data permanently
  • Helps in communication between programs
  • Useful for large data processing
  • Supports both text and binary files

File Operations

File operations refer to different tasks we can perform on files such as creating, reading, writing, deleting, and checking file information. Java provides the java.io.File class and various stream classes to perform these operations easily. File operations are very important for storing data permanently, transferring information, and managing files in applications.

1. Creating a File

Java allows creating new files using the File class.

File f = new File("data.txt");
f.createNewFile();

2. Reading from a File

Java provides FileReader (character stream) and FileInputStream (byte stream) for reading files.

FileReader fr = new FileReader("data.txt");
int ch;
while ((ch = fr.read()) != -1) {
    System.out.print((char) ch);
}
fr.close();

3. Writing to a File

Java provides FileWriter and FileOutputStream for writing data.

FileWriter fw = new FileWriter("data.txt");
fw.write("Hello Java");
fw.close();

4. Checking File Information

We can check file details using methods of the File class:

  • exists() → checks if file exists
  • length() → returns file size
  • getName() → returns file name
  • getAbsolutePath() → full path
  • isDirectory() → checks if it’s a folder
File f = new File("data.txt");
System.out.println(f.exists());
System.out.println(f.length());

5. Deleting a File

Files can be removed using the delete() method.

File f = new File("old.txt");
f.delete();