还是老规矩,转载的先说一下自己的感想。
其实通篇主要表达的思想就是:加了包装类(BufferedInputStream
)之后,程序是先把硬盘中的文件读取到内存的缓冲区。然后再由程序从内存中读取。否则不加这一层缓冲的话,程序会分块的从存储介质中分批读取文件,那样速度就慢了很多。
因为和输入流与之对应的还有输出流(即OutputStream),在此只针对输入流InputStream讨论。
1. 家族老大:
一说起家族中的老大,InputStream
自然是当仁不让,在java的输入流操作的类中,衍生出的基本子类有,可以理解为这些都是InputStream
它的孩子(子类):
InputStream
作为所有输入流中的超类,包含从输入流读取数据的基本方法,所有的具体类都包含了这些方法,例如read()
方法,它将读取一个字节并将其以int
类型返回,当到达输入流的结尾时,返回-1
,因此我们常常这样操作:
byte data;
while ((data = (byte) bis.read()) != -1) {
// 当没有达到输入流的结尾时,继续读取并打印转换成char类型的字符:
System.out.print((char) data);
}
这段代码不了解不要紧,现在我们开始正式介绍InputStream家族。
2. 家族孩子:
家族中的子类各司其职,老大FileInputStream
处理文件流,老二ByteArrayInputStream
处理字节数组流...
例如,我们来看下老大是怎么工作的:
import java.io.*;
import java.util.Date;
public class FileInputStreamTest {
public static void main(String[] args) throws Exception{
try {
// 创建FileInputStream对象:
FileInputStream fis = new FileInputStream("E:\\text.txt");
// 得到起始时间:
long start = System.currentTimeMillis( );
byte byteData;
while ((byteData = (byte) fis.read()) != -1) {
System.out.print((char) byteData);
}
// 得到读取后的结束时间:
long end = System.currentTimeMillis( );
// 计算出读取的时间:
long diff = end - start;
System.out.println("读取的时间时间共:" + diff);
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
工作完成后,我们可以得到老大干完活的花费的时间为:1097
,但是家族老大嫌这家伙干活太慢了。老大无奈,叫来了它的弟弟老二BufferedInputStream
,俗话说啊,兄弟齐心,其利断金。果不其然,兄弟俩一块干活效率果然加快了不少:
在如下的代码中我们可以看到,通过将FileInputStream
放到BufferedInputStream
中的构造方法中去创建BufferedInputStream
对象,这样FileInputStream
也就具有了缓存的输入流功能。
public class BufInputStream {
public static void main(String[] args) throws Exception{
try {
// 通过缓冲区数据向输入流添加功能,维护一个内部缓冲区以存储从底层输入流读取的字节:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\text.txt"));
long start = System.currentTimeMillis( );
byte byteData;
while ((byteData = (byte) bis.read()) != -1) {
System.out.print((char) byteData);
}
long end = System.currentTimeMillis( );
long diff = end - start;
System.out.println("读取的时间时间共:" + diff);
bis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
现在我们来看下兄弟俩一块工作花费的时间吧:281
。嘿!果然快了不少!看来增加缓冲流的功能的效果的确十分明显。
这下老二在家族的名声增大了不少,许多的兄弟都找他搭档干活了。
3. 总结:
通过上文的假设,我们可以明白了在io-InputStream
的家族中,采用了装饰者的设计模式,即不改变原类文件和使用继承的前提下,动态地扩展一个对象的功能(比如说这里的支持缓冲流),通过创建一个包装对象(这里的BufferedInputStream
),也就是装饰包裹真实的对象。
例如我们还可以也ByteArrayInputStream
中修饰PushbackInputStream
类:
PushbackInputStream pbi = new PushbackInputStream (new ByteArrayInputStream(b));
ByteArrayInputStream
是处理字节数组流;PushbackInputStream
是向输入流中添加功能,允许使用unread()
方法推回读取的字节。这样我们就可以使用pbi
对象处理字节数组,还具有推回读取字节的功能了:
import java.io.*;
public class BAStreamAndPBStream {
public static void main(String[] args) throws IOException{
byte[] b = {1,2,3};
PushbackInputStream pbi = new PushbackInputStream (new ByteArrayInputStream(b));
int result;
while ((result = pbi.read()) != -1) {
System.out.print(result + " ");
pbi.unread(result);
pbi.read();
System.out.print(result + " ");
}
pbi.close();
}
}
// 运行结果为:
// 1 1 2 2 3 3