输入/输出(I/O)流

I/O流输入输出流,输入输出流又分为字符流和字节流。其中字节流适合所有文件的输入输出,字符流使用范围有限,通常用来处理文本的输入输出。并且用字符流处理文本时不会出现乱码,而用字节流可能会产生乱码。

字节流

字节流写入时是按照字节进行写入/读取的,一个字符可能由多个字节组成,比如一个汉字按照UTF-8编码时有三个字节,因此按照字节写入时有可能会出现乱码。

InputStream

inputStream是一个抽象类,包含多种实现类,FileInputStream就是最常用的一个实现类。

FileInputStream

文件输入流FileInputStream,从磁盘将内容输入到内存中,用来读取磁盘文件。

构造方法
FileInputStream(String name)//name为文件路径,可以是绝对路径也可以是相对路径
FileInputStream(File file)//通过文件对象创建输入流
FileInputStream(FileDescriptor fdObj)//不常用
常用函数
read()

读取一个字节,返回读取到的字节,如果读取完毕再读取时返回-1

   // 根据文件路径创建输入流
            InputStream is = new FileInputStream("D://test.txt");


            // 一个字节一个字节的读取
            int readed;//读取到的字节
            while((readed = is.read())!=-1){//将读取到的字节赋给readed,如果readed返回-1表示读取完毕
                System.out.printf(String.valueOf((char)readed));
            }

单个字节读取时,如果有中文读取到的字节会变成乱码。如果采用UTF-8编码,一个中文有三个字节,分开读取时就会变成乱码。如果采用的是GBK编码,一个汉字有两个字节,同样会变成乱码。但是ASCAII里面的字符不会乱码,因为ASCAII总字符数有限,编码不会超过一个字节,UTF-8、GBK都兼容ASCAII。

读取结果:

read(byte[] b)

读取多个字节到字节数组中,返回读取到的字节数,如果读完了返回-1。byte是用来装读取字节的容易。

示例代码:

 // 根据文件路径创建输入流
            InputStream is = new FileInputStream("D://test.txt");

            int len;//读取到的字节数
            byte[] buffer = new byte[1024];
            while((len = is.read(buffer))!=-1){// 读取到的字节数为-1表示读取完毕
                System.out.printf(new String(buffer,0,len));// 只打印buffer中0到读取到的字节长度的内容
            }

打印时用了len,只打印buffer中从0到len的内容。因为每次读取时不会把上一次buffer中的内容清空,而且从前往后替换。比如假设buffer容量为3字节,读取的内容是“abcd",第一次读取后buffer内容为"abc",下一次读取会成为:“dbc”,因为只剩下一个字节了,会把第一个替换掉,后面的仍然保留上一次的,所以读取时需要通过len来记录读取到的字节数。 

BufferedInputStream

BufferedInputStream是对InputStream的一层包装,BufferedInputStream带有一个8KB的默认缓存,无论是单字节写入还是用字节数组写入,都会先把写入到的数据放到缓存中,然后再从磁盘写入到内存中,可以减少系统调用次数。

构造方法:

OutputStream os = new FileOutputStream("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\test.txt");
            byte[] buffer = new byte[1024];
            BufferedOutputStream bos = new BufferedOutputStream(os);

            InputStream is = new FileInputStream("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\test1.txt");
            BufferedInputStream bis = new BufferedInputStream(is);
            int len;
            while ((len = bis.read(buffer)) !=-1) {
                bos.write(buffer, 0, len);
            }

OutputStream

类似于InputStream,只是数据流向不同,是从内存输出到磁盘。该类也是一个抽象类,有很多实现类,FileOutputStream是常用的一个实现类。

FileOutputStream

将文件从内存输出到磁盘。构造方法和InputStream类似

构造方法
FileOutputStream(String name)//name为文件路径,可以是绝对路径也可以是相对路径
FileOutputStream(File file)//通过文件对象创建输入流
FileOutputStream(FileDescriptor fdObj)//不常用
常用函数

FileOutputStream主要用来输出文件到磁盘,通过write方法写入到磁盘,共包括以下几个重载方法:

第一个是单个字节写入,不常用。第二个是通过字节数组进行写入,第三个也是通过字节数组进行写入,但是传入了字节数组需要写入的起点和终点。

示例代码:

单个字节写入:

 try {
            OutputStream os = new FileOutputStream("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\test.txt");
            os.write('从');
            os.write('a');
            os.write(97);
        } catch (Exception e) {
            e.printStackTrace();
        }

