Fundamentals 26 min read

Java I/O Streams: Byte Streams, Character Streams, Buffered Streams, Data Streams, and Object Streams

This article provides a comprehensive tutorial on Java I/O streams, covering byte streams, character streams, buffered streams, line‑oriented I/O, scanning and formatting APIs, command‑line I/O, data streams for primitive types, and object streams for serialization, with detailed code examples and best‑practice guidelines.

Java Captain
Java Captain
Java Captain
Java I/O Streams: Byte Streams, Character Streams, Buffered Streams, Data Streams, and Object Streams

Byte Streams

Byte streams handle raw binary data using InputStream and OutputStream . The example CopyBytes copies a file byte‑by‑byte and demonstrates proper resource handling with a finally block.

public class CopyBytes {
    /**
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        FileInputStream in = null;
        FileOutputStream out = null;
        try {
            in = new FileInputStream("resources/xanadu.txt");
            out = new FileOutputStream("resources/outagain.txt");
            int c;
            while ((c = in.read()) != -1) {
                out.write(c);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }
}

Closing streams is essential to avoid resource leaks; the program also checks for null before closing to handle file‑opening failures.

Byte streams are low‑level; for text data, character streams are preferred.

Character Streams

Character streams automatically convert between the internal Unicode representation and the platform’s charset. The CopyCharacters example uses FileReader and FileWriter to copy text.

public class CopyCharacters {
    /**
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        FileReader inputStream = null;
        FileWriter outputStream = null;
        try {
            inputStream = new FileReader("resources/xanadu.txt");
            outputStream = new FileWriter("resources/characteroutput.txt");
            int c;
            while ((c = inputStream.read()) != -1) {
                outputStream.write(c);
            }
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
        }
    }
}

Character streams are essentially buffered byte streams with charset translation. Bridging streams such as InputStreamReader and OutputStreamWriter connect byte and character APIs.

Line‑oriented I/O

The CopyLines example shows how to read and write whole lines using BufferedReader and PrintWriter .

public class CopyLines {
    /**
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        BufferedReader inputStream = null;
        PrintWriter outputStream = null;
        try {
            inputStream = new BufferedReader(new FileReader("resources/xanadu.txt"));
            outputStream = new PrintWriter(new FileWriter("resources/characteroutput.txt"));

            String l;
            while ((l = inputStream.readLine()) != null) {
                outputStream.println(l);
            }
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
        }
    }
}

Reading lines abstracts away platform‑specific line terminators, making the code portable.

Buffered Streams

Buffered streams improve performance by reducing the number of native I/O calls. Wrapping a non‑buffered stream with a buffered counterpart creates a buffer that is only flushed when full or when flush() is called.

inputStream = new BufferedReader(new FileReader("xanadu.txt"));
outputStream = new BufferedWriter(new FileWriter("characteroutput.txt"));

Four buffered classes exist: BufferedInputStream , BufferedOutputStream , BufferedReader , and BufferedWriter .

Scanning and Formatting

The scanning API tokenizes input, while the formatting API produces formatted output. The ScanXan example reads words from a file, and ScanSum sums numbers using a locale‑aware scanner.

public class ScanXan {
    /**
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        Scanner s = null;
        try {
            s = new Scanner(new BufferedReader(new FileReader("resources/xanadu.txt")));
            while (s.hasNext()) {
                System.out.println(s.next());
            }
        } finally {
            if (s != null) {
                s.close();
            }
        }
    }
}
s.useDelimiter(",\\s*");
public class ScanSum {
    /**
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        Scanner s = null;
        double sum = 0;
        try {
            s = new Scanner(new BufferedReader(new FileReader("resources/usnumbers.txt")));
            s.useLocale(Locale.US);
            while (s.hasNext()) {
                if (s.hasNextDouble()) {
                    sum += s.nextDouble();
                } else {
                    s.next();
                }
            }
        } finally {
            s.close();
        }
        System.out.println(sum);
    }
}

Formatting uses printf -style format strings. Example Root shows print / println , while Root2 demonstrates format with specifiers.

public class Root {
    /**
     * @param args
     */
    public static void main(String[] args) {
        int i = 2;
        double r = Math.sqrt(i);

        System.out.print("The square root of ");
        System.out.print(i);
        System.out.print(" is ");
        System.out.print(r);
        System.out.println(".");

        i = 5;
        r = Math.sqrt(i);
        System.out.println("The square root of " + i + " is " + r + ".");
    }
}
public class Root2 {
    /**
     * @param args
     */
    public static void main(String[] args) {
        int i = 2;
        double r = Math.sqrt(i);
        System.out.format("The square root of %d is %f.%n", i, r);
    }
}

