Learning outcome: Select byte or character streams, read and write files, use buffering and explicit encodings, and close resources safely.
A program may read marks from a file, copy an image, or write a report. An I/O stream connects the program to a source or destination. Input means data entering the program; output means data leaving it. The same file can be an output destination in one operation and an input source in another. See Oracle's I/O overview.
Byte Streams and Character Streams
| Family | Input base class | Output base class | Use |
|---|---|---|---|
| Byte streams | InputStream |
OutputStream |
Binary files, images, encoded bytes |
| Character streams | Reader |
Writer |
Text decoded or encoded with a character set |
FileInputStream and FileOutputStream access file bytes. FileReader and FileWriter access file text. In Java 8, the latter use the default charset; for portable text examples, this lesson uses Files.newBufferedReader and Files.newBufferedWriter with UTF-8. The classes in java.nio.file complement java.io.
InputStreamReader bridges bytes to characters; OutputStreamWriter bridges characters to bytes. A character need not occupy one byte in UTF-8. Passing arbitrary image bytes through a text decoder can corrupt them, so copy binary data as bytes.
Reading Until the End
For a single-byte read, InputStream.read() returns an int in the range 0–255, or -1 at end of input. The result must hold both byte values and the end marker. A buffer read returns the number of bytes obtained, or -1 when finished. A short read is valid; write only the bytes actually read. See the FileInputStream API.
For text lines, BufferedReader.readLine() returns a String without its line terminator, or null at end of input. An empty line is "", not null. Do not use available() as a file length or end-of-file test: it estimates bytes readable without blocking.
Copy a Binary File
Save as BinaryCopy.java. Put a file named input.bin in the working directory. The program creates or replaces copy.bin; use a separate destination from the source.
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BinaryCopy {
public static void main(String[] args) {
long total = 0;
try (BufferedInputStream in = new BufferedInputStream(
new FileInputStream("input.bin"));
BufferedOutputStream out = new BufferedOutputStream(
new FileOutputStream("copy.bin"))) {
byte[] buffer = new byte[4096];
int count;
while ((count = in.read(buffer)) != -1) {
out.write(buffer, 0, count);
total += count;
}
} catch (IOException e) {
System.err.println("Copy failed: " + e.getMessage());
return;
}
System.out.println("Copied " + total + " bytes");
}
}
For an input containing the five bytes 0, 10, 65, 128, 255, output is:
Copied 5 bytes
Buffering reduces small operations on the underlying stream. The try resource list closes the output and input in reverse declaration order, even if an exception occurs. Closing the outer buffered stream also closes the wrapped stream. On a failed copy, a partial destination may remain; report the failure rather than claiming success.
Write and Read UTF-8 Text
Save as TextFileDemo.java. It creates or replaces marks.txt in the working directory, then reads it line by line.
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class TextFileDemo {
public static void main(String[] args) {
Path file = Paths.get("marks.txt");
try {
try (BufferedWriter writer = Files.newBufferedWriter(
file, StandardCharsets.UTF_8)) {
writer.write("Asha,84");
writer.newLine();
writer.write("Kabir,91");
writer.newLine();
}
try (BufferedReader reader = Files.newBufferedReader(
file, StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
} catch (IOException e) {
System.err.println("File operation failed: " + e.getMessage());
}
}
}
Output:
Asha,84
Kabir,91
To append with this API, pass StandardOpenOption.CREATE and StandardOpenOption.APPEND after the charset argument, and import java.nio.file.StandardOpenOption. The Files API defines the options. Relative paths start from the program's working directory, which may differ from the source file's directory.
Standard Streams and Other Useful Streams
System.in is standard input. System.out is normal output and System.err is diagnostic output; the output objects are PrintStream instances. A Scanner can read tokens from System.in, or an InputStreamReader and BufferedReader can read text. A console's charset must match its actual encoding. Closing a scanner or reader wrapped around System.in also closes standard input, so reusable methods should not close a stream owned by their caller.
| Classes | Role |
|---|---|
BufferedInputStream, BufferedOutputStream |
Buffer byte I/O |
BufferedReader, BufferedWriter |
Buffer character I/O; support convenient line operations |
ByteArrayInputStream, ByteArrayOutputStream |
Read and write bytes in memory |
DataInputStream, DataOutputStream |
Read and write primitive values in a binary format |
ObjectInputStream, ObjectOutputStream |
Read and write serialized objects |
If a DataOutputStream writes writeInt(84) followed by writeDouble(8.5), the corresponding reader must call readInt() followed by readDouble(). This is binary data, not the text "84 8.5". Reading the wrong type or order misinterprets the record. Reaching the end before a requested primitive is complete causes EOFException.
Resource and Error Handling
Use try-with-resources for streams the method opens. It works with AutoCloseable resources and preserves the main exception if closing also fails. flush() pushes buffered output toward the destination but does not close the stream or guarantee physical disk durability. Closing a writer flushes it.
An IOException may indicate a missing file, denied access, a full disk, or another I/O failure. Decide whether to handle it locally or declare it with throws. Do not hide a failure behind an empty catch block.
In simple terms: A buffer is a temporary tray that groups small reads or writes. The charset translates between file bytes and text. Closing the stream releases the connection to the file.
Practice
- Copy a small image and confirm that its size and contents match the original.
- Read a UTF-8 text file and count its lines, including blank lines.
- Append one student record without erasing previous records.
- Write an integer and a double with data streams, then read both back in the same order.
Quick Check
1. Why does a single-byte read return int?
It must represent every byte value from 0 to 255 and the separate end marker -1.
2. Is a java.io stream the same as a java.util.stream.Stream?
No. I/O streams transfer data. The Stream API describes computations over elements, such as filtering marks. A file can be a source for such a computation.
3. Does creating a File or Path object create a disk file?
No. It represents a path. A file operation such as opening an output stream with creation enabled creates the file.
Summary
Use byte streams to preserve arbitrary bytes and character streams to interpret text. Read to the correct end marker, choose an encoding, and close owned resources. Continue with serialization and deserialization.