1. Character Streams
1.1 Why Character Streams Exist
Byte streams alone present challenges when handling Chinese characters. Java provides character streams to address this limitation.
Character Stream Basics
- A character stream combines byte stream operations with character encoding
- When using byte streams to copy text files containing Chinese characters, the process works seamlessly because the底层 automatically assembles bytes into characters
- Chinese characters are identified by the fact that their first byte is always negative
1.2 Character Encoding Tables
Character Set Definition A character set is the complete collection of all characters supported by a system, including national alphabets, punctuation marks, graphical symbols, and numbers. For computers to accurately store and recognize character set symbols, character encoding is necesary. Each character set requires at least one character encoding scheme.
Common Character Sets
ASCII Character Set The fundamental encoding system based on Latin letters, used to display modern English. It includes control characters (carriage return, backspace, line feed) and displayable characters (English uppercase/lowercase letters, Arabic numerals, Western symbols).
- Basic ASCII uses 7 bits per character (128 characters)
- Extended ASCII uses 8 bits per character (256 characters), supporting common European characters
GBK Character Set The most common Chinese encoding standard. An extension of GB2312, using double-byte encoding with 21,003 Chinese characters. Fully compatible with GB2312 while supporting traditional Chinese characters and Japanese/Korean characters.
Unicode Character Set
UTF-8 Encoding Used to represent any character in the Unicode standard. Thiss is the preferred encoding for email, web pages, and other text storage or transmission applications. The Internet Engineering Task Force (IETF) requires all Internet protocols to support UTF-8 encoding. It uses one to four bytes per character:
Latin and similar characters need two bytes
Most common characters (including Chinese) use three bytes
Rare Unicode supplementary characters use four bytes
Note: UTF-8 is not a character set itself but one of many encoding formats within the Unicode character set.
1.3 Encoding and Decoding in Strings
Related Methods
| Method | Description |
|---|---|
| byte[] getBytes() | Encodes this String into a byte sequence using the platform's default charset |
| byte[] getBytes(String charsetName) | Encodes this String into a byte sequence using the specified charset |
| String(byte[] bytes) | Creates a string by decoding the byte array using the platform's default charset |
| String(byte[] bytes, String charsetName) | Creates a string by decoding the byte array using the specified charset |
Code Example
import java.util.Arrays;
import java.io.UnsupportedEncodingException;
public class StringEncodingDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
// Define a string containing Chinese characters
String text = "China";
// Default encoding
byte[] defaultBytes = text.getBytes();
// UTF-8 encoding
byte[] utfBytes = text.getBytes("UTF-8");
// GBK encoding - produces different byte sequence
byte[] gbkBytes = text.getBytes("GBK");
System.out.println("Default: " + Arrays.toString(defaultBytes));
System.out.println("UTF-8: " + Arrays.toString(utfBytes));
System.out.println("GBK: " + Arrays.toString(gbkBytes));
// Decoding with specific charset
String decoded = new String(gbkBytes, "GBK");
System.out.println("Decoded: " + decoded);
}
}
1.4 Writing Data with Character Streams
Overview
- Writer: Abstract parent class for writing character streams
- FileWriter: Commonly used subclass for writing character streams
Constructors
| Constructor | Description |
|---|---|
| FileWriter(File file) | Creates a FileWriter for the given File object |
| FileWriter(File file, boolean append) | Creates a FileWriter with append mode |
| FileWriter(String fileName) | Creates a FileWriter for the given filename |
| FileWriter(String fileName, boolean append) | Creates a FileWriter with append mode for the given filename |
Member Methods
| Method | Description |
|---|---|
| void write(int c) | Writes a single character |
| void write(char[] cbuf) | Writes a character array |
| void write(char[] cbuf, int off, int len) | Writes a portion of a character array |
| void write(String str) | Writes a string |
| void write(String str, int off, int len) | Writes a portion of a string |
Flush and Close Methods
| Method | Description |
|---|---|
| void flush() | Flushes the stream, allowing continued writing |
| void close() | Closes the stream and releases resources. Flushes before closing. |
Code Example
import java.io.FileWriter;
import java.io.IOException;
public class CharacterWriterDemo {
public static void main(String[] args) throws IOException {
FileWriter writer = new FileWriter("output/data.txt");
// Write a single character
// writer.write(97);
// writer.write(98);
// writer.write(99);
// Write a character array
char[] data = {'h', 'e', 'l', 'l', 'o'};
// writer.write(data);
// Write a portion of character array
// writer.write(data, 0, data.length);
// writer.write(data, 1, 3);
// Write a string
// writer.write("helloworld");
// Write a portion of string
String message = "helloworld";
writer.write(message, 1, 3);
writer.close();
}
}
1.5 Reading Data with Character Streams
Overview
- Reader: Abstract parent class for reading character streams
- FileReader: Commonly used subclass for reading character streams
Constructors
| Constructor | Description |
|---|---|
| FileReader(File file) | Creates a FileReader for the given File |
| FileReader(String fileName) | Creates a FileReader for the given filename |
Member Methods
| Method | Description |
|---|---|
| int read() | Reads a single character |
| int read(char[] cbuf) | Reads into a character array |
Code Example
import java.io.FileReader;
import java.io.IOException;
public class CharacterReaderDemo {
public static void main(String[] args) throws IOException {
FileReader reader = new FileReader("output/data.txt");
// Read single character at a time
// int ch;
// while ((ch = reader.read()) != -1) {
// System.out.print((char) ch);
// }
// Read into character array
char[] buffer = new char[1024];
int length;
while ((length = reader.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, length));
}
reader.close();
}
}
1.6 User Registration Example
Requirements Save username and password entered from keyboard to local file for permanent storage.
Implementation Steps
- Accept username and password from keyboard input
- Write data to local file
- Close stream and release resources
Code Example
import java.util.Scanner;
import java.io.FileWriter;
import java.io.IOException;
public class UserRegistrationDemo {
public static void main(String[] args) throws IOException {
// Accept user input
Scanner scanner = new Scanner(System.in);
System.out.println("Please enter username:");
String username = scanner.next();
System.out.println("Please enter password:");
String password = scanner.next();
// Write to local file
FileWriter writer = new FileWriter("userdata/credentials.txt");
writer.write(username);
writer.write("\r\n");
writer.write(password);
writer.flush();
writer.close();
scanner.close();
}
}
1.7 Buffered Character Streams
Introduction
- BufferedWriter: Writes text to character output stream with buffering for efficient single character, array, and string writing
- BufferedReader: Reads text from character input stream with buffering for efficient character, array, and line reading
Constructors
| Constructor | Description |
|---|---|
| BufferedWriter(Writer out) | Creates buffered character output stream |
| BufferedReader(Reader in) | Creates buffered character input stream |
Code Example
import java.io.BufferedWriter;
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.FileReader;
import java.io.IOException;
public class BufferedStreamDemo {
public static void main(String[] args) throws IOException {
// BufferedWriter
BufferedWriter bufferedWriter = new BufferedWriter(
new FileWriter("output/buffered.txt")
);
bufferedWriter.write("first line\r\n");
bufferedWriter.write("second line\r\n");
bufferedWriter.close();
// BufferedReader
BufferedReader bufferedReader = new BufferedReader(
new FileWriter("output/buffered.txt")
);
// Read single character at a time
// int ch;
// while ((ch = bufferedReader.read()) != -1) {
// System.out.print((char) ch);
// }
// Read character array
char[] buffer = new char[1024];
int length;
while ((length = bufferedReader.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, length));
}
bufferedReader.close();
}
}
1.8 Special Buffered Stream Methods
BufferedWriter Methods
| Method | Description |
|---|---|
| void newLine() | Writes a line separator. The separator is determined by system properties. |
BufferedReader Methods
| Method | Description |
|---|---|
| String readLine() | Reads a line of text. Returns content without line terminators, or null if end of stream reached. |
Code Example
import java.io.BufferedWriter;
import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.FileReader;
import java.io.IOException;
public class BufferedSpecialMethodsDemo {
public static void main(String[] args) throws IOException {
// Create buffered output stream
BufferedWriter writer = new BufferedWriter(
new FileWriter("output/sequential.txt")
);
// Write data line by line
for (int i = 0; i < 10; i++) {
writer.write("data item " + i);
writer.newLine();
writer.flush();
}
writer.close();
// Create buffered input stream
BufferedReader reader = new BufferedReader(
new FileReader("output/sequential.txt")
);
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
}
}
1.9 File Data Sorting Example
Requirements Read data from a file using buffered character streams, sort it, and write back to the local file.
Implementation Steps
- Read file data into program
- Process the read data
- Add processed data to collection
- Sort collection data
- Write sorted data back to file
Code Example
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
public class DataSortingDemo {
public static void main(String[] args) throws IOException {
// Read file data
BufferedReader reader = new BufferedReader(
new FileReader("data/unsorted.txt")
);
String content = reader.readLine();
System.out.println("Read data: " + content);
reader.close();
// Split by space
String[] parts = content.split(" ");
// Convert String array to int array
int[] numbers = new int[parts.length];
for (int i = 0; i < parts.length; i++) {
numbers[i] = Integer.parseInt(parts[i]);
}
// Sort
Arrays.sort(numbers);
System.out.println("Sorted: " + Arrays.toString(numbers));
// Write sorted data back
BufferedWriter writer = new BufferedWriter(
new FileWriter("data/unsorted.txt")
);
for (int i = 0; i < numbers.length; i++) {
writer.write(numbers[i] + " ");
writer.flush();
}
writer.close();
}
}
1.10 I/O Stream Summary
2. Conversion Streams
2.1 Two Classes for Encoding Issues
InputStreamReader Acts as a bridge from byte streams to character streams. It reads bytes and decodes them into characters using specified encoding. The character set can be specified by name or use the platform default.
OutputStreamWriter Acts as a bridge from character streams to byte streams. It encodes characters using specified encoding before writing. The character set can be specified by name or use the platform default.
2.2 Reading and Writing with Conversion Streams
Constructors
| Constructor | Description |
|---|---|
| InputStreamReader(InputStream in) | Creates InputStreamReader with default encoding |
| InputStreamReader(InputStream in, String charset) | Creates InputStreamReader with specified encoding |
| OutputStreamWriter(OutputStream out) | Creates OutputStreamWriter with default encoding |
| OutputStreamWriter(OutputStream out, String charset) | Creates OutputStreamWriter with specified encoding |
Code Example
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.IOException;
public class ConversionStreamDemo {
public static void main(String[] args) throws IOException {
// Write with specific encoding
OutputStreamWriter writer = new OutputStreamWriter(
new FileOutputStream("output/encoded.txt"), "GBK"
);
writer.write("Chinese characters");
writer.close();
// Read with same encoding
InputStreamReader reader = new InputStreamReader(
new FileInputStream("output/encoded.txt"), "GBK"
);
int ch;
while ((ch = reader.read()) != -1) {
System.out.print((char) ch);
}
reader.close();
}
}
3. Object Serialization Streams
3.1 Object Serialization Stream
Object Serialization Overview Object serialization saves objects to disk or transmits them over networks. This mechanism uses a byte sequence representing an object that includes: object type, object data, and object properties. Once written to a file, the byte sequence persists the object information. Conversely, the byte sequence can be read back to reconstruct the object through deserialization.
ObjectOutputStream Writes Java object primitive data and graphics to OutputStream. Objects can be reconstructed using ObjectInputStream. Object persistence can be achieved using file-based streams.
Constructors
| Constructor | Description |
|---|---|
| ObjectOutputStream(OutputStream out) | Creates ObjectOutputStream writing to specified OutputStream |
Serialization Method
| Method | Description |
|---|---|
| void writeObject(Object obj) | Writes specified object to ObjectOutputStream |
Person Class
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + '\'' + ", age=" + age + '}';
}
}
Test Class
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
public class ObjectOutputDemo {
public static void main(String[] args) throws IOException {
ObjectOutputStream outputStream = new ObjectOutputStream(
new FileOutputStream("objects/data.obj")
);
Person person = new Person("John Doe", 30);
outputStream.writeObject(person);
outputStream.close();
}
}
Important Notes
- For an object to be serializable, its class must implement Serializable interface
- Serializable is a marker interface requiring no method implementation
3.2 Object Deserialization Stream
ObjectInputStream Deserializes previously serialized objects and primitive data written by ObjectOutputStream.
Constructors
| Constructor | Description |
|---|---|
| ObjectInputStream(InputStream in) | Creates ObjectInputStream reading from specified InputStream |
Deserialization Method
| Method | Description |
|---|---|
| Object readObject() | Reads an object from ObjectInputStream |
Code Example
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.IOException;
import java.io.ClassNotFoundException;
public class ObjectInputDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream inputStream = new ObjectInputStream(
new FileInputStream("objects/data.obj")
);
Object obj = inputStream.readObject();
Person person = (Person) obj;
System.out.println(person.getName() + ", " + person.getAge());
inputStream.close();
}
}
3.3 serialVersionUID and transient
serialVersionUID After serializing an object, if the class definition changes, deserialization will fail with InvalidClassException. Solutions:
- Re-serialize the object
- Add serialVersionUID to the class:
private static final long serialVersionUID = 42L;
transient To exclude certain fields from serialization, mark them with transient. Such fields will not be written to file and will return null upon deserialization.
Modified Person Class
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 42L;
private String name;
private transient int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Test Class
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.ClassNotFoundException;
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
serialize();
deserialize();
}
private static void deserialize() throws IOException, ClassNotFoundException {
ObjectInputStream inputStream = new ObjectInputStream(
new FileInputStream("objects/data.obj")
);
Object obj = inputStream.readObject();
Person person = (Person) obj;
System.out.println(person.getName() + ", " + person.getAge());
inputStream.close();
}
private static void serialize() throws IOException {
ObjectOutputStream outputStream = new ObjectOutputStream(
new FileOutputStream("objects/data.obj")
);
Person person = new Person("John Doe", 30);
outputStream.writeObject(person);
outputStream.close();
}
}
3.4 Object Stream Practice
Requirements Create multiple Person objects, write to file, then read back into memory.
Implementation Steps
- Create serialization stream
- Create multiple Person objects
- Add Person objects to a collection
- Serialize collection to file
- Create deserialization stream
- Read objects from file into memory
Person Class
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 2L;
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Test Class
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.io.IOException;
public class CollectionSerializationDemo {
public static void main(String[] args) throws Exception {
// Serialize multiple objects via collection
serialize();
// Deserialize
deserialize();
}
private static void deserialize() throws Exception {
ObjectInputStream inputStream = new ObjectInputStream(
new FileInputStream("objects/people.obj")
);
Object obj = inputStream.readObject();
ArrayList<Person> people = (ArrayList<Person>) obj;
inputStream.close();
for (Person person : people) {
System.out.println(person.getName() + ", " + person.getAge());
}
}
private static void serialize() throws Exception {
ObjectOutputStream outputStream = new ObjectOutputStream(
new FileOutputStream("objects/people.obj")
);
ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 28));
outputStream.writeObject(people);
outputStream.close();
}
}
Note: If serializing multiple objects, do not serialize them directly to the same file. Instead, serialize them within a collection to avoid deserialization issues.
4. Properties Collection
4.1 Using Properties as a Map
Overview Properties is a Map-based collection class. It can be saved to or loaded from streams. Each key-value pair consists of strings.
Basic Usage
import java.util.Properties;
public class PropertiesBasicDemo {
public static void main(String[] args) {
Properties properties = new Properties();
properties.put("user001", "Alice");
properties.put("user002", "Bob");
properties.put("user003", "Charlie");
for (Object key : properties.keySet()) {
Object value = properties.get(key);
System.out.println(key + ", " + value);
}
}
}
4.2 Special Properties Methods
Unique Methods
| Method | Description |
|---|---|
| Object setProperty(String key, String value) | Sets key-value pairs as strings, uses Hashtable put internally |
| String getProperty(String key) | Searches property by key |
| Set<String> stringPropertyNames() | Returns unmodifiable key set with string values |
Code Example
import java.util.Properties;
import java.util.Set;
public class PropertiesMethodsDemo {
public static void main(String[] args) {
Properties properties = new Properties();
properties.setProperty("user001", "Alice");
properties.setProperty("user002", "Bob");
properties.setProperty("user003", "Charlie");
// Get property
// System.out.println(properties.getProperty("user001"));
// Get all keys
Set<String> keys = properties.stringPropertyNames();
for (String key : keys) {
String value = properties.getProperty(key);
System.out.println(key + ", " + value);
}
}
}
4.3 Properties Combined with I/O Streams
I/O Methods
| Method | Description |
|---|---|
| void load(Reader reader) | Reads property list from input character stream |
| void store(Writer writer, String comments) | Writes property list to output character stream |
Code Example
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
public class PropertiesIODemo {
public static void main(String[] args) throws IOException {
// Save to file
saveToFile();
// Load from file
loadFromFile();
}
private static void loadFromFile() throws IOException {
Properties properties = new Properties();
FileReader reader = new FileReader("config/app.properties");
properties.load(reader);
reader.close();
System.out.println(properties);
}
private static void saveToFile() throws IOException {
Properties properties = new Properties();
properties.setProperty("user001", "Alice");
properties.setProperty("user002", "Bob");
properties.setProperty("user003", "Charlie");
FileWriter writer = new FileWriter("config/app.properties");
properties.store(writer, null);
writer.close();
}
}
4.4 Properties Collection Practice
Requirements Manually write name and age in a properties file, read into collection, encapsulate into Person object, and write to local file.
Implementation Steps
- Create Properties collection and load data from local file
- Extract key-value pairs and encapsulate into Person object
- Create serialization stream and write Person object to local file
Person Class
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + '\'' + ", age=" + age + '}';
}
}
Test Class
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.util.Properties;
public class PropertiesPersonDemo {
public static void main(String[] args) throws IOException {
// Load properties from file
Properties properties = new Properties();
FileReader reader = new FileReader("config/person.properties");
properties.load(reader);
reader.close();
// Get values and create Person object
String name = properties.getProperty("name");
int age = Integer.parseInt(properties.getProperty("age"));
Person person = new Person(name, age);
// Serialize Person object
ObjectOutputStream outputStream = new ObjectOutputStream(
new FileOutputStream("objects/person.obj")
);
outputStream.writeObject(person);
outputStream.close();
}
}