【JAVA】I/O 基本操作

Java中的I/O流(输入/输出流)是处理输入和输出数据的机制。I/O流提供了通过数据源(如文件、网络、内存等)读取和写入数据的标准方法。Java的I/O流分为两大类:字节流(Byte Stream)和字符流(Character Stream)。字节流处理原始的二进制数据,而字符流则处理文本数据。以下是详细介绍和示例。

一、字节流(Byte Stream)

字节流用于处理8位字节的数据。它们适合于任何类型的I/O操作,如读写图像、音频等。字节流包括两个基本的抽象类:

  • InputStream:用于读取字节的抽象类。
  • OutputStream:用于写入字节的抽象类。

1.1 FileInputStream 和 FileOutputStream

FileInputStream用于从文件中读取字节数据,而FileOutputStream用于将字节数据写入文件。

示例:将文件内容从一个文件复制到另一个文件

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamExample {
    public static void main(String[] args) {
        // 输入文件路径和输出文件路径
        String inputFile = "input.txt";
        String outputFile = "output.txt";

        // 声明FileInputStream和FileOutputStream对象
        FileInputStream fis = null;
        FileOutputStream fos = null;

        try {
            // 初始化FileInputStream对象用于读取文件
            fis = new FileInputStream(inputFile);
            // 初始化FileOutputStream对象用于写入文件
            fos = new FileOutputStream(outputFile);

            // 读取的字节数据
            int byteData;

            // 循环读取文件内容,直到文件结束
            while ((byteData = fis.read()) != -1) {
                // 将读取到的字节数据写入输出文件
                fos.write(byteData);
            }

            System.out.println("文件复制完成!");
        } catch (IOException e) {
            // 捕获并处理IO异常
            e.printStackTrace();
        } finally {
            try {
                // 关闭输入流
                if (fis != null) {
                    fis.close();
                }
                // 关闭输出流
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                // 捕获并处理关闭流时的IO异常
                e.printStackTrace();
            }
        }
    }
}

二、字符流(Character Stream)

字符流用于处理16位的Unicode字符。它们适合处理文本文件。字符流也包括两个基本的抽象类:

  • Reader:用于读取字符的抽象类。
  • Writer:用于写入字符的抽象类。

2.1 FileReader 和 FileWriter

FileReader用于从文件中读取字符数据,而FileWriter用于将字符数据写入文件。

示例:将文件内容从一个文件复制到另一个文件

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CharStreamExample {
    public static void main(String[] args) {
        // 输入文件路径和输出文件路径
        String inputFile = "input.txt";
        String outputFile = "output.txt";

        // 声明FileReader和FileWriter对象
        FileReader fr = null;
        FileWriter fw = null;

        try {
            // 初始化FileReader对象用于读取文件
            fr = new FileReader(inputFile);
            // 初始化FileWriter对象用于写入文件
            fw = new FileWriter(outputFile);

            // 读取的字符数据
            int charData;

            // 循环读取文件内容,直到文件结束
            while ((charData = fr.read()) != -1) {
                // 将读取到的字符数据写入输出文件
                fw.write(charData);
            }

            System.out.println("文件复制完成!");
        } catch (IOException e) {
            // 捕获并处理IO异常
            e.printStackTrace();
        } finally {
            try {
                // 关闭读取流
                if (fr != null) {
                    fr.close();
                }
                // 关闭写入流
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                // 捕获并处理关闭流时的IO异常
                e.printStackTrace();
            }
        }
    }
}

三、缓冲流(Buffered Stream)

缓冲流提供了对原始流的包装,使得读写操作更为高效。缓冲流通常包括:

  • BufferedInputStream 和 BufferedOutputStream:为字节流提供缓冲功能。
  • BufferedReader 和 BufferedWriter:为字符流提供缓冲功能。

