Java 基础之常见的输入输出流以及基本使用


共计 6966 个字符,预计需要花费 18 分钟才能阅读完成。

介绍

流是用了读取和写出数据的对象,下面是常见的一些对象即使用方法。

1. 字节流

  • InputStream:用于从字节流中读取数据。

    InputStream 为抽象类,常见的继承类:FileInputStream、BufferedInputStream、DataInputStream、ObjectInputStream。

  • OutputStream:用于向字节流中写入数据。

    OutputStream 同样为抽象类,常见继承类:FileOutputStream、BufferedOutputStream、DataOutputStream、ObjectOutputStream

2. 字符流

  • Reader:用于从字符流中读取数据。

    Reader 也为抽象类,常见的继承类:FileReader、InputStreamReader、BufferedReader、StringReader。

  • Writer:用于向字符流中写入数据。

    Writer 为抽象类,常见的继承类:FileWriter、OutputStreamWriter、BufferedWriter、StringWriter。

3. 缓冲流

缓冲流内置了缓冲区,将磁盘内容先读取到缓冲区即内存中,一般默认大小为 8K,获取字节或字符时直接从内存中取出,减少了和磁盘打交道的次数。

  • BufferedInputStream:提供了缓冲功能的字节输入流。

  • BufferedOutputStream:提供了缓冲功能的字节输出流。

  • BufferedReader:提供了缓冲功能的字符输入流。

  • BufferedWriter:提供了缓冲功能的字符输出流。

4. 文件流

  • FileInputStream:用于从文件中读取数据的字节流。

  • FileOutputStream:用于向文件中写入数据的字节流。

  • FileReader:用于从文件中读取数据的字符流。

  • FileWriter:用于向文件中写入数据的字符流。

5. 转换流

  • InputStreamReader:将字节流转换为字符流的转换流。

  • OutputStreamWriter:将字符流转换为字节流的转换流。

6. 数据流

支持读写基本数据类型和字符串,但是不支持读写对象类型。

  • DataInputStream:用于读取基本数据类型和字符串的流。

  • DataOutputStream:用于写入基本数据类型和字符串的流。

7. 对象流

支持基本类型和字符串的读写,同时也从支持对实现了Serializable 接口的对象进行序列化和反序列化。

  • ObjectInputStream:用于读取对象的流。

  • ObjectOutputStream:用于写入对象的流。

基本使用

以下内容是对上面各种输入输出流的一些测试方法:

public class IOTest {