Command‑Line I/O

Java provides three standard streams ( System.in , System.out , System.err ) and a higher‑level Console object for interactive programs. The console supports secure password entry.

InputStreamReader cin = new InputStreamReader(System.in);
public class Password {
    /**
     * @param args
     */
    public static void main(String[] args) {
        Console c = System.console();
        if (c == null) {
            System.err.println("No console.");
            System.exit(1);
        }

        String login = c.readLine("Enter your login: ");
        char[] oldPassword = c.readPassword("Enter your old password: ");
        if (verify(login, oldPassword)) {
            boolean noMatch;
            do {
                char[] newPassword1 = c.readPassword("Enter your new password: ");
                char[] newPassword2 = c.readPassword("Enter new password again: ");
                noMatch = !Arrays.equals(newPassword1, newPassword2);
                if (noMatch) {
                    c.format("Passwords don't match. Try again.%n");
                } else {
                    change(login, newPassword1);
                    c.format("Password for %s changed.%n", login);
                }
                Arrays.fill(newPassword1, ' ');
                Arrays.fill(newPassword2, ' ');
            } while (noMatch);
        }
        Arrays.fill(oldPassword, ' ');
    }
    static boolean verify(String login, char[] password) { return true; }
    static void change(String login, char[] password) { }
}

Data Streams

Data streams provide binary I/O for primitive types and strings. The example writes an invoice consisting of price, unit count, and description, then reads it back, demonstrating matching write / read calls and EOF handling.

static final String dataFile = "invoicedata";
static final double[] prices = { 19.99, 9.99, 15.99, 3.99, 4.99 };
static final int[] units = { 12, 8, 13, 29, 50 };
static final String[] descs = {
    "Java T-shirt",
    "Java Mug",
    "Duke Juggling Dolls",
    "Java Pin",
    "Java Key Chain"
};
out = new DataOutputStream(new BufferedOutputStream(new FileInputStream(dataFile)));
for (int i = 0; i < prices.length; i++) {
    out.writeDouble(prices[i]);
    out.writeInt(units[i]);
    out.writeUTF(descs[i]);
}
in = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile)));
double price;
int unit;
String desc;
double total = 0.0;
try {
    while (true) {
        price = in.readDouble();
        unit = in.readInt();
        desc = in.readUTF();
        System.out.format("You ordered %d units of %s at $%.2f%n", unit, desc, price);
        total += unit * price;
    }
} catch (EOFException e) {
    // end of file
}

Using float for monetary values is discouraged; BigDecimal is recommended.

Object Streams

Object streams serialize objects that implement Serializable . The example writes a Calendar , several BigDecimal prices, and associated data, then reads them back, demonstrating mixed primitive and object I/O.

public class ObjectStreams {
    static final String dataFile = "invoicedata";
    static final BigDecimal[] prices = {
        new BigDecimal("19.99"),
        new BigDecimal("9.99"),
        new BigDecimal("15.99"),
        new BigDecimal("3.99"),
        new BigDecimal("4.99")
    };
    static final int[] units = { 12, 8, 13, 29, 50 };
    static final String[] descs = {
        "Java T-shirt",
        "Java Mug",
        "Duke Juggling Dolls",
        "Java Pin",
        "Java Key Chain"
    };

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectOutputStream out = null;
        try {
            out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile)));
            out.writeObject(Calendar.getInstance());
            for (int i = 0; i < prices.length; i++) {
                out.writeObject(prices[i]);
                out.writeInt(units[i]);
                out.writeUTF(descs[i]);
            }
        } finally {
            out.close();
        }

        ObjectInputStream in = null;
        try {
            in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(dataFile)));
            Calendar date = (Calendar) in.readObject();
            System.out.format("On %tA, %

Object streams preserve object identity within a single stream; multiple references to the same object are serialized once and restored as shared references upon deserialization.

JavaSerializationstreamsIOBufferingformattingfile-ioscanning
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.