简单易懂讲IO
流式 IO 是传统 IO,通过构造输入输出流,讲信息从一个地方读取,输出到另一个地方。常见的有读取文件以及写入文件。
基本 API
流失 IO 基本可以分为两个门派,一个以 InputStream 和 OutputStream 为代表的老牌 IO,一个以 Reader 和 Writer 为代表的新派 IO。
这里仅仅展示最常用 API,其余 API 可以查阅jdk API

输入
基本输入
| InputStream | Reader |
|---|---|
| InputStream 面向字节流 IO 不能很好的支持 Unicode 字符 | Reader 面向字符流 IO 能很好的支持 Unicode 字符 |
| FileInputStream 文件读取流 | FileReader 文件读取 |
| ByteArrayInputStream | CharArrayReader |
装饰器输入
基本输入中的流对象,都可以作为装饰器对象的构造器参数
| InputStream | Reader | 功能 |
|---|---|---|
| DataInputStream | 包含用于读取基本数据类型的全部接口 | |
| BufferedInputStream | BufferedReader | 本质上不提供接口,只是向进程添加缓冲功能。与接口对象搭配 |
输出
基本输出
| OutputStream | Writer |
|---|---|
| FileOutputStream | FileWriter |
| ByteArrayOutputStream | CharArrayWriter |
装饰器输出
| OutputStream | Writer | 功能 |
|---|---|---|
| DataOutputStream | 包含用于写入基本数据类型的全部接口 | |
| PrintStream | PrintWriter | 用于产生格式化输出 |
| BufferedOutputStream | BufferedWriter | 本质上并不提供接口,只是向进程添加缓冲功能。与接口对象搭配 |
常见用法
读取文件
使用 FileInputStream 读取
下面例子将输入流放入 try-with-resource 块中,以实现资源的自动关闭,本文下面例子都将采用这种形式。
这里可以看到,是一个字节一个字节的读,所以要将其转为 char 才能正常展示,否则展示的都是字节。 由于 InputStream 是字节流,因此,这里读取到的中文展示乱码。
public class Read {
/**
* 使用 FileInputStream 直接读取
* 由于 InputStream 不支持 Unicode 编码,所以中文显示会乱码
*/
public static void fileInputStream() {
try (
FileInputStream input = new FileInputStream("Read.java")
) {
int n = 0;
while (n != -1) {
n = input.read();
char c = (char) n;
System.out.print(c);
}
} catch (Exception e) {
}
}
public static void main(String[] args) {
Read.fileInputStream();
}
}
输出:
package cyrus.file_io.iostream;
import java.io.FileInputStream;
public class Read {
/**
* ä½¿ç¨ FileInputStream ç´æ¥è¯»å
* ç±äº InputStream 䏿¯æ Unicode ç¼ç ï¼æä»¥ä¸ææ¾ç¤ºä¼ä¹±ç
*/
public static void fileInputStream() {
try (
FileInputStream input = new FileInputStream("Read.java")
) {
int n = 0;
while (n != -1) {
n = input.read();
char c = (char) n;
System.out.print(c);
}
} catch (Exception e) {
}
}
public static void main(String[] args) {
Read.fileInputStream();
}
}
使用 BufferedInputStream 装饰器读取
以下例子使用 FileInputStream 构造一个 BufferedInputStream 装饰器,该适配器的主要作用是会将读取到的内容填充进缓冲区,其余用法和 FileInputStream 一样。InputStream 是字节流,因此,这里读取到的中文展示乱码。
public class Read {
/**
* 使用 FileInputStream 构造一个 BufferedInputStream 装饰器,读取,该读取会使用缓冲区
* 由于 InputStream 不支持 Unicode 编码,所以中文会乱码
*/
public static void fileInputStreamWithBuffer() {
try (
BufferedInputStream input = new BufferedInputStream(new FileInputStream("Read.java"))
) {
int n = 0;
while (n != -1) {
n = input.read();
char c = (char) n;
System.out.print(c);
}
} catch (Exception e) {
}
}
public static void main(String[] args) {
Read.fileInputStreamWithBuffer();
}
}
输出:
package cyrus.file_io.iostream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
public class Read {
/**
* ä½¿ç¨ FileInputStream æé ä¸ä¸ª BufferedInputStream è£
饰å¨ï¼è¯»åï¼è¯¥è¯»åä¼ä½¿ç¨ç¼å²åº
* ç±äº InputStream 䏿¯æ Unicode ç¼ç ï¼æä»¥ä¸æä¼ä¹±ç
*/
public static void fileInputStreamWithBuffer() {
try (
BufferedInputStream input = new BufferedInputStream(new FileInputStream("Read.java"))
) {
int n = 0;
while (n != -1) {
n = input.read();
char c = (char) n;
System.out.print(c);
}
} catch (Exception e) {
}
}
public static void main(String[] args) {
Read.fileInputStreamWithBuffer();
}
}
使用 FileReader 进行读取
使用 FileReader 直接读取,这里 Reader 支持 Unicode 编码,因此中文不会乱码,正常显示
public class Read {
/**
* 使用 FileReader 直接读取
* 由于 Reader 支持 Unicode 编码,因此中文不会乱码,正常显示
*/
public static void fileReader() {
try (
FileReader reader = new FileReader("Read.java")
) {
int n = 0;
while (n != -1) {
n = reader.read();
char c = (char) n;
System.out.print(c);
}
} catch (Exception e) {
}
}
public static void main(String[] args) {
Read.fileReader();
}
}
输出:
package cyrus.file_io.iostream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileReader;
public class Read {
/**
* 使用 FileReader 直接读取
* 由于 Reader 支持 Unicode 编码,因此中文不会乱码,正常显示
*/
public static void fileReader() {
try (
FileReader reader = new FileReader("Read.java")
) {
int n = 0;
while (n != -1) {
n = reader.read();
char c = (char) n;
System.out.print(c);
}
} catch (Exception e) {
}
}
public static void main(String[] args) {
Read.fileReader();
}
}
使用 BufferedReader 装饰器读取
这里使用 FileReader 构造一个 BufferedReader 装饰器,BufferedReader 的主要作用是会将读取到的内容填入缓冲区,并且 BufferedReader 的 lines() 方法将返回一个 stream 流,操作更方便。
public class Read {
/**
* 使用 FileReader 构造一个 BufferedReader 装饰器,读取,该读取会使用缓冲区
* 由于 Reader 支持 Unicode 编码,因此中文不会乱码,正常显示
*/
public static void fileReaderWithBuffer() {
try (BufferedReader reader = new BufferedReader(new FileReader("Read.java"))) {
reader.lines().forEach(System.out::println);
} catch (Exception e) {
}
}
public static void main(String[] args) {
Read.fileReaderWithBuffer();
}
}
输出:
package cyrus.file_io.iostream;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileReader;
public class Read {
/**
* 使用 FileReader 构造一个 BufferedReader 装饰器,读取,该读取会使用缓冲区
* 由于 Reader 支持 Unicode 编码,因此中文不会乱码,正常显示
*/
public static void fileReaderWithBuffer() {
try (BufferedReader reader = new BufferedReader(new FileReader("Read.java"))) {
reader.lines().forEach(System.out::println);
} catch (Exception e) {
}
}
public static void main(String[] args) {
Read.fileReaderWithBuffer();
}
}
使用 DataInputStream 适配器读取字符串
这里 buildString() 方法读取当前文件,将其拼装为字符串输出,ByteArrayInputStream 读取该字符串的 byte 数组,然后转化为 DataInputStream 适配器进行读取字符串内容。
DataInputStream 主要用于读取基本数据类型
public class Read {
/**
* 使用 ByteArrayInputStream 构造 DataInputStream 装饰器,输出字符串信息
*/
public static void dataInputStream() {
try (
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(buildString().getBytes()))
) {
while (inputStream.available() != 0) {
System.out.print((char) inputStream.readByte());
}
} catch (Exception e) {
}
}
/**
* 通过目前 java 文件构建一个字符串
*
* @return
*/
public static String buildString() {
try (BufferedReader reader = new BufferedReader(new FileReader("Read.java"))) {
return reader.lines().collect(Collectors.joining("\n"));
} catch (Exception e) {
}
return "ok";
}
public static void main(String[] args) {
Read.dataInputStream();
}
}
写入文件
使用 FileOutputStream 写入
这里直接使用 FileOutputStream 读取 buildString() 方法构造的字符串并将其写入 Read.txt 文件
public class Read {
/**
* 使用 FileOutputStream 直接写入字符串
*/
public static void fileOutputStream() {
try (
FileOutputStream output = new FileOutputStream("Read.txt")
) {
output.write(buildString().getBytes());
} catch (Exception e) {
}
}
/**
* 通过目前 java 文件构建一个字符串
*
* @return
*/
public static String buildString() {
try (BufferedReader reader = new BufferedReader(new FileReader("Read.java"))) {
return reader.lines().collect(Collectors.joining("\n"));
} catch (Exception e) {
}
return "ok";
}
public static void main(String[] args) {
Read.fileOutputStream();
}
}
输出:实例截图一部分

