流的概念:
由于现今社会中数据资源的类型五花八门,如网络,内存,硬盘,以及各种输入输出设备等
因此,如果想要通过一种统一的方式来从各种数据资源中读取或写入数据的话,就需要用到流
用术语说,流是字节序列的抽象概念,简单理解的话,流就是在各种各样不同的数据资源之间传递的数据
流与文件有着本质上的不同,文件是数据静态存储时的形态,而流是一种数据在传输时的形态
直接操作目标的流称为节点流,对节点流进行操作的流称为过滤流
InputStream与OutputStream类:
InputStream是对所有字节输入流的统称,也是所有字节输入流的父类
InputStream的常用方法:
read():返回值类型为int,若该方法返回值大于0,则继续读取,若该方法返回值为-1,则停止读取,若该方法未读取到数据,则阻塞
read(byte [] b):返回值类型为int,表示实际读取到字节的个数,无论返回值为多少,均停止读取,若该方法未读取到数据,则阻塞
skip(long n):跳过n个字符,返回值类型为long,表示实际跳过的字符数
available():返回值类型为int,表示当前输入流中实际可读的字节数
mark(int readlimit):在当前位置建立一个标记,并且从标记位置开始,最多还能够继续读取多少个字节
reset():与mark方法成对使用,当调用reset方法时,立即使指针回到标记的位置
close():关闭当前输入流
*Tips:为什么要通过手动调用close()方法来关闭流?
由于流可以在各种各样的数据资源之间传递数据,因此就可能会产生无数种不同的情况
所以在什么时候才应当关闭这个流,在不同的情况下也会不一样,因此只能通过手动的方式来实现对流的关闭
close()方法就是一个统一的,从各种不同的数据资源之间将流关闭的方法
OutputStream是对所有字节输出流的统称,也是所有字节输出流的父类
在OutputStream中主动将缓冲区中的数据存入IO设备中的方法:
flush(); //将OutputStream缓冲区中的数据全部存入IO设备中
close(); //将OutputStream缓冲区中的数据全部存入IO设备中,并关闭该OutputStream
什么是内存缓冲区:
当OutputStream需要向外部IO设备写入数据的时候,可以首先将需要数据写入内存中的一个指定大小的字节数组中,只有当该字节数组被写满时,才将字节数组中的数据一次性地写入外部IO设备中。这个在内存中的字节数组就是缓冲区。
为什么要使用内存缓冲区:
1、由于计算机访问内存的速度比计算机访问外部IO设备的速度快的多,因此使用内存缓冲区可以有效降低计算机对硬盘读写的次数
2、由于数据在存储在内存中时是没有持久化的,因此对缓冲区中数据的修改会比较方便
*当调用write(byte [] bytes)方法写入数据的时候,该方法会自动调用flush()方法,但是write(byte byte)不会自动调用flush()方法
FileInputStream与FileOutputStream类:
与InputStream和OutputStream的关系:
FileInputStream和FileOutputStream是InputStream和OutputStream的子类
FileInputStream和FileOutputStream主要用于对磁盘上的文件来进行读写
FileInputStream和FileOutputStream的构造方法有两种:
一种是向构造方法的参数中传入String类型的文件路径,一种是向构造方法的参数中传入File类型的对象
*创建FileINputStream对象时,指定的文件应当是存在并且可读的
创建FileOutputStream对象时,指定的文件如果已存在,则会首先覆盖清除掉文件中原来的内容
Reader与Writer类:
与InputStream和OutputStream的关系:
InputStream和OutputStream是字节流,Reader和Writer是字符流
Reader是对所有字符输入流的统称,也是所有字符输入流的父类
Writer是对所有字符输入流的统称,也是所有字符输入流的父类
Reader与Writer是专门用于对文本文件进行读写的流
*二进制文件与文本文件的区别:
文件中的数据,是从内存中的数据写入到硬盘中所形成的
因此,文件中的数据实际上都是由无数个二进制数据所组成的
由于每个字节是由8个二进制数据所组成的,而字符则是由两个字节所组成的
因此每个字节最多只能保存0~255这个区间内所表示的数据,但字符可以表示的内容就广的多
因此,中文等文字通常就是使用字符来保存的。这里使用字符来保存的文字,就可以统称为文本
如果一个文件中全部由文本所组成,那么这个文件就可以称之为文本文件
只要这个文件中包含文本以外的字节,则这个文件就应该被称之为二进制文件
PipedInputStream与PipedOutputStream类:
与InputStream和OutputStream的关系:
PipedInputStream和PipedOutputStream是InputStream和OutputStream的子类
PipedInputStream和PipedOutputStream主要用于在两个线程之间进行通信
PipedInputStream和PipedOutputStream通常是成对使用的
首先由线程A创建一个PipedOutputStream,然后由线程B创建一个PipedInputStream,然后使这个两个流对接
这样线程B就可以通过这个流从线程A中读取数据了
如何实现PipedInputStream和PipedOutputStream的对接:
1、在线程A内创建一个私有变量PipedOutputStream,并提供该PipedOutputStream的get方法
2、在线程B内创建一个私有变量PipedInputStream,并提供该PipedInputStream的get方法
3、创建一个main方法,同时创建线程A对象和线程B对象,然后获取其中的PipedOutputStream和PipedInputStream
4、调用PipedOutputStream的connect方法,并向该方法中传入PipedInputStream来进行连接
*此处也可以调用PipedInputStream中的connect方法来连接PipedOutputStream,效果是一样的
注意:正确使用管道流类,可以实现各个程序模块之间的松耦合通信!
ByteArrayInputStream与ByteArrayOutputStream类:
与InputStream和OutputStream的关系:
ByteArrayInputStream和ByteArrayOutputStream是InputStream和OutputStream的子类
ByteArrayInputStream和ByteArrayOutputStream主要用于对内存中虚拟的文件对象来进行读写
内存中虚拟的文件对象可以理解为在内存中保存的一个字节数组,这样程序对文件的读操作和写操作就可以针对这个字节数组来进行
由于在读写的过程中不需要访问硬盘,因此这样就可以有效地提高程序运行的效率
ByteArrayInputStream的构造方法有两种:
ByteArrayInputStream(byte [] buf); 使用一个字节数组中的所有数据作为数据源(就好比读取一个文件)
ByteArrayInputStream(byte [] buf, int offset, int length): 使用一个字节数组部分数据作为数据源
ByteArrayOutputStream的构造方法有两种:
ByteArrayOutputStream(); 默认创建一个32个字节大小的缓冲区
ByteArrayOutputStream(int size); 创建一个指定字节大小的缓冲区
*无论以哪种形式创建ByteArrayOutputStream,缓冲区的大小都会自动增长
为什么要使用ByteArray流:
如果在网络传输过程中需要对传输的数据进行压缩或加密
那么如果使用File流的话,就必须要先在硬盘上创建需要传输的文件,再通过FileInputStream从文件中读入数据,再对文件进行压缩或加密,再通过FileOutputStream将处理后的数据写入文件,再将文件发送出去,最后再将文件删除,这样由于要多次读写硬盘,因此效率就会非常的低
而使用ByteArray流的话,则只需要先将数据保存到内存中,然后通过ByteArrayInputStream从内存中读入数据,然后再对数据进行处理,最后通过ByteArrayOutputStream将数据发送出去就可以了,这样由于全程都不需要读写硬盘,因此效率就会大大提高
重视代码的可复用性:
在定义方法时,方法的参数最好定义为实际传入对象的的父类或实现的接口,这样可以有效地提高代码的可复用性
*System.in连接到键盘,是InputStream的实例对象,用于从键盘读入数据
System.out连接到显示器,是OutputStream的实例对象,用于向显示器输出数据
*不管底层物理设备使用什么样的方式来实现读取的终止,read()方法永远使用返回值-1来表示读取的结束
*在Windows环境下,使用Ctrl+Z来读取终止的标记。在linux环境下,使用Ctrl+D来产生读取终止的标记