Chapter 1 - Input and Output in Java

Input and output (I/O) operations are fundamental to any program as they allow data to be received (input) and sent out (output).

In Java, the java.io package provides extensive classes and methods for handling I/O operations. At the core of Java's I/O system is the concept of streams.

What is a Stream?

A stream in Java is a continuous flow of data. It represents a sequence of bytes (or characters) coming from a source (input) or going to a destination (output).

Streams abstract the underlying source or destination, allowing developers to focus on reading or writing data without worrying about the specific details of the input/output device.

Streams are categorized into:

Input Stream: Reads data from a source (e.g., file, keyboard). - important

Output Stream: Writes data to a destination (e.g., file, console). - important

Byte Streams: For reading and writing binary data.

Character Streams: For reading and writing text data.

Default Streams in Java

Before exploring different input/output streams, let’s take a look at the three standard/default streams in Java, which are also the most commonly used:

  1. System.in: Standard input stream, typically used to read data from the keyboard.

  2. System.out: Standard output stream, used to print results to the console or screen.

  3. System.err: Standard error stream, used to output error messages to the console.

Example:

Using System.in

  • Scanner scanner = new Scanner(System.in): The Scanner class is used to read input from System.in.

  • The program prompts the user to enter their name and age, then prints the values entered by the user.

import java.util.Scanner;

public class SystemInExample {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // Reading a string input from the user
        System.out.print("Enter your name: ");
        String name = scanner.nextLine();

        // Reading an integer input from the user
        System.out.print("Enter your age: ");
        int age = scanner.nextInt();

        System.out.println("Name: " + name);
        System.out.println("Age: " + age);

        scanner.close();
    }
}

Using System.err

System.err is typically used to print error messages. It behaves like System.out, but it’s used for outputting errors and warnings.

System.err.println("Error: The number cannot be negative."): Prints the error message to the console using the error stream, System.err.

public class SystemErrExample {
    public static void main(String[] args) {
        int number = -5;

        // Check if the number is positive
        if (number < 0) {
            System.err.println("Error: The number cannot be negative.");
        } else {
            System.out.println("The number is " + number);
        }
    }
}

Using System.out

public class OutputExample {
    public static void main(String[] args) {
        System.out.println("Hello, World!"); // with ln means next line 
        // without ln means continues - doesnt move to next line
        System.out.print("Hello"); 
        System.out.print(" World!");

        // through this we can format thee output
        int num = 100;
        System.out.printf("Number: %d", num);
    }
}

// output
Hello, World!
Hello World!
// formated as number
Number: 100

Types of Streams

1. Byte Streams

Byte streams are used to handle raw binary data and read/write data byte by byte. They are ideal for dealing with file I/O, such as copying files.

  • Input Streams: FileInputStream, BufferedInputStream, DataInputStream

  • Output Streams: FileOutputStream, BufferedOutputStream, DataOutputStream

Example: Copying a File with Byte Streams
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamExample {
    public static void main(String[] args) {
        FileInputStream inputStream = null;
        FileOutputStream outputStream = null;

        try {
            // Open a file to read
            inputStream = new FileInputStream("source.txt");

            // Open a file to write
            outputStream = new FileOutputStream("destination.txt");

            // Read byte by byte and write it to another file
            int byteData;
            while ((byteData = inputStream.read()) != -1) {
                outputStream.write(byteData);
            }

            System.out.println("File copied successfully!");

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // Close streams
                if (inputStream != null) inputStream.close();
                if (outputStream != null) outputStream.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

Explanation:

  • FileInputStream: Reads bytes from a source file.

  • FileOutputStream: Writes bytes to a destination file.

  • The program reads each byte from source.txt and writes it to destination.txt.

2. Character Streams

Character streams handle text data, using Unicode to automatically encode and decode characters. They read/write data character by character and are typically used when working with text files.

  • Input Streams: FileReader, BufferedReader, InputStreamReader

  • Output Streams: FileWriter, BufferedWriter, PrintWriter

Example: Reading a File with Character Stream
import java.io.FileReader;
import java.io.IOException;

public class CharacterStreamExample {
    public static void main(String[] args) {
        FileReader reader = null;

        try {
            reader = new FileReader("test.txt");

            // Read and display each character from the file
            int character;
            while ((character = reader.read()) != -1) {
                System.out.print((char) character);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (reader != null) reader.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

Explanation:

  • FileReader: Reads characters from a file.

  • The program reads each character from test.txt and prints it to the console.

Summary Notes:

  • Stream abstraction: Streams in Java abstract the details of I/O sources and destinations, allowing you to focus on reading or writing data without worrying about how it is stored or transferred.

  • Standard streams:

    • System.in: Used for standard input (keyboard).

    • System.out: Used to standard output( file or console).

    • System.err: Used to show output in error format.

        import java.util.Scanner;
      
        public class SystemInExample {
            public static void main(String[] args) {
                Scanner scanner = new Scanner(System.in);
                System.out.print("Enter your name: ");
                String name = scanner.nextLine();  // Reads input from keyboard
                System.out.println("Hello, " + name + "!");
      
                // system.err
                int number = -1;
                if (number < 0) {
                    System.err.println("Error: The number cannot be negative.");  // Outputs error to the console
                }
                scanner.close();
            }
        }
      

Byte streams are used for handling raw binary data.

  • Character streams are used for handling text data with automatic character encoding/decoding.

  • Error handling: Always remember to handle I/O exceptions (e.g., IOException) properly, especially when working with files or streams.

  • Closing streams: Always close streams in the finally block or use try-with-resources to ensure streams are closed even if an exception occurs.

1
Subscribe to my newsletter

Read articles from Vaishnavi Dwivedi directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Vaishnavi Dwivedi
Vaishnavi Dwivedi