    /**
     * 字节流 InputStream OutputStream
     * 1. 利用输入流,读取 file.txt, 将字符打印在控制台
     * 2. 利用输出流,复制 file.txt 里面的内容,追加到 file_copy.txt
     */
    @Test
    public void test1() throws IOException {
        String inputFile = "file/io/file.txt";
        String outPutFile = "file/io/file_copy.txt";
        initFileTxt(inputFile);  // 初始化文件内容为:你好啊amjun
        // try-with-resources 语句
        try (InputStream inputStream = new FileInputStream(inputFile);
             OutputStream outputStream = new FileOutputStream(outPutFile);) {
            int byteData;
            while ((byteData = inputStream.read()) != -1) {
                // byteData 是读取到的单个字节,因为这种读取方式是单个字符读取的
                // 打印中文时会乱码,因为中文在utf-8中是三个字节为一个字符
                System.out.println(byteData + "  " + (char)byteData);
                outputStream.write(byteData);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 使用缓冲区进行数据读取
     * 1. 创建一个字节数组作为缓冲区
     * 2. 输入流读取字节时,将字节读入缓冲区
     * 3. 打印时,通过缓冲区的内容创建一个字符串
     * 4. 写入文件时,将缓冲区的内容一次性写入
     */
    @Test
    public void test2(){
        String inputFile = "file/io/file.txt";
        String outPutFile = "file/io/file_buffer_copy.txt";
        initFileTxt(inputFile); // 初始化文件内容为:你好啊amjun
        try (InputStream inputStream = new FileInputStream(inputFile);
             OutputStream outputStream = new FileOutputStream(outPutFile);) {
            // 创建临时字节数组缓冲区
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                // bytesRead 是读取到的字节数,当字节少于1024时,一次性全部读取并打印,所以打印不会乱码
                System.out.print(new String(buffer, 0 , bytesRead));
                // 字节数为 14,因为3中文+5英文,中文3个字节1个字符,英文1个字节1个字符
                System.out.println("\n读取到的字节数:" + bytesRead);
                outputStream.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 字节缓冲流 BufferedInputStream BufferedOutputStream
     * 引入了缓冲区,缓冲区的存在可以减少和磁盘之间的交互次数,提高读写效率。有需要可以打印时间进行测试
     * 缓冲区会一次读取一定量的数据,然后在内存中进行操作,直到缓冲区被填满或者操作完成后再写回磁盘。
     */
    @Test
    public void test3(){
        String inputFile = "file/io/file.txt";
        String outPutFile = "file/io/file_buffer2_copy.txt";
        initFileTxt(inputFile); // 初始化文件内容为:你好啊amjun
        try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(inputFile));
             BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(outPutFile))) {
            // 创建临时字节数组缓冲区
            int bytesRead;
            while ((bytesRead = bufferedInputStream.read()) != -1) {
                // 该乱码还是乱码,因为即使内部实现了缓冲区,但是读取的字节还是一个个读取出来的,但是字节的读取是在内存中读的,而不是在磁盘中
                System.out.println(bytesRead + "  " + (char)bytesRead);
                bufferedOutputStream.write(bytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 字符流转换流 InputStreamReader OutputStreamWriter
     * 1. 先将文件读取为字节流,然后用字符流包装,输出流文件也是如此
     * 2. 创建一个字符数组作为缓冲区
     * 3. 将文件读取到字符缓冲区
     * 4. 将字符缓冲区的内容打印和写入
     */
    @Test
    public void test4(){
        String inputFile = "file/io/file.txt";
        String outPutFile = "file/io/file_buffer_copy.txt";
        initFileTxt(inputFile); // 初始化文件内容为:你好啊amjun
        try (InputStreamReader reader = new InputStreamReader(new FileInputStream(inputFile));
             OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(outPutFile));) {
            // 创建临时字符数组缓冲区
            char[] buffer = new char[1024];
            int charsRead;
            while ((charsRead = reader.read(buffer)) != -1) {
                // 将读取到的字符写入控制台,由于字符数小于1024,会一次性全部输出
                System.out.print(new String(buffer, 0, charsRead));
                // 3中文字符+5英文字符
                System.out.println("\n读取到的字符数:" + charsRead);
                // 将读取的数据追加到目标文件中
                writer.write(buffer, 0, charsRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 字符缓冲流 BufferedReader BufferedWriter
     */
    @Test
    public void test5(){
        String inputFile = "file/io/file.txt";
        String outPutFile = "file/io/file_buffer_copy.txt";
        initFileTxt(inputFile); // 初始化文件内容为:你好啊amjun
        // 装饰器模式
        try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(inputFile)));
             BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outPutFile)))) {
            // 创建临时字符数组缓冲区
            char[] buffer = new char[1024];
            int charsRead;
            while ((charsRead = bufferedReader.read(buffer)) != -1) {
                // 将读取到的字符写入控制台,由于字符数小于1024,会一次性全部输出
                System.out.print(new String(buffer, 0, charsRead));
                // 3中文字符+5英文字符
                System.out.println("\n读取到的字符数:" + charsRead);
                // 将读取的数据追加到目标文件中
                bufferedWriter.write(buffer, 0, charsRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     *  字符流 FileReader FileWriter
     */
    @Test
    public void test6() throws IOException {
        String inputFile = "file/io/file.txt";
        String outPutFile = "file/io/file_buffer_copy.txt";
        initFileTxt(inputFile); // 初始化文件内容为:你好啊amjun
        try (Reader reader = new FileReader(inputFile);
            Writer writer = new FileWriter(outPutFile);) {
            // 创建临时字符数组缓冲区
            char[] buffer = new char[1024];
            int charsRead;
            while ((charsRead = reader.read(buffer)) != -1) {
                // 将读取到的字符写入控制台,由于字符数小于1024,会一次性全部输出
                System.out.print(new String(buffer, 0, charsRead));
                // 3中文字符+5英文字符
                System.out.println("\n读取到的字符数:" + charsRead);
                // 将读取的数据追加到目标文件中
                writer.write(buffer, 0, charsRead);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 数据流 DataInputStream DataOutputStream
     * 只支持读写基本数据类型和字符串
     */
    @Test
    public void test7() {
        String obgFile = "file/io/data.bin";
        try (DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(obgFile))) {
            dataOutputStream.writeInt(19);
            System.out.println("写入基本数据类型成功...");
        } catch (Exception e) {
            e.printStackTrace();
        }

        try (DataInputStream dataInputStream = new DataInputStream(new FileInputStream(obgFile));) {
            int i = dataInputStream.readInt();
            System.out.println("读取基本类型数据:" +  i);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 对象流 ObjectInputStream ObjectOutputStream
     * 对象流不能使用字符流进行包装,因为O对象流是基于字节流的,用于写入对象的二进制表示。而字符流主要用于处理字符数据。
     */
    @Test
    public void test8() {
        // 序列化对象
        String obgFile = "file/io/obj.bin";
        try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(obgFile))) {
            User user = new User().setName("zhangsan").setAge(18);
            objectOutputStream.writeObject(user);
            System.out.println("写入对象成功...");
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 反序列化对象
        try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(obgFile));) {
            User user = (User)objectInputStream.readObject();
            System.out.println("读取的对象为:" + user);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 初始化文件内容
     */
    public void initFileTxt(String outputFile){
        try(OutputStream outputStream = new FileOutputStream(outputFile);
        OutputStreamWriter writer = new OutputStreamWriter(outputStream)){
            String str = "你好啊amjun";
            // 将字符串以字节数组方式写入文件
            //byte[] byteArray = str.getBytes("UTF-8");
            //outputStream.write(byteArray);

            // 将文件以字符数组写入文件
            char[] charArray = str.toCharArray();
            writer.write(charArray);
        }catch (IOException e){

        }
    }
}

@Data
@Accessors(chain = true)
class User implements Serializable{

    public String name;

    public int age;
}

提醒:本文发布于402天前,文中所关联的信息可能已发生改变,请知悉!

Tips:清朝云网络工作室

阅读剩余
THE END