流是能统一描述所有常见输入输出类型的模型,包括文件、键盘、显示器、磁带等等,因此使用非常广泛,统一使用流的模型描述之后,相同的程序就可以通过输入输出重定向来处理不同的对象,这其实是多态的思想。后来网络出现之后,网络本来是基于分组交换的,但为了能让网络IO也适用流的模型,设计了TCP协议,从此网络协议也大部分都会使用流的模型来描述
假设我们有个大水缸,水缸里灌满了水,为了能够让水从大水缸里拿出来使用,就需要接一个水龙头,打开水龙头,水就出来了。
可是大水缸除了放水,我们还希望能不断的蓄水,于是就需要有另外一个口可以给水缸蓄水,蓄水口默认是关闭的,每次都要打开蓄水口。
假设我们往大水缸里蓄水,水有N杯,我们只能一杯一杯的往里灌,每次灌入就要打开蓄水口,
为了提高蓄水速度,我们想到了一个办法,准备一个盆,每个盆只能装512杯水,当盆装满后,再从盆里往水缸里灌入。
水就相当于计算机里的数据,而数据是有顺序而且以字节方式存在的,出水口和入水口对应输入输出流,stdin和stdout,而后面加的盆就是内部缓冲区,大水缸就是我们的磁盘,磁盘操作相对于cpu的处理速度来说非常慢,所以为了提高效率,引入了内部缓冲区。
昨天刚好想到了这个概念,搜到了这个话题,然后我也结合我自己的理解,回答一波。
其实楼上的已经回答的很好了,也给了我启发,我这里再结合代码稍作修饰。
try {
FileInputStream fileInputStream = new FileInputStream(f);//这是左边的水缸
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();//这是右边的水缸
byte[] buffer = new byte[1000]; //准备一个瓢
int readCount = 0;
while ((readCount = fileInputStream.read(buffer)) != -1) { //一直舀
byteArrayOutputStream.write(buffer, 0, readCount);
}
fileInputStream.close(); //关掉出水水龙头
byteArrayOutputStream.close(); //关掉进水口
return byteArrayOutputStream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
这是一段很典型的Java从文件中读取字节流的代码,注释已经很形象地解释了整个过程。
现在假设有两个水缸在你前面,一个位于左边,一个位于右边。左边的水缸在计算机里可以是一个文件,也可以是键盘的输入等输入设备。右边的水缸可以是一个文件,也可以是一块内存或者屏幕等输出设备。
现在你要把一个文件从一个地方挪到另一个地方,比如从C盘挪到D盘,或者是单纯地从磁盘上读取到内存里。你要把左边水缸里的水挪到右边的水缸里去,这时候你怎么办呢?你手上没有任何工具,除了一个瓢!
然后你拿着一个只能放1000个字节大小的瓢,要把水缸的水挪出去,你懒得动手去舀,你惊奇地发现左边的水缸上有一个水龙头,右边的水缸有一个进水通道,你拧开水龙头,水就自动流出来了,而且更神奇的是你把瓢放到水龙头底下,水就流出来,你一拿开,水就停住了,于是你等瓢满了就从右边水缸的进水口灌进去,于是你一直这样操作,循环反复,直到把水放完,最后的一次可能只有半瓢水,这时候你明白,再把瓢放到水龙头底下也不可能再有水流出来了,于是你拧紧了水龙头,关掉了它。再把右边的进水口也挡住了。
你成功地把左边水缸的水挪到了右边。
“流是与外围设备关联的数据的源和目的地。”
把数据源和数据目的地以及从源到目的地的过程,这三个部分作为一个整体看待就是“流”,通俗的描述就是:数据 从源“流动”到目的地。
打开一个“流”意味着在源和目的地之间建立起一条通路(数据传输方式),数据便沿着这条这条路传输