输入与输出-2.1输入/输出流

2.1 输入/输出流

在JAVA API中,可以从其中读入一个字节序列的对象称做输入流,而可以向其中写入一个字节序列的对象称作输出流。抽象类InputStream和OutputStream构成了输入/输出(I/O)类层次结构的基础。

  • 注意:这些输入/输出流与前面看到的流没有任何关系。

2.1.1 读写字节

InputStream类有一个抽象方法:
abstract int read()
这个方法将读入一个字节,并返回读入的字节,遇到输入源结尾时返回-1。在设计具体输入流类时,必须覆盖这个方法以提供适用的功能。例如,在FileInputStream类中,这个方法将从某个文件中读入一个字节,而System.in(InputStream的一个子类的预定义对象)却是从“标准输入”中读入信息,即控制台或重定向的文件。

InputStream类还有若干个非抽象的的方法,他们可以读入一个字节数组,或者跳过大量的字节。这些方法都要调用抽象的read方法,因此,各个子类都只需要覆盖这一个方法。

与此类似,OutputStream类定义了下面的抽象方法:
abstract voidwrite(int b)
他可以向某个输出位置写出一个字节。

read和write方法在执行时都将阻塞,直至字节确实被读入或写出。这意味着如果流不能被立即访问,那么当前的线程将被阻塞。这使得在这两个方法等待指定的流变为可用的这段时间里,其他的线程就有机会去执行有用的工作。

available方法使我们可以去检查当前可读入的字节数量,这意味着像下面这样的代码片段不可能被阻塞:

int bytesAvailable = in.available();
if( bytesAvailable>0)
{
	byte[] data = new byte[bytesAvailable];
	in.read(data);
	}

当你完成输入/输出流的读写时,应该通过调用close方法来关闭它,这个调用会释放掉十分有限的操作系统资源。关闭一个输出流的同时还会冲刷用于该输出流的缓冲区:所有被临时置于缓冲区中,以便用更大的包的形式传递字节在关闭输出流是都将被送出。如果不关闭文件,那么写出字节的最后一个包可能将永远也得不到传递。也可以用flush方法来人为地冲刷这些输出。

2.1.2 完整的流家族

让我们把输入输出流家族中的成员按照他们的使用方法来进行划分,这样就形成了处理字节和字符的两个单独层次结构。InputStream和OutputStream类可以读写单个字节或字节数组。DataInputStream和DataOutputStream可以以二进制格式读写所有的基本JAVA类型。ZipInputStream和ZipOutputStream可以以常见的ZIP压缩格式读写文件。

另一方面,对于Unicode文本,可以使用抽象类Reader和Writer的子类。read方法将返回一个Unicode码元,或者在碰到文件结尾时返回-1。

还有四个附加的接口:Closeable、Flushable、Readable、Appendable。前面两个接口非常简单,他们分别拥有下面的方法:

void close()throw IOException
void flush()

而OutputStream和Write还实现了Flushable接口。
Readabe接口只有一个方法:

扫描二维码关注公众号,回复: 4252299 查看本文章
int read(CharBuffer cb)

CharBuffer类拥有按顺序和随机地进行读写访问的方法 ,他可以表示一个内存中的缓冲区或者一个内存映像的文件。

Appendable接口有两个用于添加单个字符的字符序列的方法:

Appendable append(char c)
Appendable append(CharSequence s)

CharSequence接口描述了一个char值序列的基本属性,String、CharBuffer、StringBuilder和StringBuffer都实现了它。

2.1.3 组合输入/输出流过滤器

FileInputStream和FileOutputStream可以提供附着在一个磁盘文件上的输入流和输出流,而你只需向其构造器提供文件名或文件的完整路径名。例如:

FileInputStream fin = new FileInputStream("employee.dat");

这行代码可以查看在用户目录下名为“employee.dat”的文件。

这些类只支持在字节级别上的读写。也就是说,我们只能从fin对象中读入字节和字节数组。

byte b = (byte)fin.read();

如果我们只有DataInputStream,那么我们就只能读入数值类型:

DataInputStream din = ...;
double x = din.readDouble();

正如FileInputStream没有任何读入数值类型的方法一样,DataInputStream也没有任何从文件中获取数据的方法。

java使用了一种灵巧的机制来分离这两种职责。某些输入流(如FileInputStream和由URL类的openStream方法返回的输入流)可以将字节组装到更有用的数据类型中。java程序员必须对二者进行结合。
l例如,为了从文件中读入数字,首先需要创建一个FileInputStream,然后将其传递给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提供了此功能。

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"))));

当然,在其他编程语言的输入/输出流类库中诸如缓冲机制和预览等细节都是自动处理的。因此,相比较而言,Java就有些麻烦,他必须将多个流过滤器组合起来。但是这种混合并匹配过滤器以构建真正有用的输入/输出流序列的能力,将带来极大的灵活性,例如,你可以从一个ZIP压缩文件中通过使用下面的输入流序列来读入数字:

ZipInputStream zin = new ZipInputStream(new FileInputStream("employee.zip");
DataInputStream din = new DataInputStream(zin);

如有问题,评论处指正Thanks♪(・ω・)ノ

猜你喜欢

转载自blog.csdn.net/z036548/article/details/84292836
今日推荐