使用 BufferedOutputStream 适配器写入
这里使用 FileOutputStream 构造一个 BufferedOutputStream 适配器,BufferedOutputStream 会使用到缓冲区,加快读取写入速度。
public class Read {
/**
* 使用 FileOutputStream 构造一个 BufferedOutputStream 装饰器,读取,该写入会使用缓冲区
*/
public static void fileOutputStreamWithBuffer() {
try (
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream("Read.txt"));
) {
output.write(buildString().getBytes());
} catch (Exception e) {
}
}
/**
* 通过目前 java 文件构建一个字符串
*
* @return
*/
public static String buildString() {
try (BufferedReader reader = new BufferedReader(new FileReader("Read.java"))) {
return reader.lines().collect(Collectors.joining("\n"));
} catch (Exception e) {
}
return "ok";
}
public static void main(String[] args) {
Read.fileOutputStreamWithBuffer();
}
}
后面就不展示输出了,所有的输出都是输出到当前工作目录的Read.txt文件夹下,并且文件内容都是当前 .java 文件的内容
使用 FileWriter 写入
public class Read {
/**
* 使用 FileWriter 直接写入
*/
public static void fileWriter() {
try (
FileWriter writer = new FileWriter("Read.txt");
) {
writer.write(buildString());
} catch (Exception e) {
}
}
/**
* 通过目前 java 文件构建一个字符串
*
* @return
*/
public static String buildString() {
try (BufferedReader reader = new BufferedReader(new FileReader("Read.java"))) {
return reader.lines().collect(Collectors.joining("\n"));
} catch (Exception e) {
}
return "ok";
}
public static void main(String[] args) {
Read.fileWriter();
}
}
使用 PrintWriter 进行输出
这里使用了两层适配器,第一层是使用 FileWriter 构造一个 BufferedWriter,该适配器会使用缓冲区进行写入,然后再使用 BufferedWriter 构造一个PrintWriter 适配器,使用该适配器的println()方法直接输出到指定文件。
public class Read {
/**
* 使用 FileWriter 构造一个 BufferedWriter 装饰器,该装饰器会使用缓冲区,然后再使用 BufferedWriter 构造一个 PrintWriter装饰器,使用 println 输出到文件
*/
public static void fileWriterWithBuffer() {
try (
PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter("Read.txt")))
) {
writer.println(buildString());
} catch (Exception e) {
}
}
/**
* 通过目前 java 文件构建一个字符串
*
* @return
*/
public static String buildString() {
try (BufferedReader reader = new BufferedReader(new FileReader("Read.java"))) {
return reader.lines().collect(Collectors.joining("\n"));
} catch (Exception e) {
}
return "ok";
}
public static void main(String[] args) {
Read.fileWriterWithBuffer();
}
}
总结
流式IO 其实挺好理解的,一共分为两套,InputStream、OutputStream 为一套,Reader 和 Writer 为一套,其中各自都有基本输入输出类和使用基本输入输出类构造的装饰器输入输出类,装饰器还能构造装饰器,无限套娃。
如 new PrintWriter(new BufferedWriter(new FileWriter("Read.txt"))),这里就将 BufferedWriter 装饰器装配成了 PrintWriter 装饰器,这样这个 PrintWriter 就拥有了 BufferedWriter 的特性(使用缓冲区),以及他自己的特性。
文章为本人学习过程中的一些个人见解,漏洞是必不可少的,希望各位大佬多多指教,帮忙修复修复漏洞!!!
参考资料:java 编程思想
欢迎进入本人语雀文档阅读,格式更清晰哦:
https://www.yuque.com/docs/share/26bf2f91-b3d3-4b28-a43e-302a1e650d57?# 《流式 IO》
简单易懂讲IO的更多相关文章
- 简单聊下IO复用
没图,不分析API Java中IO API的发展:Socket -> SocketChannel -> AsynchronousSocketChannelServerSocket -> ...
- 【repost】让你一句话理解闭包(简单易懂)
接触javascript很久了,每次理解闭包都似是而非,最近在找Web前端的工作,所以需要把基础夯实一下. 本文是参照了joy_lee的博客 闭包 在她这篇博客的基础上以批注的形式力争把我的理解阐述出 ...
- 【转】JS回调函数--简单易懂有实例
JS回调函数--简单易懂有实例 初学js的时候,被回调函数搞得很晕,现在回过头来总结一下什么是回调函数. 我们先来看看回调的英文定义:A callback is a function that is ...
- java生成RSA公私钥字符串,简单易懂
java生成RSA公私钥字符串,简单易懂 解决方法: 1.下载bcprov-jdk16-140.jar包,参考:http://www.yayihouse.com/yayishuwu/chapter ...
- HashSet的实现原理,简单易懂
HashSet的实现原理,简单易懂 答: HashSet实际上是一个HashMap实例,都是一个存放链表的数组.它不保证存储元素的迭代顺序:此类允许使用null元素.HashSet中不允许有重复元 ...
- Linux服务器评测脚本 中文IO脚本简单易懂
中文版: wget -N --no-check-certificate https://raw.githubusercontent.com/FunctionClub/ZBench/master/ZBe ...
- MEF入门之不求甚解,但力求简单能讲明白(四)
上一篇我们已经可以获取各种FileHandler的实例和对应的元数据.本篇,我们做一个稍微完整的文件管理器. 1.修改接口IFileHandler,传入文件名 namespace IPart { pu ...
- 简单易懂, JUnit 框架问答
本文算是一个关于Junit4相关的知识分享,但是不同于网上大段的源码分析,模式学习文章,我想通过问答的形式,引出代码来简明阐述JUnit4是如何实现需要的功能的. 考虑到任何一个框架,都是为了解决问题 ...
- Http与协议TCP协议简单易懂
于C#编写代码,很多时候会遇到Http协议或TCP合约,这里做一个简单的了解. TCP对应于该传送层协议,和HTTP对应于应用层协议,从本质上讲,两者是没有可比性.Http该协议是基于TCP之上的,当 ...
随机推荐
- Spring Boot 入门系列(二十八) JPA 的实体映射关系,一对一,一对多,多对多关系映射!
前面讲了Spring Boot 使用 JPA,实现JPA 的增.删.改.查的功能,同时也介绍了JPA的一些查询,自定义SQL查询等使用.JPA使用非常简单,功能非常强大的ORM框架,无需任何数据访问层 ...
- Linux上安装服务器监视工具,名为Scout_Realtime。
如何从浏览器监视Linux服务器和进程指标 在服务器上安装Ruby 1.9.3+ sudo yum -y install rubygems-devel 在Linux系统上安装了Ruby之后,现在可以使 ...
- CodeForce-810B Summer sell-off (结构体排序)
http://codeforces.com/problemset/problem/810/B 已知n天里,已知第i天的供货量和需求量,给定一个f,可以在n天之中选f天促销使得供货量翻倍. 问选择其中f ...
- AT2368-[AGC013B]Hamiltonish Path【构造】
正题 题目链接:https://www.luogu.com.cn/problem/AT2368 题目大意 给出 \(n\) 个点 \(m\) 条边的一张无向图,然后求一条路径满足 路径长度不小于二. ...
- 牛客练习赛79E-小G的数学难题【dp,单调队列】
正题 题目链接:https://ac.nowcoder.com/acm/contest/11169/E 题目大意 给出\(n\)个三元组\((a_i,b_i,c_i)\). 要求选出一个集合\(S\) ...
- Linux系统自我学习的一些笔记1
远程连接: 1.查看IP地址 ip addr 2.远程登陆linux系统 ssh 主机名@IP地址 文件操作: 新建文件touch 例如:touch test.txt (创建单个文件) 例如:to ...
- MySQL优化之路
一.Mysql的存储原理 索引相关 本质 索引是帮助MySQL高效获取数据的排好序的数据结构 建索引,提高数据检索的效率,降低数据库的IO成本: 通过索引列对数据进行排序,降低数据排序的成本, ...
- java 从零开始手写 RPC (04) -序列化
序列化 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 java 从零开始手写 RPC (03) 如何实 ...
- CSharpEntityFramework与CodeFirst实践
CSharpEntityFramework与CodeFirst实践 前言 当我们进行开发的时候,常常会用到数据库来对数据进行持久化的操作,有的时候,我们并不想要在进行代码开发的过程中,还去关注数据库的 ...
- 从零入门 Serverless | 企业级 CI/CD 工具部署 Serverless 应用的落地实践
背景知识 通过以往几节课程的学习,相信大家对于 SAE 平台已经有了一定的了解.SAE 为客户免除了很多复杂的运维工作,开箱即用.按用量付费:与此同时 SAE 提供了丰富的 Open API,可以很容 ...