提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、缓冲流
-
- 1 字节缓冲流
-
- (1)BufferedInputStream:字节缓冲输入流
-
- 构造方法
-
- ---- BufferedInputStream(InputStream in):创建一个使用默认缓冲区大小的缓冲输入流。
- ---- BufferedInputStream(InputStream in, int size):创建一个指定缓冲区大小的缓冲输入流。
- 输入方法
-
- ---- int read(): 读取一个字节的数据。
- ---- int read(byte[] b) : 读取多个字节的数据。
- ---- int read(byte[] b, int off, int len) : 读取多个字节的数据。
- 关闭资源 void close()
- 代码演示
-
- ---- 读取文件(一个一个字节读):int read()
- ---- 读取文件(多个多个字节读):int read(byte[] b)
- (2)BufferedOutputStream:字节缓冲输出流
-
- 构造方法
-
- ---- BufferedOutputStream(OutputStream out):使用默认的缓冲区大小创建一个新的缓冲输出流,out 是底层的输出流(例如 FileOutputStream)
- ---- BufferedOutputStream(OutputStream out, int size):创建一个带有指定缓冲区大小的缓冲输出流,size 指定缓冲区的大小
- 写入方法
-
- ---- void write(int b):一个一个字节写入
- ---- void write(byte[] b):多个多个字节写入
- ---- void write(byte[] b, int off, int len):多个多个字节写入
- 关闭资源 void close()
- 代码演示
-
- ---- 写入文件(单个单个字节写入void write(int b)):覆盖模式
- ---- 写入文件(多个多个字节写入void write(byte[] b)):覆盖模式
- ---- 写入文件(多个多个字节写入void write(byte[] b)):追加模式
- (3)字节缓冲流拷贝文件
- (4)字节缓冲流底层原理
- 2 字符缓冲流
-
- (1)BufferedReader:字符缓冲输入流
-
- 构造方法
-
- ---- BufferedReader(Reader in):创建一个使用默认大小(8192长度的字符数组)输入缓冲区的缓冲字符输入流
- ---- BufferedReader(Reader in, int size):创建一个使用指定大小输入缓冲区的缓冲字符输入流
- 读取方法
-
- ---- int read():一个一个字符的读
- ---- int read(char[] cbuf):多个多个字符的读,返回实际读取的字符数
- ---- int read(char[] cbuf, int off, int len):多个多个字符的读,返回实际读取的字符数
- ---- String readLine():一行一行的读
- 关闭资源:void close()
- 代码演示
-
- ---- 读取文件(一个一个字符的读):int read()
- ---- 读取文件(多个多个字符的读):int read(char[] cbuf)
- ---- 读取文件(一行一行的读):String readLine()
- ---- 读取文件:指定编码格式
- (2)BufferedWriter: 字符缓冲输出流
-
- 构造方法
-
- ---- BufferedWriter(Writer out):使用默认缓冲区大小(8192字符数字)创建一个缓冲字符输出流
- ---- BufferedWriter(Writer out, int size):创建一个具有指定缓冲区大小的缓冲字符输出流
- 写入方法
-
- ---- void write(int c):一个一个写入,将指定的字符写入到缓冲区。
- ---- void write(char[] cbuf):多个多个写入
- ---- void write(char[] cbuf, int off, int len):多个多个写入
- ----- void write(String s):直接写入字符串
- ----- void write(String s, int off, int len):直接写入字符串
- ----- void newLine():写入一个跨平台换行符
- 关闭资源:void close()
- 代码演示
-
- ---- 写入文件(一个一个字符写入void write(int c) (超笨)):覆盖写入
- ---- 写入文件(多个多个字符写入void write(char[] cbuf) (超笨)):覆盖写入
- ---- 写入文件(直接写入void write(String s),推荐):覆盖写入
- ---- 写入文件(直接写入void write(String s),推荐):追加写入
- ---- 写入文件:指定编码格式,覆盖写入
- ---- 写入文件(跨系统换行, void newLine()):追加写入
- 3 综合练习
-
- 练习1:恢复出师表顺序
- 练习2:软件运行次数(主要是计数器要保存到本地)
- 易犯的错误和创建IO流的原则:输出流的覆盖模式构造方法在打开流的时候就会将文件清空,所以如果输出流和输入流是操作同一个文件,一定要考虑创建流对象的位置和顺序,不然输入流就会读入的是被输出流清空的文件然后读到一个空指针null
- 二、转换流(转换流只有字符流,没有字节流)== 了解即可,JDK11以后基本开始要被淘汰了,我们都统一使用字符流中基本流的新特性
-
- 1 InputStreamReader
-
- (1)构造方法
-
- ---- InputStreamReader(InputStream in):使用系统默认的字符编码来创建 InputStreamReader
- ---- InputStreamReader(InputStream in, String charsetName):使用指定的字符编码来创建 InputStreamReader
- ---- InputStreamReader(InputStream in, Charset cs):使用指定的字符编码对象来创建 InputStreamReader
- (2)读取方法
-
- ---- int read():读取单个字符,返回的值是字符的 Unicode 编码。如果已到达流的末尾,则返回 -1
- ---- int read(char[] cbuf):多个多个字符读取,返回读取的字符个数,如果到达流末尾则返回 -1。
- ---- int read(char[] cbuf, int offset, int length):多个多个字符读取,返回读取的字符个数,如果到达流末尾则返回 -1
- (3)关闭资源
- (4)代码演示:读取指定编码格式
- 2 OutputStreamWriter
-
- (1)构造方法
-
- ---- OutputStreamWriter(OutputStream out):使用系统默认的字符编码进行构建OutputStreamWriter
- ---- OutputStreamWriter(OutputStream out, String charsetName):使用指定字符编码构建OutputStreamWriter
- ---- OutputStreamWriter(OutputStream out, Charset cs):使用Charset类对象指定字符编码创建OutputStreamWriter
- (2)写入方法
-
- ---- void write(int c):一个一个写入,将指定的字符写入到缓冲区。
- ---- void write(char[] cbuf):多个多个写入
- ---- void write(char[] cbuf, int off, int len):多个多个写入
- ----- void write(String s):直接写入字符串
- ----- void write(String s, int off, int len):直接写入字符串
- (3)关闭资源
- (4)代码演示:按照指定编码格式写入文件
- 3 综合练习:改变文件的编码格式(建议用JDK11前后两种不同手段,今后我们统一使用jdk11后那种新特性)
- 三、序列化流和反序列化流(都是字节流没有字符流)== 存对象,类似Python pickle库
-
- 1 序列化流(写出对象):对象操作输出流
-
- (1)构造方法:public ObjectOutputStream(OutputStream out):把基本流(字节流)包装成高级流(序列化流)
- (2)写入方法:public final void writeObject(Object obj): 把对象序列化(写出)到文件中去
- (3)释放资源
- (4)序列化流使用细节:要写到本地的Javebean类必须要实现Serializable接口(标记型接口)
- (5)代码演示
- 2 反序列化流(读入对象):对象操作输入流
-
- (1)构造方法:public ObjectInputStream(InputStream out):把基本输入流变量反序列化流
- (2)读入方法:public Object readObject(): 把序列化到本地文件中的对象,读取到程序中来(注意:读进来是Object类型,还需要强转一下)
- (3)关闭资源
- (4)代码演示
- 3 序列化流和反序列化流的使用细节
-
- (1)JavaBean版本号 implements Serializable
- (2)transient修饰关键字,用这个关键字修饰的属性不会被序列化到本地中,用于保密
- 4 综合练习和总结
-
- (1)总结
- (2)综合练习:序列化多个对象到本地
- 四、打印流(不能读,只能写,只有输出流)== 结合System.out来学习
-
- 1 字节打印流:PrintStream
-
- (1)构造方法
-
- ---- public PrintStream(OutputStream/File/String): 关联字节输出流/文件/文件路径
- ---- public PrintStream(File/String, Charset charset): 关联字节输出流/文件/文件路径,并且指定字符编码
- ---- public PrintStream(OutputStream out, boolean autoFlush): 关联字节输出流, 并设置自动刷新
- ---- public PrintStream(OutputStream out, boolean autoFlush,String encoding): 关联字节输出流, 并设置自动刷新,指定字符编码方式
- (2)写入(常规方法)和打印(print系列)方法(特殊方法)
-
- ---- public void write(int b):常规方法:规则和之前一样,将指定的字节写出
- ---- public void println(Xxx xx):特有方法:打印任意数据,自动刷新,自动换行
- ---- public void print(Xxx xx):特有方法:打印任意数据,自动刷新,不换行
- ---- public void printf(String format ,Object... args):特有方法:带有占位符的打印语句(格式化打印),不换行
- (3)关闭资源 void close()
- (4)代码示例
- (5)具体应用场景:模拟System.out和System.out原理解释
- 2 字符打印流:PrintWriter
-
- (1)构造方法
-
- ---- public PrintWriter(Writer/File/String): 关联字符输出流/文件/文件路径
- ---- public PrintWriter(String fileName,Charset charset): 关联字符文件路径、指定字符编码
- ---- public PrintWriter(Writer w,boolean autoFlush): 关联字符输出流、自动刷新
- ---- public PrintWriter(OutputStream out, boolean autoFlush, Charset charset): 指定字符编码、自动刷新
- (2)写入方法和打印方法
-
- ---- public void write(int b):常规方法:规则和之前一样,将指定的字节写出
- ---- public void println(Xxx xx):特有方法:打印任意数据,自动刷新,自动换行
- ---- public void print(Xxx xx):特有方法:打印任意数据,自动刷新,不换行
- ---- public void printf(String format ,Object... args):特有方法:带有占位符的打印语句(格式化打印),不换行
- (3)关闭资源 void close()
- (4)代码示例
- 五、压缩流(写)和解压缩流(读)(都是字节流)
-
- 1 ZipInputStream: 解压缩流
-
- (1)构造方法
-
- ---- public ZipInputStream(InputStream in)
- (2)ZipInputStream中:getNextEntry()方法:返回解压缩流中每一个ZipEntry对象,到达最后返回null
- (3)ZipEntry(目录或者文件)中的一些方法
-
- ---- isDirectory():判断是否是目录
- ---- read():读取一个字节,到末尾返回-1
- ---- int read(byte[] b) : 读取多个字节的数据。
- ---- int read(byte[] b, int off, int len) : 读取多个字节的数据。
- ---- closeEntry():每处理完一个ZipEntry对象都要关闭
- (4)关闭资源 void close()
- (5)代码演示
- 2 ZipOutputStream: 压缩流
-
- (1)压缩单个文件
- (2)压缩文件夹
前言
前面学FileReader和FileWriter字符流:对于中文只能处理当前平台的编码方式进行编码和解码,不能指定编码格式进行编码解码。
这个指定编码的功能,在本节的高级流(转换流)上就会得到处理。
另外,高级流都是在上节的四中基本流上面进一步封装而来的,可以说前面基本流是他们的爸爸。
缓冲流(提供缓冲区,加快读取写入速度,减少和硬盘交互次数)
转换流(可以指定编码格式)
基本流—>缓冲流
转换流(都是字符流)
序列化流和反序列化流(都是字节流)
打印流(不能读,只能写,只有输出流)
一、缓冲流
缓冲流就是在之前基本流的基础上加了一个缓冲区(缓冲数组,减少和磁盘交互的次数),可以加快读写的效率。
其实基本流中的字符流本身底层原理就是有缓冲区的参考博客,所以缓冲流的对于字符流的加快读写效率并没有快很多。主要是其中有几个非常好用的方法需要我们学习一下。
1 字节缓冲流
- 原理:底层自带了长度为8192的缓冲区(缓冲数组)提高性能
这样每次和硬盘交互我都可以直接将8192个字节放进内存,就是是一个字节一个字节的读8193个字节才和硬盘交互一次,不然一个字节就和硬盘交互一次太浪费时间了。
(1)BufferedInputStream:字节缓冲输入流
【注】:其实基本流学好了,这个没有什么区别。
构造方法
---- BufferedInputStream(InputStream in):创建一个使用默认缓冲区大小的缓冲输入流。
默认缓冲区是 8192 的字节数组
---- BufferedInputStream(InputStream in, int size):创建一个指定缓冲区大小的缓冲输入流。
输入方法
---- int read(): 读取一个字节的数据。
返回值是读取的字节(0 到 255 之间的整数),如果已经到达流的末尾,返回 -1
如果是处理文本文件通常要结合char强转使用。
---- int read(byte[] b) : 读取多个字节的数据。
一次读取多个字节(返回值表示本次读取到的字节的个数,不是值),从文件中读取字节并将其存储到字节数组 b 中(每次读取会尽可能把数组装满),返回读取的字节数,如果到达文件末尾返回 -1。
如果是处理文本文件通常结合String(字节数组,start,end)构造方法来使用,start、end分别是字节数组起始索引
---- int read(byte[] b, int off, int len) : 读取多个字节的数据。
从输入流中读取最多 len 个字节的数据,并将其存储到字节数组 b 的从 off 位置开始的部分,返回实际读取的字节数,或者在流结束时返回 -1。
关闭资源 void close()
关闭缓冲流就行,在底层关闭缓冲流里面已经封装了关闭基本流的代码。所以关闭缓冲流就直接将基本流一起关闭了。
代码演示
---- 读取文件(一个一个字节读):int read()
int read()
public class Test {
public static void main(String[] args) throws IOException {
// a.txt文件内容:abcdefdghiuihhj
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(".\\a.txt"));
StringBuilder sb = new StringBuilder();
int b;
while((b = bis.read()) != -1){
sb.append((char)b);
}
bis.close(); // 关闭流,关闭缓冲流,基本流也会关闭
System.out.println(sb.toString()); // abcdefdghiuihhj
}
}
---- 读取文件(多个多个字节读):int read(byte[] b)
int read(byte[] b)
public class Test {
public static void main(String[] args) throws IOException {
// a.txt文件内容:abcdefdghiuihhj
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(".\\a.txt"));
try(bis){
StringBuilder sb = new StringBuilder();
byte[] bytes = new byte[2];
int len;
while ((len = bis.read(bytes)) != -1) {
sb.append(new String(bytes, 0, len));
}
System.out.println(sb.toString()); // abcdefdghiuihhj
}
}
}
(2)BufferedOutputStream:字节缓冲输出流
构造方法
【注】:覆盖模式还是追加模式还是在基本流的构造方法里面传入就可以了
---- BufferedOutputStream(OutputStream out):使用默认的缓冲区大小创建一个新的缓冲输出流,out 是底层的输出流(例如 FileOutputStream)
---- BufferedOutputStream(OutputStream out, int size):创建一个带有指定缓冲区大小的缓冲输出流,size 指定缓冲区的大小
写入方法
---- void write(int b):一个一个字节写入
将指定的字节写入到缓冲区。如果缓冲区满了,则会将缓冲区中的内容写入到底层输出流。
---- void write(byte[] b):多个多个字节写入
---- void write(byte[] b, int off, int len):多个多个字节写入
将 b 数组中从 off 位置开始的 len 个字节写入到缓冲区中。如果缓冲区满了,也会将缓冲区的内容写入到底层输出流。
关闭资源 void close()
关闭缓冲流就行,在底层关闭缓冲流里面已经封装了关闭基本流的代码。所以关闭缓冲流就直接将基本流一起关闭了。
代码演示
---- 写入文件(单个单个字节写入void write(int b)):覆盖模式
void write(int b)
public class Test {
public static void main(String[] args) throws IOException {
// a.txt文件 写入 abc
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.txt"));
try (bos) {
bos.write(97);
bos.write(98);
bos.write(99);
}
}
}
---- 写入文件(多个多个字节写入void write(byte[] b)):覆盖模式
void write(byte[] b)
public class Test {
public static void main(String[] args) throws IOException {
// a.txt文件 写入 abc你好
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.txt"));
try (bos) {
bos.write("abc你好".getBytes()); // 默认是平台的utf-8编码写入字节,也可以指定编码写入
}
}
}
---- 写入文件(多个多个字节写入void write(byte[] b)):追加模式
public class Test {
public static void main(String[] args) throws IOException {
// a.txt文件内容:abc你好 追加写入 \n曹潇潇我爱你
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.txt", true));
try (bos) {
bos.write("\n曹潇潇我爱你".getBytes());
}
}
}
(3)字节缓冲流拷贝文件
同样是拷贝视频文件
public class Test {
public static void main(String[] args) throws IOException {
// 拷贝 D:\GraduateStudent\研一\taobao_项目\【深度学习理论基础】\13-13 - 2.6更多导数示例.mp4 到 IDEA当前工作路径 video.mp4
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\GraduateStudent\\研一\\taobao_项目\\【深度学习理论基础】\\13-13 - 2.6更多导数示例.mp4"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(".\\video.mp4"));
try(bis; bos) {
byte[] bytes = new byte[1024*1024*5];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
}
}
}
(4)字节缓冲流底层原理
参考视频
2 字符缓冲流
(1)BufferedReader:字符缓冲输入流
构造方法
---- BufferedReader(Reader in):创建一个使用默认大小(8192长度的字符数组)输入缓冲区的缓冲字符输入流
---- BufferedReader(Reader in, int size):创建一个使用指定大小输入缓冲区的缓冲字符输入流
读取方法
---- int read():一个一个字符的读
读取一个字符。返回值是读取的字符的对应字符集对应的十进制数字(作为 int 返回),如果已经到达流的末尾,返回 -1。
---- int read(char[] cbuf):多个多个字符的读,返回实际读取的字符数
---- int read(char[] cbuf, int off, int len):多个多个字符的读,返回实际读取的字符数
从字符输入流中最多读取 len 个字符,存储到 cbuf 数组中,从 off 位置开始。返回实际读取的字符数,流结束时返回 -1。
---- String readLine():一行一行的读
读取一行文本。当到达流末尾时,返回 null。通常用于逐行读取文件内容。
【注】:虽然是一行一行读。但是并不会将换行符也读到内存,也就是说每行结尾的换行符不会读进内存。
关闭资源:void close()
代码演示
---- 读取文件(一个一个字符的读):int read()
public class Test {
public static void main(String[] args) throws IOException {
// 读取 a.txt 里面内容为: abc你好\r\n曹潇潇我爱你
BufferedReader br = new BufferedReader(new FileReader(".\\a.txt"));
StringBuilder sb = new StringBuilder();
int ch;
while ((ch = br.read()) != -1) {
sb.append((char) ch);
}
br.close();
System.out.println(sb.toString());
/*
abc你好
曹潇潇我爱你
*/
}
}
---- 读取文件(多个多个字符的读):int read(char[] cbuf)
public class Test {
public static void main(String[] args) throws IOException {
// 读取 a.txt 里面内容为: abc你好\r\n曹潇潇我爱你
BufferedReader br = new BufferedReader(new FileReader(".\\a.txt"));
try(br){
StringBuilder sb = new StringBuilder();
char[] buf = new char[2];
int len;
while ((len = br.read(buf)) != -1) {
sb.append(new String(buf, 0, len));
}
System.out.println(sb.toString());
}
/*
abc你好
曹潇潇我爱你
*/
}
}
---- 读取文件(一行一行的读):String readLine()
public class Test {
public static void main(String[] args) throws IOException {
// 读取 a.txt 里面内容为: abc你好\r\n曹潇潇我爱你
BufferedReader br = new BufferedReader(new FileReader(".\\a.txt"));
try(br){
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
/*
abc你好
曹潇潇我爱你
*/
}
}
通常我们喜欢将每一行的String放进一个List再进行进一步的处理加工
public class Test {
public static void main(String[] args) throws IOException {
// 读取 a.txt 里面内容为: abc你好\r\n曹潇潇我爱你
BufferedReader br = new BufferedReader(new FileReader(".\\a.txt"));
ArrayList<String> list = new ArrayList<>();
try(br){
String line;
while ((line = br.readLine()) != null) {
list.add(line);
}
}
System.out.println(list); // [abc你好, 曹潇潇我爱你]
}
}
---- 读取文件:指定编码格式
public class Test {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(".\\gbk.txt",Charset.forName("GBK")));
StringBuilder sb = new StringBuilder();
try(br){
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
}
System.out.println(sb.toString());
}
}
(2)BufferedWriter: 字符缓冲输出流
构造方法
---- BufferedWriter(Writer out):使用默认缓冲区大小(8192字符数字)创建一个缓冲字符输出流
---- BufferedWriter(Writer out, int size):创建一个具有指定缓冲区大小的缓冲字符输出流
写入方法
---- void write(int c):一个一个写入,将指定的字符写入到缓冲区。
---- void write(char[] cbuf):多个多个写入
---- void write(char[] cbuf, int off, int len):多个多个写入
将字符数组的 len 个字符,从偏移量 off 开始,写入到缓冲区。
----- void write(String s):直接写入字符串
----- void write(String s, int off, int len):直接写入字符串
将字符串的 len 个字符,从偏移量 off 开始,写入到缓冲区。
----- void newLine():写入一个跨平台换行符
写入一个行分隔符。BufferedWriter 会根据操作系统自动使用适当的换行符,例如 Windows 使用 \r\n,Unix 使用 \n。
原因是,不可能每换个平台我们都去修改源码,我们调用这个方法写入就可以解决这个问题了。
关闭资源:void close()
代码演示
---- 写入文件(一个一个字符写入void write(int c) (超笨)):覆盖写入
public class Test {
public static void main(String[] args) throws IOException {
// 往a.txt中覆盖写入 abc我 a -> 97 b -> 98 c -> 99 我 -> 25105
BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
try(bw) {
bw.write(97);
bw.write(98);
bw.write(99);
bw.write(25105);
}
}
}