一、什么是IO流
Java IO流(Input/Output Streams)是Java中用于处理数据读写操作的一组类和接口,它们提供了以流的形式进行输入和输出的功能。IO流的核心是将数据视为连续的字节流,既可以从源(如文件、网络连接)中读取数据,也可以将数据写入目标(如文件、控制台)。
IO流分为两大类:
- 字节流(Byte Streams):处理二进制数据,通过InputStream和OutputStream类及其子类来实现,适合处理所有类型的数据(如图像、视频)。
- 常见的子类包括:FileInputStream、FileOutputStream。
- 字符流(Character Streams):处理文本数据,通过Reader和Writer类及其子类来实现,专门用于处理字符数据,适合处理文本文件。
- 常见的子类包括:FileReader、FileWriter。
此外,Java IO还提供了缓冲流(如BufferedReader、BufferedWriter),可以提高读写性能,以及对象流(如ObjectInputStream、ObjectOutputStream),用于序列化对象。
二、什么是Java File类
Java 中的 File 类是 java.io 包的一部分,用于表示文件和目录路径名。它提供了多种方法,允许你对文件和目录进行操作,比如创建、删除、重命名、检查属性等。
File 类本身并不直接处理文件内容的读写(那是 IO 流的工作),它主要用来操作文件的元数据和路径名。
以下是 File 类的一些常用功能:
- 创建文件或目录:你可以使用 createNewFile() 创建新文件,或者用 mkdir() 创建新目录。
- 检查文件状态:例如,使用 exists() 检查文件是否存在,isDirectory() 判断是否是目录,isFile() 判断是否是文件。
- 获取文件信息:可以使用 getName() 获取文件名,length() 获取文件大小,getAbsolutePath() 获取文件的绝对路径。
- 修改文件:通过 delete() 删除文件,renameTo() 重命名文件。
- 目录操作:使用 list() 或 listFiles() 可以列出目录下的文件或子目录。
示例代码:
File file = new File("example.txt");
if (!file.exists()) {
file.createNewFile(); // 创建新文件
}
System.out.println("文件名: " + file.getName());
System.out.println("文件路径: " + file.getAbsolutePath());
System.out.println("文件大小: " + file.length() + " 字节");
总之,File 类是管理文件和目录的工具,虽然不直接负责文件内容的读写,但可以用于执行文件和目录的创建、删除、重命名等操作。
三、IO流分类
Java IO流是处理数据输入和输出的强大工具,分为字节流和字符流两大类。每个类都有具体用途,适合不同类型的数据处理需求。下面是Java IO流的全览,包括常用的类及其功能:
1. 字节流(Byte Streams)
字节流用于处理二进制数据,如图像、音频、视频等。主要操作单个字节或字节数组。
输入流(InputStream)
- InputStream:所有字节输入流的抽象基类。
- FileInputStream:从文件中读取字节数据。
- ByteArrayInputStream:从字节数组中读取数据。
- BufferedInputStream:对其他输入流进行缓冲,提高读取效率。
- DataInputStream:可以读取Java的基本数据类型(int、char、float等)。
- ObjectInputStream:用于读取对象(反序列化)。
- PipedInputStream:用于线程间通信的管道输入流。
- FilterInputStream:为其他输入流提供附加功能的基类。
输出流(OutputStream)
- OutputStream:所有字节输出流的抽象基类。
- FileOutputStream:向文件写入字节数据。
- ByteArrayOutputStream:将数据写入字节数组。
- BufferedOutputStream:对其他输出流进行缓冲,提高写入效率。
- DataOutputStream:写出Java的基本数据类型。
- ObjectOutputStream:用于写出对象(序列化)。
- PipedOutputStream:用于线程间通信的管道输出流。
- FilterOutputStream:为其他输出流提供附加功能的基类。
2. 字符流(Character Streams)
字符流用于处理文本数据,以字符(而非字节)的方式进行读写,适合处理文本文件。
输入流(Reader)
- Reader:所有字符输入流的抽象基类。
- FileReader:从文件中读取字符数据。
- BufferedReader:对其他字符输入流进行缓冲,提供高效读取,并支持按行读取(readLine())。
- InputStreamReader:将字节输入流转换为字符输入流(通常用于处理不同编码)。
- CharArrayReader:从字符数组中读取数据。
- StringReader:从字符串中读取字符数据。
- PipedReader:用于线程间通信的字符输入流。
- FilterReader:为其他字符输入流提供附加功能的基类。
输出流(Writer)
- Writer:所有字符输出流的抽象基类。
- FileWriter:向文件写入字符数据。
- BufferedWriter:对其他字符输出流进行缓冲,提高写入效率,并支持换行(newLine())。
- OutputStreamWriter:将字节输出流转换为字符输出流。
- CharArrayWriter:将数据写入字符数组。
- StringWriter:将数据写入字符串缓冲区。
- PipedWriter:用于线程间通信的字符输出流。
- FilterWriter:为其他字符输出流提供附加功能的基类。
3. 特殊IO流
这些流不属于纯粹的字节或字符流,但提供额外的功能。
- PrintStream:提供了方便的打印方法(如System.out),可以打印Java的基本数据类型和对象,且不会抛出IOException。
- PrintWriter:类似于PrintStream,但用于字符流,支持自动换行和格式化输出。
- DataInputStream 和 DataOutputStream:用于以二进制形式读取和写入Java的基本数据类型。
- ObjectInputStream 和 ObjectOutputStream:用于对象的序列化和反序列化操作(持久化对象)。
4. 缓冲流(Buffered Streams)
缓冲流是对输入输出流的增强,提供内部缓冲区以提高读写性能。
- BufferedInputStream 和 BufferedOutputStream:字节流的缓冲版本。
- BufferedReader 和 BufferedWriter:字符流的缓冲版本,支持按行读取和写入。
5. 管道流(Piped Streams)
用于线程之间的数据通信。
- PipedInputStream 和 PipedOutputStream:字节管道流,允许一个线程向管道写入,另一个线程从管道读取。
- PipedReader 和 PipedWriter:字符管道流,类似字节管道流,但用于字符数据。
6. 对象流(Object Streams)
对象流用于序列化和反序列化对象,将对象转换为字节流,以便存储或通过网络传输。
- ObjectInputStream 和 ObjectOutputStream:分别用于读取和写出对象。
7. 过滤流(Filter Streams)
过滤流提供了一种装饰器模式,可以为已有的流添加功能,如缓冲、压缩等。
- FilterInputStream 和 FilterOutputStream:字节过滤流的基类,其他过滤流继承自它们。
- FilterReader 和 FilterWriter:字符过滤流的基类。
8. 推回流(Pushback Streams)
提供了一种"推回"的功能,可以将读取的数据放回流中。
- PushbackInputStream:字节推回输入流。
- PushbackReader:字符推回输入流。
9. 序列流(SequenceInputStream)
- SequenceInputStream:将多个输入流合并成一个连续的输入流。
10. ZIP流(压缩流)
用于处理压缩文件,如ZIP格式。
- ZipInputStream 和 ZipOutputStream:处理ZIP压缩文件。
- GZIPInputStream 和 GZIPOutputStream:处理GZIP压缩文件。
总结:
Java IO流提供了广泛的工具集,处理从基础的字节和字符操作到复杂的对象序列化、压缩以及多线程管道通信。通过选择合适的IO流类,可以灵活、高效地处理各种数据输入输出场景。
四、IO流线程安全实现
在Java中,IO流的线程安全实现可以通过几种方式来确保多个线程安全地访问和操作文件或数据流。以下是几种常见的线程安全实现策略和方法:
1. 同步(Synchronized)
使用同步机制可以确保多个线程在同一时间只能有一个线程访问某个资源。这种方法可以通过 synchronized 关键字来实现:
public class ThreadSafeFileWriter {
private final File file;
public ThreadSafeFileWriter(File file) {
this.file = file;
}
public synchronized void write(String data) throws IOException {
try (FileWriter writer = new FileWriter(file, true)) {
writer.write(data);
}
}
}
在这个例子中,write 方法被 synchronized 修饰,确保每次只有一个线程能够执行文件写操作。
2. 使用线程安全的类
某些Java类和库已经内建了线程安全特性。例如:
- ConcurrentLinkedQueue:对于需要线程安全的队列操作,可以使用 ConcurrentLinkedQueue。
- CopyOnWriteArrayList 和 CopyOnWriteArraySet:对于线程安全的集合操作,可以使用 CopyOnWriteArrayList 或 CopyOnWriteArraySet。
3. 使用 java.util.concurrent包
java.util.concurrent 包提供了许多线程安全的工具和数据结构,可以在多线程环境中安全地进行读写操作。例如:
- BlockingQueue:ArrayBlockingQueue 或 LinkedBlockingQueue 可以用作线程安全的数据缓冲区。
- ReentrantLock:提供比 synchronized 更灵活的锁机制,可以进行更细粒度的同步控制。
import java.io.*;
import java.util.concurrent.locks.ReentrantLock;
public class SafeFileWriter {
private final File file;
private final ReentrantLock lock = new ReentrantLock();
public SafeFileWriter(File file) {
this.file = file;
}
public void write(String data) throws IOException {
lock.lock();
try (FileWriter writer = new FileWriter(file, true)) {
writer.write(data);
} finally {
lock.unlock();
}
}
}
4. 使用 BufferedOutputStream/ BufferedWriter
这些类提供了缓冲机制,可以减少对磁盘的直接读写操作。在多线程环境中,它们可以减少线程间的竞争,但仍然需要适当的同步:
import java.io.*;
import java.util.concurrent.locks.ReentrantLock;
public class BufferedSafeFileWriter {
private final File file;
private final ReentrantLock lock = new ReentrantLock();
public BufferedSafeFileWriter(File file) {
this.file = file;
}
public void write(String data) throws IOException {
lock.lock();
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, true))) {
writer.write(data);
} finally {
lock.unlock();
}
}
}
5. 文件锁
文件锁机制可以通过 FileChannel 和 FileLock 来实现,确保文件在某个线程中被独占访问:
import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
public class FileLockWriter {
private final File file;
public FileLockWriter(File file) {
this.file = file;
}
public void write(String data) throws IOException {
try (FileChannel channel = new RandomAccessFile(file, "rw").getChannel()) {
FileLock lock = null;
try {
lock = channel.lock(); // 阻塞直到获取文件锁
try (BufferedWriter writer = new BufferedWriter(new FileWriter(file, true))) {
writer.write(data);
}
} catch (OverlappingFileLockException e) {
// 处理锁重叠异常
} finally {
if (lock != null) {
lock.release(); // 释放文件锁
}
}
}
}
}
总结
确保IO流的线程安全可以通过多种方式实现,包括同步、使用线程安全的数据结构、利用java.util.concurrent包中的工具、使用缓冲流、以及使用文件锁等。具体选择取决于应用场景、性能需求和线程安全的需求。
五、IO流实战
在 Java 中,IO 流(输入输出流)是处理数据读写的核心机制,主要用于读取和写入文件、网络通信、内存数据流等。Java 提供了多种类来处理不同形式的 IO,如字节流、字符流、缓冲流等。
常见的 IO 流分类
- 字节流(Byte Stream):
- InputStream 和 OutputStream 是字节流的抽象父类,分别用于输入和输出。
- 适用于处理原始的二进制数据,如图片、音频、视频等。
- 字符流(Character Stream):
- Reader 和 Writer 是字符流的抽象父类,分别用于读取和写入文本数据。
- 适用于处理文本文件(如 .txt、.csv 等)。
- 缓冲流(Buffered Stream):
- 包括 BufferedInputStream、BufferedOutputStream、BufferedReader 和 BufferedWriter,提供缓冲功能,提高读写效率。
代码实战
1. 使用 FileInputStream和 FileOutputStream进行字节流操作
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamExample {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("input.txt");
FileOutputStream fos = new FileOutputStream("output.txt")) {
int byteData;
// 一次读取一个字节
while ((byteData = fis.read()) != -1) {
fos.write(byteData); // 一次写入一个字节
}
System.out.println("字节流文件复制成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. 使用 BufferedReader和 BufferedWriter进行字符流操作
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CharacterStreamExample {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
String line;
// 一次读取一行
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine(); // 添加换行符
}
System.out.println("字符流文件复制成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 使用 BufferedInputStream和 BufferedOutputStream进行高效的字节流操作
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedByteStreamExample {
public static void main(String[] args) {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("output.txt"))) {
byte[] buffer = new byte[1024];
int bytesRead;
// 一次读取多个字节
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead); // 一次写入多个字节
}
System.out.println("缓冲字节流文件复制成功!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
IO 流使用注意事项
- 关闭流:在使用 IO 流时,应该确保在操作完成后关闭流,防止资源泄漏。Java 7 引入了 try-with-resources 语法,自动关闭流。
- 缓冲流提高效率:直接使用 FileInputStream 或 FileOutputStream 处理大量数据时效率较低,建议使用缓冲流来提高读写性能。
- 字符编码:在使用字符流时,要注意指定正确的字符编码(如 UTF-8),避免因编码不一致导致读取或写入时出现乱码。
通过这些例子,IO 流的基本操作已经涵盖了常见的场景,如文件复制、文本处理等。