示例:使用缓冲流进行文件复制

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedStreamExample {
    public static void main(String[] args) {
        // 输入文件路径和输出文件路径
        String inputFile = "input.txt";
        String outputFile = "output.txt";

        // 声明BufferedReader和BufferedWriter对象
        BufferedReader br = null;
        BufferedWriter bw = null;

        try {
            // 初始化BufferedReader对象用于读取文件
            br = new BufferedReader(new FileReader(inputFile));
            // 初始化BufferedWriter对象用于写入文件
            bw = new BufferedWriter(new FileWriter(outputFile));

            // 读取的文本行数据
            String line;

            // 循环读取文件内容,直到文件结束
            while ((line = br.readLine()) != null) {
                // 将读取到的文本行写入输出文件
                bw.write(line);
                // 换行符
                bw.newLine();
            }

            System.out.println("文件复制完成!");
        } catch (IOException e) {
            // 捕获并处理IO异常
            e.printStackTrace();
        } finally {
            try {
                // 关闭读取流
                if (br != null) {
                    br.close();
                }
                // 关闭写入流
                if (bw != null) {
                    bw.close();
                }
            } catch (IOException e) {
                // 捕获并处理关闭流时的IO异常
                e.printStackTrace();
            }
        }
    }
}

四、标准I/O

Java还提供了对标准输入(System.in)、标准输出(System.out)、和标准错误(System.err)的支持,通常用于命令行操作。

示例:从标准输入读取数据并打印到标准输出

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class StandardIOExample {
    public static void main(String[] args) {
        // 使用BufferedReader和InputStreamReader从标准输入读取数据
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        try {
            System.out.println("请输入一些文字:");

            // 读取用户输入的文本行
            String input = reader.readLine();

            // 打印输入的文本行到标准输出
            System.out.println("您输入的是:" + input);
        } catch (IOException e) {
            // 捕获并处理IO异常
            e.printStackTrace();
        } finally {
            try {
                // 关闭读取流
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                // 捕获并处理关闭流时的IO异常
                e.printStackTrace();
            }
        }
    }
}

五、对象流(Object Stream)

对象流用于读写Java对象,它可以将对象写入流中或从流中读取对象。对象流包括:

  • ObjectInputStream:用于反序列化对象(将流中的字节转换为对象)。
  • ObjectOutputStream:用于序列化对象(将对象转换为字节并写入流中)。

对象流常用于保存对象的状态到文件或通过网络传输对象。

5.1 ObjectOutputStream 和 ObjectInputStream

这些类可以将一个对象写入文件(序列化),或从文件中读取对象(反序列化)。

示例:将对象保存到文件并从文件中读取对象

import java.io.*;

// 创建一个可序列化的简单类
class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

public class ObjectStreamExample {
    public static void main(String[] args) {
        // 定义一个Person对象
        Person person = new Person("Alice", 30);

        // 文件路径
        String filename = "person.dat";

        // 序列化:将对象写入文件
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename))) {
            // 将Person对象写入文件
            oos.writeObject(person);
            System.out.println("对象已序列化并保存到文件:" + filename);
        } catch (IOException e) {
            // 捕获并处理IO异常
            e.printStackTrace();
        }

        // 反序列化:从文件读取对象
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename))) {
            // 从文件中读取Person对象
            Person savedPerson = (Person) ois.readObject();
            System.out.println("从文件中读取的对象:" + savedPerson);
        } catch (IOException | ClassNotFoundException e) {
            // 捕获并处理IO异常和类未找到异常
            e.printStackTrace();
        }
    }
}

5.2 注意事项

对象的可序列化性:类必须实现Serializable接口,否则将抛出NotSerializableException异常。

  • serialVersionUID:用于确保反序列化时类的一致性。如果类定义发生改变且未指定serialVersionUID,可能会导致反序列化失败。

六、Java NIO(New I/O)

Java NIO (New I/O) 是Java 1.4引入的一个高级I/O API,它与传统的I/O流相比提供了更高的效率和更强的功能。NIO主要通过缓冲区(Buffer)、**通道(Channel)和选择器(Selector)**等核心概念来实现非阻塞的I/O操作。

6.1 缓冲区(Buffer)

缓冲区是一个用于读写数据的容器。与传统I/O中的直接读写操作不同,NIO通过缓冲区进行数据交换。缓冲区可以理解为一个数组,但它除了数据存储功能外,还提供了对数据读写操作的管理,如位置、限制、容量等。

示例:使用ByteBuffer操作数据

import java.nio.ByteBuffer;