结果: 

�aa

write方法可以传入汉字,但是只会截取汉字中的第一个字节进行写入,因此在解码时会成为乱码。也可以写入字符的代码。

使用字节数组写入

try {
            OutputStream os = new FileOutputStream("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\test.txt");
            byte[] buffer = new byte[1024];


            InputStream is = new FileInputStream("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\test1.txt");

            int len;
            while ((len = is.read(buffer)) !=-1) {
                os.write(buffer, 0, len);
            }

            os.close();
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

BufferedOutputStream

BufferedOutputStream类似于BufferedInputStream,只是BufferedOutputStream是对OutputStream的包装,BufferedOutputStream同样也有一个默认8KB的缓存。构造方法中需要传入一个OutputStream,也可以传入一个大小,表示缓存的大小,默认是8KB。

OutputStream os = new FileOutputStream("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\test.txt");
            byte[] buffer = new byte[1024];
            BufferedOutputStream bos = new BufferedOutputStream(os);

            InputStream is = new FileInputStream("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\test1.txt");
            BufferedInputStream bis = new BufferedInputStream(is);
            int len;
            while ((len = bis.read(buffer)) !=-1) {
                bos.write(buffer, 0, len);
            }

字符流

字符流写入时是根据字符进行写入的,写入时不会出现乱码的情况。

Reader

reader是一个抽象类,FileReader是其常用的一个实现类。

FileReader

逐个字符读取:

 Reader reader = new FileReader("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\test.txt");
            System.out.printf(String.valueOf((char)reader.read()));
            System.out.printf(String.valueOf((char)reader.read()));
            System.out.printf(String.valueOf((char)reader.read()));
            System.out.printf(String.valueOf((char)reader.read()));
            System.out.printf(String.valueOf((char)reader.read()));
            System.out.printf(String.valueOf((char)reader.read()));

因为是按字符读取,因此不会出现乱码。

按照字符数组进行读取:

 Reader reader = new FileReader("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\test.txt");

            char[] buffer = new char[1024];

            int len;
            while ((len = reader.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, len));
            }

BufferedReader

BufferedReader是Reader的一个包装类,传入参数reader和size,size表示BufferedReader的缓存大小,可以不用传,不传默认为8KB。

 Reader reader = new FileReader("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\test.txt");
            BufferedReader br = new BufferedReader(reader,8*1024);
            char[] buffer = new char[1024];

            int len;
            while ((len = reader.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, len));
            }

InputStreamReader

字符转换流,可以将字节流转换为字符流,并且可以设置编码方式。

 InputStream is = new FileInputStream("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\test.txt");
            InputStreamReader isr = new InputStreamReader(is,"GBK");//按照GBK编码读取
            BufferedReader br = new BufferedReader(isr);// 构造BufferedReader
            char[] buffer = new char[1024];

            int len;
            while ((len = br.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, len));
            }

因为我原文件的内容是用UTF-8编码,所以改成GBK会变成乱码。

aaab杩欐槸涓�娈垫祴璇曟枃鏈琣

Writer

抽象类

FileWriter

writer实现类,可以将文件从内存写入到磁盘。

 Writer writer = new FileWriter("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\output.txt");


            Reader reader = new FileReader("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\test.txt");

            char[] buffer = new char[1024];

            int len;
            while ((len = reader.read(buffer)) != -1) {
                writer.write(buffer, 0, len);
                System.out.printf(String.valueOf(buffer, 0, len));
            }

如果运行这段代码,会发现能够创造出output.txt文件,日志中打印的内容也不是空的,但是output.txt中的文件是空的,这是因为通过writer写入到磁盘时存在一个缓冲区,写入时会把内容放到缓冲区,并不会马上写入到磁盘中,如果需要马上写入到磁盘中,需要调用FileWriter.flush()方法才会写入,如果还没有调用flush进行写入到磁盘的时候缓冲区已经满了,则会自动写入。当然也可以不用通过flush方法来刷新写入到磁盘,可以直接调用Writer的close方法,在close时会将缓存中的内容写入到磁盘中。这也提醒我们,在使用IO流时需要做好资源的释放,避免产生内存泄漏问题。

BufferedWriter

BufferedWriter带有缓冲,默认为8KB,通过Writer创建。

OutputStreamWriter

