抽象类InputStream
和OutputStream
构成了输出/输出(I/O)类层次结构的基础。
读写字节
InputStream
类有一个抽象方法:
abstract int read()
这个方法将读入一个字节,并返回读入的字节,或者在遇到输入源结尾时返回-1。
在设计具体的输入流类时,必须覆盖这个方法以提供适用的功能。
InputStream
类还有若干非抽象的方法,可以读入一个字节数组,或者跳过大量的字节。这些方法都要调用抽象的read
方法,因此,各个子类都只需覆盖这个方法。
public abstract class InputStream implements Closeable {
public abstract int read() throws IOException;
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
public long skip(long n) throws IOException {
long remaining = n;
int nr;
if (n <= 0) {
return 0;
}
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
byte[] skipBuffer = new byte[size];
while (remaining > 0) {
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
if (nr < 0) {
break;
}
remaining -= nr;
}
return n - remaining;
}
}
与此类似,OutputStream
类定义了下面的抽象方法:
abstract void write(int b);
它可以向某个输出位置写入一个字节。
read
和write
方法在执行时都将阻塞,知道字节确实被读入或写出。
```available`方法检查当前可读入的字节数量,下面的代码不可能被阻塞:
int bytesAvaliable = in.available();
if (bytesAvailable >0)
{
byte[] data = new byte[bytesAvailable];
in.read(data);
}
当完成对输入/输出流的读写时,应该通过调用close
方法来关闭它。
- 这个调用会释放操作系统资源
- 还会冲刷用于该输出流的缓冲区
如果不关闭文件,写出字节的最后一个包可能得不到传递。可以使用flush
方法来认为地冲刷这些输出。
完整的流家族
流家族按照使用方法划分,分为处理字节和字符的两个单独层次结构。
InputStream
和OutputStream
可以读写单个字节或字节数组,构成层级结构的基础。DataInputStream
和DataOutputStream
可以以二进制格式读写所有基本Java类型,提供读写字符串和数字更强大的功能。ZipInputStream
和ZipOutputStream
等包含了很多有用的功能。
对于Unicode文本,可以使用抽象类Reader
和Writer
的子类。
Reader
的read
方法将返回一个Unicode码元(一个在0~65535之间的整数),或者在碰到文件结尾时返回-1。
Writer
的write
方法写入一个Unicode码元。
还有4各附加接口:Closeable
,Flushable
,Readable
,Appenable
。
Closeable
接口:
void close() throw IOException
Flushable
接口:
void flush()
Readable
接口:
int read(CharBuffer cb)
Appendable
接口
Appendable append(Char c)
Appendable append(CharSequence s)
CharBuffer
表示一个内存中的缓冲区或者一个内存映像的文件。
CharSequence
接口描述了一个char
值序列的基本属性
组合输入/输出过滤器
FileInputStream
和FileOutputStream
可以提供附着在一个磁盘文件上的输入流和输出流。
FileInputStream fin = new FileInputStream("employee.dat")
所有在java.io
中的类都将相对路基那个解释为以用户工作目录开始,可以调用System.getProperty("user.dir")
类获取
DataInputStream
可以读入数值类型,但没有任何从文件中获取数据的方法。
DataInputStream din = ...;
double x = din.readDouble();
Java使用组合的方式分类这种指责:
- 某些输入流(例如
FileInputStream
和由URL类的openStream
方法返回的输入流)可以从文件和其他更外部的位置上获取字节 - 其他的输入流(例如
DataInputStream
)可以将字节组装到更有用的数据类型中
必须对二者进行组合。例如:
FileInputStream fin = new FileInputStream("employee.dat");
DataInputStream din = new DataInputStream(fin);
double x = din.readDouble();
FilterInputStream
和FilterOutputStream
的子类用于向处理字节的输入/输出流添加额外的功能。
输入流在默认下是不被缓冲区缓存的,每个对read
的调用都会请求操作系统再分发一个字节。相比下,请求一个数据块并将其置于缓冲区中会更加高效。
要使用缓冲机制,以及用于文件的数据输入方法,就使用下面的构造器序列:
DataInputStream din = new DataInputStream(
new BufferedInputStream(
new FileInputStream("employee.dat");
把DataInputStream
置于构造器的最后,是希望使用DataInputStream
的方法,并希望能使用带缓冲机制的read
方法。
当读入输入时,经常需要预览下一个字节,以了解它是否是想要的值。
PushbackInputStream pbin = new PushbackInputStream(
new BufferedInputStream(
new FileInputStream("employee.dat");
可以预读下一个字节:
int b = pbin.read();
并且在它并发是所期望的值时将其推回流中:
if (b != '<') pbin.unread(b);
如果希望能预先浏览并且还可以读入数字,那么需要一个既是可回退输入流又是一个数据输入流的引用:
DataInputStream din = new DataInputStream(
pbin = new PushbackInputStream(
new BufferedInputStream(
new FileInputStream("employee.dat");