public class BufferExample {
    public static void main(String[] args) {
        // 分配一个容量为10字节的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(10);

        // 写入数据到缓冲区
        for (int i = 0; i < buffer.capacity(); i++) {
            buffer.put((byte) i);
        }

        // 切换为读取模式
        buffer.flip();

        // 读取数据并打印
        while (buffer.hasRemaining()) {
            System.out.print(buffer.get() + " ");
        }
    }
}

关键点解释:

  • allocate(int capacity):分配一个指定容量的缓冲区。
  • put(byte b):将数据写入缓冲区。
  • flip():切换缓冲区为读取模式。
  • get():从缓冲区读取数据。

6.2 通道(Channel)

通道类似于传统I/O中的流,但它可以双向操作,即既可以读也可以写。常见的通道包括FileChannel、SocketChannel、ServerSocketChannel等。通道通常与缓冲区结合使用,以实现高效的数据传输。

示例:使用FileChannel复制文件

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ChannelExample {
    public static void main(String[] args) {
        try (
            // 创建文件输入流和输出流
            FileInputStream fis = new FileInputStream("input.txt");
            FileOutputStream fos = new FileOutputStream("output.txt");

            // 获取输入和输出通道
            FileChannel inputChannel = fis.getChannel();
            FileChannel outputChannel = fos.getChannel()
        ) {
            // 分配缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            // 读取数据到缓冲区
            while (inputChannel.read(buffer) != -1) {
                // 切换缓冲区为读取模式
                buffer.flip();

                // 将数据从缓冲区写入输出通道
                outputChannel.write(buffer);

                // 清空缓冲区以准备下一次读操作
                buffer.clear();
            }

            System.out.println("文件复制完成!");
        } catch (Exception e) {
            // 捕获并处理异常
            e.printStackTrace();
        }
    }
}

关键点解释:

  • getChannel():从文件输入流/输出流中获取通道。
  • read(ByteBuffer buffer):将数据从通道读入缓冲区。
  • write(ByteBuffer buffer):将缓冲区的数据写入通道。
  • clear():清空缓冲区,以准备下次读取数据。

6.3 选择器(Selector)

选择器是NIO中的核心组件之一,允许单线程监控多个通道的事件(如读、写、连接等),从而实现非阻塞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;

public class SelectorExample {
    public static void main(String[] args) throws IOException {
        // 打开ServerSocketChannel和Selector
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        Selector selector = Selector.open();

        // 绑定服务器端口并设置为非阻塞模式
        serverChannel.bind(new InetSocketAddress(8080));
        serverChannel.configureBlocking(false);

        // 注册通道到选择器,并关注接受连接事件
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("服务器启动,等待连接...");

        while (true) {
            // 选择已准备好的通道
            selector.select();

            // 获取选择的键集合
            Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();

            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();

                // 检查是否有新的连接
                if (key.isAcceptable()) {
                    // 接受新的连接
                    SocketChannel clientChannel = serverChannel.accept();
                    clientChannel.configureBlocking(false);

                    // 注册新连接的通道到选择器,并关注读事件
                    clientChannel.register(selector, SelectionKey.OP_READ);

                    System.out.println("新的连接已接受:" + clientChannel.getRemoteAddress());
                } else if (key.isReadable()) {
                    // 读取客户端数据
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(256);
                    clientChannel.read(buffer);
                    String request = new String(buffer.array()).trim();
                    System.out.println("收到消息:" + request);

                    // 响应客户端
                    buffer.flip();
                    clientChannel.write(ByteBuffer.wrap("响应消息".getBytes()));

                    // 关闭连接
                    clientChannel.close();
                }

                // 移除已处理的键
                keyIterator.remove();
            }
        }
    }
}

关键点解释:

  • open():打开通道或选择器。
  • configureBlocking(false):设置通道为非阻塞模式。
  • register(Selector selector, int ops):将通道注册到选择器并指定要监听的事件类型。
  • select():选择已经准备好的通道。
  • selectedKeys():获取已选择的键集合,代表通道已准备好的操作。

七、小结

Java的I/O流提供了灵活的读写数据方式,适用于各种类型的输入输出操作。字节流用于处理二进制数据,字符流则适合处理文本数据,而缓冲流则提高了I/O操作的效率。在实际开发中,根据具体需求选择合适的流类型,可以显著提高程序的性能和可靠性。

  • 21
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值