处理流之一:缓冲流

处理流,就是“套接”在已有的流的基础上。

为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类 时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区

当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区

当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从 文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中 读取下一个8192个字节数组。

缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:

  • BufferedInputStream 和 BufferedOutputStream
  • BufferedReader 和 BufferedWriter

向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法 flush()可以强制将缓冲区的内容全部写入输出流。

flush()方法的使用:手动将buffer中内容写入文件

上图中WEB服务器通过输出流向客户端响应了一个300字节的信息,但是,这时的输出流有一个1024字节的缓冲区。所以,输出流就一直等着WEB服务器继续向客户端响应信 息,当WEB服务器的响应信息把输出流中的缓冲区填满时,这时,输出流才向WEB客户端响应消息。

为了解决这种尴尬的局面,flush()方法出现了。flush()方法可以强迫输出流(或缓冲的流)发送数据,即使此时缓冲区还没有填满,以此来打破这种死锁的状态

当我们使用输出流发送数据时,当数据不能填满输出流的缓冲区时,这时,数据就会被存储在输出流的缓冲区中。如果,我们这个时候调用关闭(close)输出流,存储在输出流的缓冲区中的数据就会丢失。所以说,关闭(close)输出流时,应先刷新(flush)换冲的输出流,话句话说就是:“迫使所有缓冲的输出数据被写出到底层输出流中”。

关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,但关闭最外层流也 会相应关闭内层节点流*

如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出,流对象就不能在使用了。

BufferedInputStream和BufferedOutputStream

创建一个BufferedInputStream 并保存其参数输入流 in,以供以后使用。

1
public BufferedInputStream​(InputStream in)

创建BufferedInputStream 具有指定缓冲区大小的,并保存其参数输入流 in,以供以后使用。

1
public BufferedInputStream​(InputStream in, int size)

创建一个新的缓冲输出流,以将数据写入指定的基础输出流。

1
public BufferedOutputStream​(OutputStream out)

创建一个新的缓冲输出流,以指定的缓冲区大小将数据写入指定的基础输出流。

1
public BufferedOutputStream​(OutputStream out, int size)

BufferedReader和BufferedWriter

创建一个使用默认大小的输入缓冲区的缓冲字符输入流。

1
public BufferedReader​(Reader in)

创建使用指定大小的输入缓冲区的缓冲字符输入流。

1
public BufferedReader​(Reader in, int sz)

BufferedReader拥有readLine()方法,方法返回值为一个字符串,其中包含行的内容,不包含任何行终止符;如果在不读取任何字符的情况下到达了流的末尾,则返回null

创建一个使用默认大小的输出缓冲区的缓冲字符输出流。

1
public BufferedWriter​(Writer out)

创建一个使用给定大小的输出缓冲区的新的缓冲字符输出流。

1
public BufferedWriter​(Writer out, int sz)

BufferedWriter将文本写入字符输出流,缓冲字符,以便有效地写入单个字符,数组和字符串。可以指定缓冲区大小,也可以接受默认大小。对于大多数用途,默认值足够大。

提供了newLine()方法,该方法使用平台自己的由system属性定义的行分隔符概念line.separator。并非所有平台都使用换行符(’\ n’)来终止行。因此,调用此方法终止每条输出行比直接编写换行符更好。

image-20200516163025216

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import org.junit.Test;

import java.io.*;

/**
* 处理流之一:缓冲流的使用
* <p>
* 1.缓冲流:
* BufferedInputStream
* BufferedOutputStream
* BufferedReader
* BufferedWriter
* <p>
* 2.作用:提供流的读取、写入的速度
* 提高读写速度的原因:内部提供了一个缓冲区
* <p>
* 3. 处理流,就是“套接”在已有的流的基础上。
*/
public class BufferedTest {

//实现非文本文件的复制
@Test
public void BufferedStreamTest() {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;

try {
//1.造文件
File srcFile = new File("爱情与友情.jpg");
File descFile = new File("爱情与友情3.jpg");

//2.造流
//2.1造节点流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(descFile);
//2.2造缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);

//3.复制的细节:读写,写入
byte[] buffer = new byte[10];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
// bos.flush();//刷新缓冲区
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源关闭
//要求:先关闭外层的流,再关闭内存的流
//说明:关闭外层流的同时,内层流也会自动关闭。关于内层流的关闭,我们可以省略。
// fos.close();
// fis.close();
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

//实现文件复制的方法
public void copyFileWithBuffered(String srcPath, String destPath) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;

try {
//1.造文件
File srcFile = new File(srcPath);
File destFile = new File(destPath);
//2.造流
//2.1 造节点流
FileInputStream fis = new FileInputStream((srcFile));
FileOutputStream fos = new FileOutputStream(destFile);
//2.2 造缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);

//3.复制的细节:读取、写入
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源关闭
//要求:先关闭外层的流,再关闭内层的流
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}

}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}

}
//说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
// fos.close();
// fis.close();
}
}

@Test
public void testCopyFileWithBuffered() {
long start = System.currentTimeMillis();

String srcPath = "C:\\Data\\素材库\\视频\\48370019\\588\\48370019_588_0.flv";
String destPath = "视频.avi";


copyFileWithBuffered(srcPath, destPath);


long end = System.currentTimeMillis();

System.out.println("复制操作花费的时间为:" + (end - start));//67
}


//使用BufferedReader和BufferedWriter实现文本文件的复制

@Test
public void testBufferedReaderBufferedWriter() {
BufferedReader br = null;
BufferedWriter bw = null;
try {
//创建文件和相应的流
br = new BufferedReader(new FileReader(new File("dbcp.txt")));
bw = new BufferedWriter(new FileWriter(new File("dbcp1.txt")));

//读写操作
//方式一:使用char数组
/* char[] cbuf = new char[1024];
int len;
while ((len = br.read(cbuf)) != -1) {
bw.write(cbuf, 0, len);
}*/
//方式二:使用String
String data;
while ((data= br.readLine())!=null){
//方法一:
// bw.write(data+"\n");//data中不包含换行符
//方法二:
bw.write(data);
// bw.newLine();//提供换行的操作
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭资源
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
} if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}