引言
在Java编程中,输入和输出(I/O)操作是必不可少的部分。Java I/O通过一系列流(Stream)类和方法,支持文件操作、控制台输入输出、网络I/O等多种I/O操作。本文将详细介绍Java I/O的基础概念、文件操作、字符流与字节流的区别、序列化与反序列化以及新的I/O(NIO)等内容,并通过表格进行总结和示范。
Java I/O基础概念
流的概念
流(Stream)是指数据的流动,是一个顺序读写数据的抽象。Java定义了两种基本类型的流:
- 字节流:用于处理字节数据,最基础的类是
InputStream
和OutputStream
。 - 字符流:用于处理字符数据,最基础的类是
Reader
和Writer
。
流的分类
Java I/O流可以按照数据类型分类为字节流和字符流,也可以按照数据流向分为输入流和输出流。
类型 | 输入流 | 输出流 |
---|---|---|
字节流 | InputStream | OutputStream |
字符流 | Reader | Writer |
文件与目录操作
文件类(File)
File
类表示文件或目录,可以对其进行创建、删除、获取信息等操作。
import java.io.File;
import java.io.IOException;
public class FileExample {
public static void main(String[] args) {
// 创建一个File对象
File file = new File("example.txt");
try {
// 创建新文件
if (file.createNewFile()) {
System.out.println("File created: " + file.getName());
} else {
System.out.println("File already exists.");
}
// 获取文件信息
System.out.println("Absolute path: " + file.getAbsolutePath());
System.out.println("Writable: " + file.canWrite());
System.out.println("Readable: " + file.canRead());
System.out.println("File size in bytes: " + file.length());
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
}
}
}
文件与目录操作方法表
方法 | 描述 | 示例 |
---|---|---|
createNewFile() | 创建一个新的文件 | file.createNewFile(); |
delete() | 删除文件或目录 | file.delete(); |
exists() | 判断文件或目录是否存在 | file.exists(); |
getAbsolutePath() | 获取文件的绝对路径 | file.getAbsolutePath(); |
canWrite() | 判断文件是否可写 | file.canWrite(); |
canRead() | 判断文件是否可读 | file.canRead(); |
length() | 获取文件大小(字节) | file.length(); |
字符流和字节流
字节流
字节流用于处理字节数据,通过InputStream
和OutputStream
类及其子类进行输入输出操作。
示例:使用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);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符流
字符流用于处理字符数据,通过Reader
和Writer
类及其子类进行输入输出操作。
示例:使用FileReader
和FileWriter
进行文件字符操作
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamExample {
public static void main(String[] args) {
try (FileReader fr = new FileReader("input.txt");
FileWriter fw = new FileWriter("output.txt")) {
int charData;
// 逐字符读取数据
while ((charData = fr.read()) != -1) {
// 逐字符写入数据
fw.write(charData);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符流和字节流的比较表
特性 | 字节流 | 字符流 |
---|---|---|
基本类 | InputStream 和 OutputStream | Reader 和 Writer |
数据处理单位 | 字节(8位) | 字符(16位) |
适用场景 | 二进制数据(如图像、音频) | 文本数据(如文本文件) |
典型实现类 | FileInputStream , FileOutputStream | FileReader , FileWriter |
序列化与反序列化
序列化
序列化是将对象转换为字节流并写到文件或网络中的过程。实现Serializable
接口的类可以被序列化。
示例:对象序列化
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class Person implements Serializable {
private static final long serialVersionUID = 1L;
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class SerializationExample {
public static void main(String[] args) {
Person person = new Person("John", 30);
try (FileOutputStream fos = new FileOutputStream("person.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(person);
} catch (IOException e) {
e.printStackTrace();
}
}
}
反序列化
反序列化是将字节流转换为对象的过程。读取序列化文件时使用ObjectInputStream
。
示例:对象反序列化
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class DeserializationExample {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("person.ser");
ObjectInputStream ois = new ObjectInputStream(fis)) {
Person person = (Person) ois.readObject();
System.out.println("Name: " + person.name);
System.out.println("Age: " + person.age);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
序列化与反序列化的表格总结
概念 | 描述 | 示例 |
---|---|---|
序列化 | 将对象转换为字节流并写到文件或网络中 | ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file.ser")); oos.writeObject(object); |
反序列化 | 将字节流转换为对象 | ObjectInputStream ois = new ObjectInputStream(new FileInputStream("file.ser")); Object obj = ois.readObject(); |
必需接口 | 需要实现 Serializable 接口 | class Person implements Serializable { ... } |
序列化ID | 建议定义 serialVersionUID 字段应避免反序列化不兼容 | private static final long serialVersionUID = 1L; |
新的I/O (NIO)
Java的新的I/O(NIO)引入了多个新概念,如缓冲区、通道和选择器。这些概念提供了更高效的I/O处理,特别是在处理大数据和高性能网络应用方面。下面我们将详细介绍这些概念。
缓冲区(Buffer)
缓冲区是NIO数据操作的基础,用于存储和操作数据。Java NIO提供了多种类型的缓冲区,比如ByteBuffer
、CharBuffer
等。
示例:使用ByteBuffer
import java.nio.ByteBuffer;
public class BufferExample {
public static void main(String[] args) {
// 创建一个大小为10的字节缓冲区
ByteBuffer buffer = ByteBuffer.allocate(10);
// 写入数据到缓冲区
buffer.put((byte) 1);
buffer.put((byte) 2);
// 切换到读模式
buffer.flip();
// 读取缓冲区中的数据
while (buffer.hasRemaining()) {
byte data = buffer.get();
System.out.println(data);
}
}
}
通道(Channel)
通道是一个连接数据源和缓冲区的通道,数据可以通过通道从数据源读入缓冲区,或从缓冲区写入到数据源。常用的通道有FileChannel
、SocketChannel
、ServerSocketChannel
等。
示例:使用FileChannel
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class ChannelExample {
public static void main(String[] args) {
// 文件路径
Path path = Path.of("example.txt");
// 使用文件通道写入数据
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
ByteBuffer buffer = ByteBuffer.allocate(64);
buffer.put("Hello, NIO!".getBytes());
buffer.flip();
fileChannel.write(buffer);
} catch (IOException e) {
e.printStackTrace();
}
// 使用文件通道读取数据
try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(64);
fileChannel.read(buffer);
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
选择器(Selector)
选择器是NIO中的多路复用器,可以用于管理多个通道,特别适用于网络编程。Selector
与SelectableChannel
配合使用,可以高效地处理非阻塞I/O操作。
示例:使用Selector
进行非阻塞I/O
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class SelectorExample {
public static void main(String[] args) {
try {
// 打开选择器
Selector selector = Selector.open();
// 打开服务器套接字通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(8080));
serverSocketChannel.configureBlocking(false);
// 将通道注册到选择器,并监听接受事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 循环等待事件
while (true) {
selector.select();
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
// 接受新连接
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = serverChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
// 读取数据
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(256);
socketChannel.read(buffer);
buffer.flip();
System.out.println("Received: " + new String(buffer.array()).trim());
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
NIO关键概念表格总结
概念 | 描述 | 示例 |
---|---|---|
缓冲区(Buffer) | 用于存储数据的容器,可以是字节、字符、短整型等 | ByteBuffer buffer = ByteBuffer.allocate(10); |
通道(Channel) | 用于连接数据源与缓冲区,实现I/O操作 | FileChannel.open(path, StandardOpenOption.READ); |
选择器(Selector) | 用于管理多个通道,实现非阻塞I/O的多路复用 | Selector.open(); selector.select(); |
表格总结
文件与目录操作方法表
方法 | 描述 | 示例 |
---|---|---|
createNewFile() | 创建一个新的文件 | file.createNewFile(); |
delete() | 删除文件或目录 | file.delete(); |
exists() | 判断文件或目录是否存在 | file.exists(); |
getAbsolutePath() | 获取文件的绝对路径 | file.getAbsolutePath(); |
canWrite() | 判断文件是否可写 | file.canWrite(); |
canRead() | 判断文件是否可读 | file.canRead(); |
length() | 获取文件大小(字节) | file.length(); |
字符流和字节流的比较表
特性 | 字节流 | 字符流 |
---|---|---|
基本类 | InputStream 和 OutputStream | Reader 和 Writer |
数据处理单位 | 字节(8位) | 字符(16位) |
适用场景 | 二进制数据(如图像、音频) | 文本数据(如文本文件) |
典型实现类 | FileInputStream , FileOutputStream | FileReader , FileWriter |
序列化与反序列化的表格总结
概念 | 描述 | 示例 |
---|---|---|
序列化 | 将对象转换为字节流并写到文件或网络中 | ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("file.ser")); oos.writeObject(object); |
反序列化 | 将字节流转换为对象 | ObjectInputStream ois = new ObjectInputStream(new FileInputStream("file.ser")); Object obj = ois.readObject(); |
必需接口 | 需要实现 Serializable 接口 | class Person implements Serializable { ... } |
序列化ID | 建议定义 serialVersionUID 字段以避免反序列化不兼容 | private static final long serialVersionUID = 1L; |
NIO关键概念表格总结
概念 | 描述 | 示例 |
---|---|---|
缓冲区(Buffer) | 用于存储数据的容器,可以是字节、字符、短整型等 | ByteBuffer buffer = ByteBuffer.allocate(10); |
通道(Channel) | 用于连接数据源与缓冲区,实现I/O操作 | FileChannel.open(path, StandardOpenOption.READ); |
选择器(Selector) | 用于管理多个通道,实现非阻塞I/O的多路复用 | Selector.open(); selector.select(); |
总结
本文详细介绍了Java I/O操作,包括文件和目录操作、字符流与字节流的区别、序列化与反序列化、新的I/O(NIO)等内容。通过示例代码和表格总结,帮助您更好地理解和应用Java中的I/O操作,提高程序的读取和写入效率。