流
Java IO流是既可以从中读取,也可以写入到其中的数据流。
流和数组不一样,不能通过索引读写数据。在流中,你也不能像数组那样前后移动读取数据,除非使用RandomAccessFile 处理文件。流仅仅只是一个连续的数据流。
Java IO流通常是基于字节或者基于字符的。字节流通常以“stream”命名,比如InputStream和OutputStream。除了DataInputStream 和DataOutputStream 还能够读写int, long, float和double类型的值以外,其他流在一个操作时间内只能读取或者写入一个原始字节。
字符流通常以“Reader”或者“Writer”命名。字符流能够读写字符(比如Latin1或者Unicode字符)。
1.1 InputStream
java.io.InputStream类是所有Java IO输入流的基类。如果你正在开发一个从流中读取数据的组件,请尝试用InputStream替代任何它的子类(比如FileInputStream)进行开发。这么做能够让你的代码兼容任何类型而非某种确定类型的输入流。
通常使用输入流中的read()方法读取数据。read()方法返回一个整数,代表了读取到的字节的内容(译者注:0 ~ 255)。当达到流末尾没有更多数据可以读取的时候,read()方法返回-1。
InputStream input = new FileInputStream("c:\\data\\input-file.txt");
int data = input.read();
while(data != -1){
data = input.read();
}
1.2 OutputStream
java.io.OutputStream是Java IO中所有输出流的基类。如果你正在开发一个能够将数据写入流中的组件,请尝试使用OutputStream替代它的所有子类。
OutputStream output = new FileOutputStream("c:\\data\\output-file.txt");
output.write("Hello World".getBytes());
output.close();
1.3 组合流
你可以将流整合起来以便实现更高级的输入和输出操作。比如,一次读取一个字节是很慢的,所以可以从磁盘中一次读取一大块数据,然后从读到的数据块中获取字节。为了实现缓冲,可以把InputStream包装到BufferedInputStream中
InputStream input = new BufferedInputStream(new FileInputStream("c:\\data\\input-file.txt"));
缓冲同样可以应用到OutputStream中。你可以实现将大块数据批量地写入到磁盘(或者相应的流)中,这个功能由BufferedOutputStream实现。
将不同的流整合到一个链中,可以实现更多种高级操作。通过编写包装了标准流的类,可以实现你想要的效果和过滤器。
Reader And Writer
Java IO的Reader和Writer除了基于字符之外,其他方面都与InputStream和OutputStream非常类似。他们被用于读写文本。InputStream和OutputStream是基于字节的。
2.1 Reader
Reader类是Java IO中所有Reader的基类。子类包括BufferedReader,PushbackReader,InputStreamReader,StringReader和其他Reader。
Reader reader = new FileReader("c:\\data\\myfile.txt");
int data = reader.read();
while(data != -1){
char dataChar = (char) data;
data = reader.read();
}
请注意,InputStream的read()方法返回一个字节,意味着这个返回值的范围在0到255之间(当达到流末尾时,返回-1),Reader的read()方法返回一个字符,意味着这个返回值的范围在0到65535之间(当达到流末尾时,同样返回-1)。这并不意味着Reade只会从数据源中一次读取2个字节,Reader会根据文本的编码,一次读取一个或者多个字节。
你通常会使用Reader的子类,而不会直接使用Reader。Reader的子类包括InputStreamReader,CharArrayReader,FileReader等等。
2.2 整合Reader与InputStream
一个Reader可以和一个InputStream相结合。如果你有一个InputStream输入流,并且想从其中读取字符,可以把这个InputStream包装到InputStreamReader中。把InputStream传递到InputStreamReader的构造函数中:
Reader reader = new InputStreamReader(inputStream);
2.3 Writer
Writer类是Java IO中所有Writer的基类。子类包括BufferedWriter和PrintWriter等等
Writer writer = new FileWriter("c:\\data\\file-output.txt");
writer.write("Hello World Writer");
writer.close();
同样,你最好使用Writer的子类,不需要直接使用Writer,因为子类的实现更加明确,更能表现你的意图。常用子类包括OutputStreamWriter,CharArrayWriter,FileWriter等。Writer的write(int c)方法,会将传入参数的低16位写入到Writer中,忽略高16位的数据。
2.4 整合Writer和OutputStream
与Reader和InputStream类似,一个Writer可以和一个OutputStream相结合。把OutputStream包装到OutputStreamWriter中,所有写入到OutputStreamWriter的字符都将会传递给OutputStream。这是一个OutputStreamWriter的例子:
Writer writer = new OutputStreamWriter(outputStream);
2.5 整合Reader和Writer
和字节流一样,Reader和Writer可以相互结合实现更多更有趣的IO,工作原理和把Reader与InputStream或者Writer与OutputStream相结合类似。举个栗子,可以通过将Reader包装到BufferedReader、Writer包装到BufferedWriter中实现缓冲。
Reader reader = new BufferedReader(new FileReader(...));
Writer writer = new BufferedWriter(new FileWriter(...));
Java IO: 异常处理
流与Reader和Writer在结束使用的时候,需要正确地关闭它们。通过调用close()方法可以达到这一点。不过这需要一些思考。请看下边的代码:
InputStream input = new FileInputStream("c:\\data\\input-text.txt");
int data = input.read();
while(data != -1) {
//do something with data...
doSomethingWithData(data);
data = input.read();
}
input.close();
第一眼看这段代码时,可能觉得没什么问题。可是如果在调用doSomethingWithData()方法时出现了异常,会发生什么呢?没错,这个InputStream对象就不会被关闭。
为了避免异常造成流无法被关闭,我们可以把代码重写成这样:
InputStream input = null;
try{
input = new FileInputStream("c:\\data\\input-text.txt");
int data = input.read();
while(data != -1) {
//do something with data...
doSomethingWithData(data);
data = input.read();
}
}catch(IOException e){
//do something with e... log, perhaps rethrow etc.
} finally {
try{
if(input != null)
input.close();
} catch(IOException e){
//do something, or ignore
}
}
注意到这里把InputStream的关闭代码放到了finally块中,无论在try-catch块中发生了什么,finally内的代码始终会被执行,所以这个InputStream总是会被关闭。
备注:文章参考