java 的I/O建立于流(stream)之上。输入流读取数据;输出流写入数据。不同的流类读写某一种数据源。但是。所有的输出流都有相同的基本方法来写入数据,所有的输入流都有相同的基本方法来读取数据。
过滤器(filter)流可以链接到输入流或输出流上。过滤器可以在读写数据时修改数据(例如,通过加密或者压缩) 或者提供额外的方法,将读写的数据转换为其他的格式。例如,java.io.DAtaOutputStream类就提供了一个方法,可以将 int 转化为一个字节,并且写入底层的输出流中。
阅读器(reader)和书写器(write)可以链接到输入流和输出流上,允许读写文本(即字符)而不是字节。只要正确地使用,阅读器和书写器可以处理很多字符编码,包括多字符集。
Java的基本输出流的类都是 java.io.OutputStream : public abstract class OutputStream
写入数据的基本方法
public abstract void write (int b) throws IOException
public void write ( byte[ ] data ) throws IOException
public void write ( byte[ ] date , int offset , int length ) throws IOEception
public void flush ( ) throws IOException
public void close ( ) throws IOException
OutputStream的子类使用这些方法向某种媒体写入数据。如,FileOutputStream使用这些方法将数据写入文件。Tel恩特Out普通Stream使用这些方法将数据写入网络连接。ByteArrayOutputStream使用这些方法写入扩展的字节组。当不知道正在写入的流是具体是何种类型,在方法的声明时只返回OutputStream,而不是具体的子类。这就是多态。
OutputStream 的基本方法是write(int b)。这个方法接受一个0到255之间的整数作为参数,将对应的字节写入到输出流中。虽然这个方法接受一个int作为参数,但它的实际上写入一个无法好字节。java没有无符号字节数据类型,所以这里要使用 int 来代替。无符号字节和有符号字节之间唯一区别的在于解释。他们都由8个位组成,当使用write ( int b ) 将 int 写入网络连接时,只有8个数据位放在线缆上。如果0 ~ 255范围之外的 int 传递给 write(int),将写入此数字的最低字节,其他3个字节将被忽略(这是讲 int 转换为 byte的结果)。
流可以在软件中得到缓冲,即在Java代码中缓存。这可以通过把BufferedOutputStream 或者 BufferedWrite链接到底层上的流来实现。因此,在写入数据完成后,刷新(flush)输出流是重要,否则,数据可能丢失。最后,当结束一个流的操作是要通过其close( ) 方法将其关闭。这会释放与这个流的关联的所有操作。一旦关闭输出流关闭,进一步的写入就会抛出异常IOException异常。但是,有些流人会允许该对象进行一些操作。例如,关闭的ByteArrayOutputStream仍可以转换为实际的字节数组,关闭的DigestOutputStream仍然可以放回摘要。
java的基本输入流类是java.io.InputStream : public abstract class InputStream
这个类提供了将数据读取为原始字节所需的基本方法
public abstract int read ( ) throws IOException
public int read ( byte[ ] input ) throws IOException
public int read ( byte[ ] input , int offset , int length ) throws IOException
public long skip ( long n ) throws IOException
public int available ( ) throws IOException
public void close ( ) throws IOException
InputStream的基本方法是没有参数的read( )方法。这个方法从输入流的原中读取一个字节数据,作为一个0到255的int返回。流的结束由返回-1表示。read( ) 方法会等待并且阻塞其后任何代码的执行,直到有一字节的数据可用,准备读取。输入和输出可能很慢,所以如果程勋在进行其他重要的操作,请尝试将I/O 放在自己的线程中。
下面的代码段从InputStream in中读取10个字节,存储在byte数组的input中。如果检测到流结束。循环会会提前终止:
byte[ ] input = new byte[10] ;
for ( int i = 0 ; i < input.length ; i ++ ; ) {
int b = in.read( ) ;
if ( b == -1 ) break ;
//将有符号的字节转化为无符号的字节
if ( b>= 0 ) b = 256 + b ;
input[ i ] = ( byte ) b ;
有两个重载的read( ) 方法,可以用从流读取的多个字节数据来填充数组:read( byte[ ] input ) 和 read ( byte[ ] input , int offset, int length ). 第一钟尝试填充指定的数组input。第二种尝试填充指定的input中从offset开始连续length个字节的字数组。
以上方法是在尝试的填充数组,不一定成功。一次尝试可能会失败,如程序正在通过PPP拨号链接从远程Web服务器读取数据四,电话公司中心的交换机bug会切断程序的链接,导致一个IOException异常。还有一种情况是不完全失败,如当尝试从网络连接中读取1024个字节,这是实际只有512个来自服务器的字节到达,其他还在传输中,但此时却不可用。
byte[ ] input = new byte[1025] ;
int bytesRead = in.read( input ) ;
以上代码尝试从InputStream in 中读取1024字节,写入数组input中。但是,如果只有512字节可用,那么久会读取这么多,则bytesRead将会设置为512。为保证所希望的所有数据能实际读取到,要将读取放在循环中进行重复,直到数组填满为止。如:
int bytesRead = 0 ;
int bytesToRead = 1024 ;
byte[ ] input = new byte[bytesToRead] ;
while ( bytesRead <bytesToRead ) {
bytesRead + = in.read( input , bytesRead , bytesToRead - bytesRead );
}
这项技术对于网络流尤为重要。可能一个文件完全可用,那么文件的所有字节也都可用。但是,由于网络要比CPU慢的多,所以程序很容在所有数据到达前清空网络缓冲区。事实上,如果这两方法尝试读取暂时但打开的网路缓冲区,它一般都返回0,表示没有数据可用,但流还没有关闭,这通常比单字节的read( ) 方法要好,单字节方法会阻塞同一环境下运行的线程。
任何一个read( ) 方法的调用会返回-1,。-1 不会放在数组中,数组中只包含实际的数据。前面的代码段存在一个bug,因为没有考虑所有1024字节不会到达的可能性。修复此bug,需要添加到bytesRead之前测试read( ) 的返回值。如:
int bytesRead = 0 ;
int bytesToRead = 1024 ;
byte[ ] input = new byte[bytesToRead] ;
while ( bytesRead <bytesToRead ) {
int result = in.read( input , bytesRead , bytesToRead - bytesRead );
if ( result == -1 ) break ;
bytesRead += result ;
}
如果不想等待所有所需的字节都立即可用,可以使用available( )方法,在不阻塞的情况下确定有多少字节可以读取。它会返回可以读取的最少字节数。事实上能够读取的更多,但至少可以读取available建议的那么多字节。如:
int bytesAvailable = in.available( ) ;
byte[] input = new byte[bytesAvailable];
int bytesRead = in.read( input , 0 , bytesAvailable ) ;
//程序其他部分
这种情况下,可以断言bytesRead与bytesAvailable相等。但是,不能断言bytesRead大于0,即没有可用的字节。在流的最后, available( ),会返回0,。一般来说,read( byte[ ] input , int offset , int length )在流结束时返回-1,;但是如果length是0,那么它不会注意留的结束,而返回0。
一旦结束输入流的操作,应当调用其close( ) 方法将其关闭。
InputStream 类有3个不太常用的方法,有序程序备份和重载已经读取的数。方法如下:
public void mark( int readAheadLimit )
public void reset( ) throws IOException
public boolean markSupported ( )
为了重新读取数据,要用mark( ) 方法来标记流的当前记录。在后面可以用reset( ) 把流重置到标记的位置。从标记读取和重置的字节数取决于mark( ) 的 readAheadLimit参数。如果试图标记太远,就会抛出IOException异常。此外,一个流在任何时刻都智能支持一个标记。标记第二个位置时会清除第一个标记。
Java.io中的唯一两个始终支持标记的输入类型BufferedInputSream和ByteArrayInputStream。而其他的输入流,如TelnetInputStream可能在首先链接到缓冲的输入流才支持标记。
InputStream和OutputStream是相当于原始的类。它们单个或成组地读写字节,但仅此而已。Java提供了很多的过滤器类,可以附加到原始流中,在原始字节和各种格式之间来回转换。
过滤器有两个版本:过滤器流及阅读器和书写器。过滤器流仍然主要将原始数据作为字节操作,同时过滤器流置于原始流或其他过滤器流之上。阅读器和书写器置于原始、过滤器流或其他的阅读器和书写器之上。但是,过滤器流不能放在阅读器和书写器上面。
1、缓冲流
BufferedOutputStream 类将待写入的数据存储在缓冲区(名为buf的保护字节数组字段),直到缓冲区满或刷新输出流然后将数据全部写入底层输入流。如果一次写入多个字节,这与多次小的写入,但字节加起来一样的情况相比,前者要快得多(对于网络连接尤其是这样)。
BufferedInputStream类也有作为缓冲区的名为buf的保护字节数组。当调用某个流的read( ) 方法时,它首先尝试从缓冲获得请求数据。只有当缓冲区没有数据时,流才会从底层的源中读取。
BufferedInputSream和BufferedOutputSream各有两个构造函数。
public BufferedInputStream ( InputStream in )
public BufferedInputStream ( InputStream in , int bufferSize )
public BufferedOutputStream ( OutputStream out )
public BufferedOutputStream ( OutputStream out , int bufferSize )
第一个参数是底层流,可以从中读取或向其写入未缓冲的数据。如果给出第二个参数,则指定缓冲的字节数量。否则,输入流的缓冲区大小设为2048字节,输出流设为512字节。缓冲区的理想大小取决于所缓冲的流是何种类型。
BufferedInputStream没有申明自己的任何新方法。它只覆盖了InputStream方法,同时它支持标记和重置。
public int read ( ) throws IOException
public int read ( byte[ ] input , int offset , int length ) throws IOExcepiton
public long skip ( long n ) throws IOExption
public int available ( ) IOException
public void mark ( int readLimit )
public void reset ( ) throws IOException
public boolean markSupported ( )
两个多字节的read( ) 方法尝试根据需要多次从底层输入流中读取,从而填充指定的数组或子数组。它们只有在数组或子数组完全填满、到达流末尾或底层流阻塞而无法进一步读取。大多数输入流多不这样做,它们在返回前指从底层流或数据源中读取一次。
BufferedOutputStream没有声明自己任何新方法只是覆盖了OutputStream的三个方法
public void write ( int b ) throws IOException
public void write ( byte[ ] data, int offset , int length ) throws IOException
public void flush ( ) throws IOException
调用这些方法和任何输出流完全相同。区别在于,每次写入会吧数据存放在缓冲区,而不是直接放在底层的输出流。因此,在数据发送时刷新输出流是非常重要的。
此外,还有PrintStream ( 过滤器输出流 )、PushbackInputStream
除了以上的缓冲流外还有数据流、压缩流和摘要流以及加密流。
缓冲流 | BufferedOutputStream BufferedInputStream PrintStream PushbackInputStream |
数据流 | DataInputStream DataOutStream |
压缩流 | DeflaterOutputStream InflaterInputStream GZIPOutputStream GZIPInptuStream ZipOutputStream ZipInputStream |
摘要流 | DigestInputStream DigestOutputStream |
加密流 | CipherInputStream CipherOutputStream |
java.io.Reader类指定读取字符的API,java.io.Writer指定写入的API.相应的所有输入和输出流使用字节的地方,阅读器和书写器会使用Unicode字符。Reader和Writer的具体子类允许读取某个源和写入某个目标。过滤阅读器和书写器可以附加到其他阅读器或书写上,提供二外的服务或接口。
Reader和Writer最重要的具体子类是InputStreamReader和OutputStreamwriter来。InputStream类包含一个底层输入流,从中可以读取原始字节。它根据特定的编写方式,将这些字节转换为Unicode字符。OutputStream从运行的程序中接受Unicode字符,然后使用特定的编码方式将这些字符转化为字节,再将字节写入底层的输出流中。
除了InputStreamReader和OutputStreamWriter这连个类,Java.io包还提供不需要直接冲底层输入流读取字符的原始阅读器和书写器类,包括:
- FileReader
- FileWriter
- StringReader
- StringWriter
- CharArrayReader
- CharArrayWrite
列表中前两个类可处理文件,后四个由Java内部使用所以网络编程中不太常用。到那时,除了构造函数不同,这类与其他所有的阅读器和书写器类一样,具有相同的公共接口。
Write类映射了java.io.OutputStream类。它是抽象类,有两个保护类型的构造函数。与OutputStream一样,write类从不直接使用;而是通过它的子类以多态的方式使用;它有5个write ( )方法,以及flush ( ) 和 close ( ) 方法:
protected Writer ( )
protected Writer ( Object lock )
public abstract void write ( chart[ ] text , int offset , int length ) throws IOException
public void write ( int c ) throws IOException
public void write ( char [ ] text ) throws IOException
public void write ( String s ) throws IOException
public void write ( String s ) throws IOException
public void write ( String s , int offset , int length ) throws IOException
public void abstract void flush ( ) throws IOException
public void abstract void close ( ) throws IOException
write ( char[ ] text , int offset , int length ) 方法是基础法法,其他四个write都是根据他来实现的。它的子类至少实现这个方法和flush ( ) 及close ( ) 。书写器可能会得到缓冲,不管是直接链接到BufferedWriter( 属于过滤器书写器的一种),还是间接与之链接(因为其底层输入时缓冲的)。为强制将一次写入提交给媒介,要调用flush ( ) 方法。
close ( )方法的行为与OutputStream的close ( )方法相似。close ( )刷新书写器,然后关闭底层输出流,释放与之相关的所有资源:
public abstract void ( ) throws IOException
OutputStreamWriter是Writer的最重要的具体子类。OutputStreamWriter会从Java程序接受字符。它根据指定的编码方式将这些字符转化为字节,写入底层输入流中。它的构造函数指明写入的输入流和使用编码方式:
public OutputStreamWriter ( OutputStream out Strng encoding ) throws UnsupportedEncodingException
public OutputStreamWriter ( OutputStream out )
Reader类是Java.io.InputStream类的镜像。它是抽象类,有两个保护类型构造函数。与InputStream和Writer一样,Reader类从来不直接使用,只通过其子类来使用。它的主要方法如下:
protected Reader ( )
protected Reader ( Obect lock )
public abstact int read ( char[ ] text , int offset , int length ) throws IOException
public int read ( ) throws IOException
public int read ( char[ ] text ) throws IOException
public long skip ( long n ) throws IOException
public boolean ready 90
public boolean markSupported ( )
public void mark mark ( int readAheadLimit ) throws IOException
public void reset ( ) throws IOException
publix abstract void close ( ) IOException
read( char[ ] text , int offset , int length ) 方法是基础方法,其他两个read ( ) 方法都是根据它实现的。子类必须覆盖这个方法及close ( ) ,但是为了提供更高效实现方法,大多数子类还会覆盖其他的read ( ) 。
InputStreamReader是Reader的最重要的具体子类 。InputStreamReader从其底层输入流(如FileInputStream或TelnetInputStream)中读取字节数。它根据指定编码方式将这些字节转化为字符,并返回这些字符。构造函数指定要读取的输入流和所用的编码方式:
public InputStreamReader ( InputStream in )
public InputStreamReader ( InputStream in , String encoding ) throws UnsupportedEncodingException
如果没有指定编码方式,就使用平台的默认编码方式。如果指定了未知的编码方式,就会抛出UnsupportedEncodingException异常。
InputStreamReader和OutputStreamWriter 类就像装饰器,位于输入和输出流之上,把面向字节的接口改为面向字符的接口。完成之后,其他面向字符的过滤器就可以放在使用Java.io.FilterReader和Java.io.FilterWriter类的阅读器或书写器上面。与过滤器流一样,有很多子类可以执行特定的过滤,包括:
BufferedReader
BufferedWriter
LineNumberReader
PushbackReader
PrintWriter
BufferedReader和BufferedWriter类是基于字符的,相当面向字节的Buffered类是基于字符的,相当于面向字节的BufferedInputStream和BufferedOutputStream类。在BuffrerdInputStream和BufferedInputStream中使用内部字节数组作为缓冲区的地方,BufferedReader和BufferedWriter都是用内部字符数组。
当程序从BufferedReader和BufferedWriter也有平常的与阅读器和书写器关联的方法,如read ( ) 、ready ( )、write ( )、和close ( ) 。这两个类都有构造函数,吧BufferedReader或BufferedWriter链接到底层阅读器或书写器,并设置缓冲区的大小。如果没有缓设置冲区大小,则使用默认的大小8192字符:
public BufferedR ( Read in , int bufferSize )
public BufferedReader ( Read in )
public BufferedWriter ( Writer out )
public BufferedWriter ( Writer out , int bufferSize )