目录
一、流的概念
流是对 I/O 操作的形象描述,水从一个地方转移到另一个地方就形成了水流,而信息从一处转移到另一处就叫做 I/O 流。
一个流,必有源端和目的端,它们可以是计算机内存的某些区域,也可以是磁盘文件,甚至可以是 Internet 上的某个 URL。对于流而言,不用关心数据是如何传输的,只需要从源端输入数据(读),向目的端输出数据(写)。
如何理解输入和输出呢?简单地说,你听别人唠叨就是输入,你向别人发牢骚就是输出。在计算机的世界中,输入 Input
和输出 Output
都是针对计算机的内存而言的。比如读取一个硬盘上的文件,对于内存就是输入;向控制台打印输出一句话,就是输出。Java 中对于此类输入/输出的操作统称为 I/O,即 Input/Output。
输入流的抽象表示形式是接口 InputStream
;输出流的抽象表示形式是接口 OutputStream
。
System.out.println();
就是一个典型的输出流,目的是将内存中的数据输出到控制台。而 new Scanner(System.in);
就是一个典型的输入流,目的是将控制台接收的信息输入到内存中。
按照处理数据的单位,流可以分为字节流和字符流。字节流的处理单位是字节,通常用来处理二进制文件,如音乐、图片文件等,并且由于字节是任何数据都支持的数据类型,因此字节流实际可以处理任意类型的数据。而字符流的处理单位是字符,因为 Java 采用 Unicode 编码,Java 字符流处理的即 Unicode 字符,所以在操作文字、国际化等方面,字符流具有优势。
二、字节流
输入字节流类继承自抽象类 InputStream
,输出字节流继承自抽象类 OutputStream
FileInputStream
:把一个文件作为输入源,从本地文件系统中读取数据字节,实现对文件的读取操作。ByteArrayInputStream
:把内存中的一个缓冲区作为输入源,从内存数组中读取数据字节。ObjectInputStream
:对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化,用于恢复那些以前序列化的对象,注意这个对象所属的类必须实现 Serializable 接口。PipedInputStream
:实现了管道的概念,从线程管道中读取数据字节。主要在线程中使用,用于两个线程间的通信。SequenceInputStream
:其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直至到达文件末尾,接着从第二个输入流读取,依次类推。System.in
:从用户控制台读取数据字节,在System
类中,in 是InputStream
类型的静态成员变量。
public class TestByteStream {
public static void main(String[] args) throws IOException {
FileInputStream in = null;
FileOutputStream out = null;
try {
File f = new File("/home/project/new_file.txt");
f.createNewFile();
//通过构造方法之一:String构造输入流
in = new FileInputStream("/home/project/ori_file.txt");
//通过构造方法之一:File类构造输出流
out = new FileOutputStream(f);
//通过逐个读取、存入字节,实现文件复制
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
上面的代码分别通过传入字符串和 File
类,创建了文件输入流和输出流,然后调用输入流类的 read()
方法从输入流读取字节,再调用输出流的 write()
方法写出字节,从而实现了复制文件内容的目的。注意:一是 read()
方法碰到数据流末尾时(即读取完毕时),返回值是 -1,否则返回值 > -1;二是在输入、输出流用完之后,要在异常处理的 finally
块中关闭输入、输出流,以释放资源。
输出流的 write()
方法是覆盖文件内容,而不是在文件内容后面追加内容。如果想采用追加的方式,则在使用构造方法创建字节输出流时,增加第二个值为 true 的参数即可,如 new FileOutputStream(f,true)
。
InputStream
输入流的可用方法
int read()
:从输入流中读取数据的下一字节,返回 0 ~ 255 范围内的整型字节值;如果输入流中已无新的数据,则返回 -1。int read(byte[] b)
:从输入流中读取一定数量的字节,并将其存储在字节数组 b 中,以整数形式返回实际读取的字节数(要么是字节数组的长度,要么小于字节数组的长度)。int read(byte[] b, int off, int len)
:将输入流中最多 len 个数据字节读入字节数组 b 中,以整数形式返回实际读取的字节数,off 指数组 b 中将写入数据的初始偏移量。void close()
:关闭此输入流,并释放与该流关联的所有系统资源。int available()
:返回可以不受阻塞地从此输入流读取(或跳过)的估计字节数。void mark(int readlimit)
:在此输入流中标记当前的位置。void reset()
:将此输入流重新定位到上次 mark 的位置。boolean markSupported()
:判断此输入流是否支持mark()
和reset()
方法。long skip(long n)
:跳过并丢弃此输入流中数据的 n 字节。