OutputStream os = new FileOutputStream("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\output.txt");
            OutputStreamWriter osw = new OutputStreamWriter(os,"GBK");// 通过GBK编码方式写入
            BufferedWriter bw = new BufferedWriter(osw);

            Reader reader = new FileReader("C:\\Users\\Windows\\IdeaProjects\\FileAndIo\\test.txt");

            char[] buffer = new char[1024];

            int len;
            while ((len = reader.read(buffer)) != -1) {
                bw.write(buffer, 0, len);
                System.out.printf(String.valueOf(buffer, 0, len));
            }

性能对比

字节流

单字节输入流读取


    // 单字节输入流读取
    public static void singleInputStream() {
        long startTime = System.currentTimeMillis();
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream("D:\\test.txt"); // 请替换为实际路径
            int readed;
            StringBuffer sb = new StringBuffer();
            while ((readed = inputStream.read()) != -1) {
                sb.append((char) readed); // 将读取的字节转换为字符并添加到 StringBuffer
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 释放资源
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("singleInputStream耗时: " + (endTime - startTime) + " ms");
    }

字节数组输入流读取

// 字节数组输入流读取
    public static void multiInputStream() {
        long startTime = System.currentTimeMillis();
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream("D:\\test.txt"); // 请替换为实际路径
            byte[] buffer = new byte[1024];
            int readed;
            while ((readed = inputStream.read(buffer)) != -1) {
                // 处理读取的字节
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 释放资源
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("multiInputStream耗时: " + (endTime - startTime) + " ms");
    }

 单字节缓冲输入流读取

 // 单字节缓冲输入流读取
    public static void singleBufferedInputStream() {
        long startTime = System.currentTimeMillis();
        InputStream inputStream = null;
        try {
            inputStream = new BufferedInputStream(new FileInputStream("D:\\test.txt")); // 请替换为实际路径
            int readed;
            StringBuffer sb = new StringBuffer();
            while ((readed = inputStream.read()) != -1) {
                sb.append((char) readed); // 将读取的字节转换为字符并添加到 StringBuffer
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 释放资源
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("singleBufferedInputStream耗时: " + (endTime - startTime) + " ms");
    }

字节数组缓冲输入流读取

  // 字节数组缓冲输入流读取
    public static void multiBufferedInputStream() {
        long startTime = System.currentTimeMillis();
        InputStream inputStream = null;
        try {
            inputStream = new BufferedInputStream(new FileInputStream("D:\\test.txt")); // 请替换为实际路径
            byte[] buffer = new byte[1024];
            int readed;
            while ((readed = inputStream.read(buffer)) != -1) {
                // 处理读取的字节
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 释放资源
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("multiBufferedInputStream耗时: " + (endTime - startTime) + " ms");
    }

字符流

单字符读取


    // 单字符读取
    public static void singleReader() {
        long startTime = System.currentTimeMillis();
        Reader reader = null;
        try {
            reader = new FileReader("D:\\test.txt"); // 请替换为实际路径
            int readed;
            StringBuffer sb = new StringBuffer();
            while ((readed = reader.read()) != -1) {
                sb.append((char) readed); // 将读取的字符添加到 StringBuffer
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 释放资源
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("singleReader耗时: " + (endTime - startTime) + " ms");
    }

字符数组读取

 // 字符数组读取
    public static void multiReader() {
        long startTime = System.currentTimeMillis();
        Reader reader = null;
        try {
            reader = new FileReader("D:\\test.txt"); // 请替换为实际路径
            char[] buffer = new char[1024];
            int readed;
            while ((readed = reader.read(buffer)) != -1) {
                // 处理读取的字符
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 释放资源
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("multiReader耗时: " + (endTime - startTime) + " ms");
    }

单字符缓冲读取

 // 单字符缓冲读取
    public static void singleBufferedReader() {
        long startTime = System.currentTimeMillis();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader("D:\\test.txt")); // 请替换为实际路径
            int readed;
            StringBuffer sb = new StringBuffer();
            while ((readed = reader.read()) != -1) {
                sb.append((char) readed); // 将读取的字符添加到 StringBuffer
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 释放资源
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("singleBufferedReader耗时: " + (endTime - startTime) + " ms");
    }

字符数组缓冲读取


    // 字符数组缓冲读取
    public static void multiBufferedReader() {
        long startTime = System.currentTimeMillis();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader("D:\\test.txt")); // 请替换为实际路径
            char[] buffer = new char[1024];
            int readed;
            while ((readed = reader.read(buffer)) != -1) {
                // 处理读取的字符
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 释放资源
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("multiBufferedReader耗时: " + (endTime - startTime) + " ms");
    }

运行结果

开启多个线程执行所有方法

public static void main(String[] args) {
        new Thread(() -> singleInputStream()).start();
        new Thread(() -> multiInputStream()).start();
        new Thread(() -> singleBufferedInputStream()).start();
        new Thread(() -> multiBufferedInputStream()).start();
        new Thread(() -> singleReader()).start();
        new Thread(() -> multiReader()).start();
        new Thread(() -> singleBufferedReader()).start();
        new Thread(() -> multiBufferedReader()).start();
    }

用线程池执行 

 public static void main(String[] args) {
        // 创建 ThreadPoolExecutor,核心线程数、最大线程数、空闲时间和单位、工作队列
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                4, // 核心线程数
                8, // 最大线程数
                60, // 空闲线程存活时间
                TimeUnit.SECONDS, // 时间单位
                new LinkedBlockingQueue<>() // 工作队列
        );

        // 提交任务
        executor.execute(Main::singleInputStream);
        executor.execute(Main::multiInputStream);
        executor.execute(Main::singleBufferedInputStream);
        executor.execute(Main::multiBufferedInputStream);
        executor.execute(Main::singleReader);
        executor.execute(Main::multiReader);
        executor.execute(Main::singleBufferedReader);
        executor.execute(Main::multiBufferedReader);

        // 关闭线程池
        executor.shutdown();
        try {
            // 等待所有任务完成
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {

                executor.shutdownNow(); // 超时后强制关闭
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt(); // 恢复中断状态
        }
    }

 

multiBufferedInputStream耗时: 157 ms
multiReader耗时: 536 ms
multiBufferedReader耗时: 551 ms
multiInputStream耗时: 563 ms
singleBufferedReader耗时: 3506 ms
singleReader耗时: 5593 ms
singleBufferedInputStream耗时: 7698 ms
singleInputStream耗时: 296171 ms

可以带有缓冲流同时通过字节数组来多个读取的速度最快,单个字节读取并且不用缓冲流速度最慢。 接下来再做进一步测试,由于单个字节/字符读取使用并不是很多,字节流使用场景有一定的限制,只能用来读取文本,所以只保留用字节流数组读取的非缓冲流和缓冲流读取的方法。

因为缓冲字节流默认的缓冲大小是8KB,所以先将不带缓冲的字节数组大小改为8KB,看看两者耗时如何。

完整代码:

 public static void main(String[] args) {

        new Thread(() -> multiInputStream()).start();
        new Thread(() -> multiBufferedInputStream()).start();

    }


    // 字节数组输入流读取
    public static void multiInputStream() {
        long startTime = System.currentTimeMillis();
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream("D:\\test.txt"); // 请替换为实际路径
            byte[] buffer = new byte[1024];
            int readed;
            while ((readed = inputStream.read(buffer)) != -1) {
                // 处理读取的字节
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 释放资源
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("非缓冲流读取耗时: " + (endTime - startTime) + " ms");
    }



    // 字节数组缓冲输入流读取
    public static void multiBufferedInputStream() {
        long startTime = System.currentTimeMillis();
        InputStream inputStream = null;
        try {
            inputStream = new BufferedInputStream(new FileInputStream("D:\\test.txt")); // 请替换为实际路径
            byte[] buffer = new byte[1024];
            int readed;
            while ((readed = inputStream.read(buffer)) != -1) {
                // 处理读取的字节
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // 释放资源
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis();
        System.out.println("缓冲流读取耗时: " + (endTime - startTime) + " ms");
    }

字节数组大小为1KB时耗时:

缓冲流读取耗时: 99 ms
非缓冲流读取耗时: 296 ms

将buffer改成new byte[1024*8]之后的耗时:

非缓冲流读取耗时: 90 ms
缓冲流读取耗时: 92 ms

此时两者耗时几乎一致,改成16KB之后再次测试:

缓冲流读取耗时: 68 ms
非缓冲流读取耗时: 67 ms 

读取速度提升还是比较明显 

改成32KB:

非缓冲流读取耗时: 63 ms
缓冲流读取耗时: 65 ms

读取速度有所提升,但是提升有限。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值