Input/Output (I/O) in Java allows your application to interact with external sources: files on disk, network sockets, keyboard inputs, and printer streams. In Java, all data transport is modeled through Streams — an ordered sequence of data flowing from a producer (source) to a consumer (destination).
In Java I/O: Input means reading data INTO your JVM memory from an external source. Output means writing data OUT from your JVM memory to an external file or destination.
Java divides I/O streams into two fundamentally distinct hierarchies based on data granularity:
| Feature | Byte Streams (1 Byte / 8-bit) | Character Streams (2 Bytes / 16-bit) |
|---|---|---|
| Root Abstract Classes | InputStream / OutputStream | Reader / Writer |
| Primary Data Type | Raw binary data: byte, images, PDFs, MP3s | Unicode text: char, Strings, text logs, CSV |
| Encoding Awareness | None (raw bytes passed as-is) | Translates bytes to Unicode chars (UTF-8, UTF-16, ASCII) |
| Common Disk Implementations | FileInputStream, FileOutputStream | FileReader, FileWriter |
| Buffered Decorators | BufferedInputStream, BufferedOutputStream | BufferedReader, BufferedWriter |
The File class represents an abstract pathname of a file or directory on the file system. It does not read or write data inside the file; it inspects and manages file metadata (existence, length, permissions, deletion, directory creation).
import java.io.File;
import java.io.IOException;
public class FileDemo {
public static void main(String[] args) {
File file = new File("veewom-notes.txt");
try {
if (file.createNewFile()) {
System.out.println("File created: " + file.getName());
} else {
System.out.println("File already exists on disk.");
}
System.out.println("Absolute Path: " + file.getAbsolutePath());
System.out.println("Can Read: " + file.canRead());
System.out.println("Can Write: " + file.canWrite());
System.out.println("File Size (bytes): " + file.length());
System.out.println("Is Directory: " + file.isDirectory());
} catch (IOException e) {
System.err.println("I/O Error: " + e.getMessage());
}
}
}
Byte streams read and write raw 8-bit bytes. Use byte streams whenever you transfer non-text binary assets (e.g., JPEG images, ZIP archives) or need raw binary compatibility.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamCopy {
public static void main(String[] args) {
// Copying an image byte-by-byte with try-with-resources
try (FileInputStream in = new FileInputStream("input.png");
FileOutputStream out = new FileOutputStream("copy.png")) {
byte[] buffer = new byte[4096]; // 4 KB chunk buffer
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
System.out.println("Binary file copied successfully!");
} catch (IOException e) {
System.err.println("File copy failed: " + e.getMessage());
}
}
}
Character streams read and write 16-bit Unicode characters. In modern Java (Java 11+), you can explicitly specify the StandardCharsets.UTF_8 encoding to avoid platform-dependent encoding bugs (e.g., Windows CP-1252 vs Linux UTF-8).
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class CharStreamDemo {
public static void main(String[] args) {
String data = "Welcome to Veewom Java Tutorials!\nLearn Java 21 LTS.";
// Writing text using FileWriter
try (FileWriter writer = new FileWriter("greeting.txt", StandardCharsets.UTF_8)) {
writer.write(data);
System.out.println("Data successfully written to greeting.txt");
} catch (IOException e) {
e.printStackTrace();
}
// Reading text using FileReader
try (FileReader reader = new FileReader("greeting.txt", StandardCharsets.UTF_8)) {
int ch;
while ((ch = reader.read()) != -1) {
System.out.print((char) ch); // cast int to character
}
System.out.println();
} catch (IOException e) {
e.printStackTrace();
}
}
}
| Step | Expression | Variable (ch / bytesRead) | Action / Condition |
|---|---|---|---|
| 1 | in.read(buffer) | 4096 | Reads first 4096 bytes from disk; condition != -1 is TRUE. Writes chunk. |
| 2 | in.read(buffer) | 1420 | Reads remaining 1420 bytes of file; condition != -1 is TRUE. Writes slice. |
| 3 | in.read(buffer) | -1 (EOF) | End-of-File reached. While loop terminates immediately. |
| 4 | try-with-resources exit | in.close(), out.close() | Native OS file handles released in reverse order of declaration. |
read() method return an int instead of a byte or char?
-1. An int has 32 bits, allowing 0 to 255 for valid byte values and -1 for EOF without collision.InputStream and Reader when asked in theoretical papers.Accelerate your Java mastery by studying these tightly connected core tutorials:
High-speed buffered reading and token parsing.
Modern non-blocking and high-performance file APIs.
Safe automatic closing of file streams.
Saving and restoring Java objects to file streams.