1. streams及i/o
stream就是信息源与目的地之间的通信路径,这里的信息源可以是文件、内存、网络等。streams主要分为input及output stream。
1.1 inputstream类
类inputstream处于input stream类层次的最顶层,它主要具有以下几种方法:
1.1.1 read方法
read方法用于从指定的输入流读取以字节为单位的数据,第一次从流的开始位置开始读取,以后每次从上次的结束部位开始读取,即自动实现了位移。
read方法有以下三种形态:
(1) int read(byte buff[n]):从指定输入流中读取n个字节填充到buff中,该方法返回读取的实际字节数,如果读取的实际字节数小于n,一般是因为已读到指定输入流的末尾;
(2) int read():即不带参数,该方法每次一个字节从指定的输入流中读取数据。返回值也是int类型,但它并不代表读取的字节数,而是从流中读取的数据的本身,因数据本身是byte类型的,所以一般要强制进行转化;如果读到流的末尾返回的值是-1;
(3) int read(byte buff[n],int start, int len):从指定流读取数据,从start开始,填充len个字节到buff中,返回值为实际的填充数,如果返回值<len,一般表示已将指定流中的数据读完;
以下是read的简单例子:
import java.io.*;
class testio1{
public static void main(string args[]) {
inputstream s = null;
try{
s = new fileinputstream("io.txt");
}catch(filenotfoundexception e){
system.out.println("file not find");
}
int i;
try{
i = s.read();
while(i != -1){
system.out.println((char)i);
i = s.read();
}
}catch(ioexception e){
system.out.println("io error");
} } }
1.1.2 skip方法
skip方法类似于c语言中的lseek都是用于定位的。skip方法定义:long skip(long n),该方法使指定流中的当前位置移动n个字节,n的值可以是负值用于向前移,skip方法返回值为实际移动的字节数,由于种种原因,如已到流尾或者其它原因返回的值往往小于n。对于读取文件来说,小于n的原因最大的原因是读到了文件尾。
1.1.3 available方法
available方法用于计算指定流中当前有多少字节,如果指定的流是文件流,那么就返回文件的大小。available返回的值是int类型。
有的输入流可能没有能力返回字节数,如果对这些输入流使用avaiable方法,返回值为0。
1.1.4 close方法
对于打开的stream,java可以自动回收,但是java自动回收需要时间,所以最好自己调用close方法来关闭stream,这样方便下一次重新指定的流。
1.2 bytearrayinputstream
bytearrayinputstream是从inputstream中继承下来的,用于从字节数组中提取数据,关于bytearrayinputstream的创建例子如下:
byte[] buffer = new byte[1024];
fillwithusefuldata(buffer); //自定义的方法,用于在buffer中填充数据
inputstream s = new bytearrayinputstream(buffer);
inputstream s1 = new bytearrayinputstream(buffer,100,300);
其中bytearrayinputstream(buffer,100,300)是创建到buffer的stream,从buffer的第100个字节开始取300字节。
bytearrayinputstream的其它方法与inputstream类似,这里不再重复。
1.3 fileinputstream
fileinputstream也是从inputstream中继承下来的,用于从指定的文件中提取。因此它的方法也与inputstream中的方法类似,这里不再介绍,只介绍fileinputstream中特殊的方法:getfd(),该方法用于获取文件句柄。使用方法如下:
fileinputstream afis = new fileinputstream("afilename");
filedescriptor myfd = afis.getfd();
这样以后要用到afilename文件时可以使用myfd这个文件句柄(实际上是文件描述类的实例),如要重新打开该文件,可以使用fileinputstream afis = new fileinputstream(myfd)。
关于文件描述类filedescriptor,有以下几点说明:
(1) 属性in:标准输入;
(2) 属性out:标准输出;
(3) 属性err:标准错误输出;
在fileinputstream中还有另一个特殊的方法就是:finalize()。
1.4 filterinputstream
filterinputstream也是从inputstream中继承下来,不过filterinputstream类基本上不能直接使用,一般上使用该类的派生类,如bufferedinputstream等。该类的最大特点是,在定义时可以嵌套:
inputstream s = getaninputstreamfromsomewhere();
filterinputstream s1 = new filterinputstream(s);
filterinputstream s2 = new filterinputstream(s1);
filterinputstream s3 = new filterinputstream(s2);
所以该类的所有派生类都具有这个特性。
1.5 bufferedinputstream
bufferedinputstream指定数据源是内存的指定区域,从filterinputstream继承下来的,这种类型的stream主要用于提高性能,它定义时一般指定其它的inputstream,如:
inputstream s = new bufferedinputstream(new fileinputstream("foo"));
bufferedinputsream是可以使用mark及reset方法,使用上述的嵌套方法间接的使其它的stream也支持这些方法了。
由于bufferedinputstream需要buffer,所以它需要等待在它之前的数据都到了后才工作,所以bufferedinputstream最好用在流的前面,如上面这个例子,当然用在最前面也没有什么意思了。
1.6 datainputstream
datainputstream也是从filterinputstream继承下来的,所以也具有父类的特性。datainputstream还implement datainput接口,所以datainputstream具体的方法最多,如:readshort、readboolean、readbyte、readunsignedbyte、readshort等。这些方法的都是read方法的扩展,使用上也相似,所以这里不多介绍。
以下是readint的实现:
public final int readint() throws ioexception {
int ch1 = in.read();
int ch2 = in.read();
int ch3 = in.read();
int ch4 = in.read();
if ((ch1 | ch2 | ch3 | ch4) < 0)
throw new eofexception();
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
}
以下是readline的实现:
public final string readline() throws ioexception {
char buf[] = linebuffer;
if (buf == null) {
buf = linebuffer = new char[128];
}
int room = buf.length;
int offset = 0;
int c;
loop: while (true) {
switch (c = in.read()) {
case -1:
case \n:
break loop;
case \r:
int c2 = in.read();
if ((c2 != \n) && (c2 != -1)) {
if (!(in instanceof pushbackinputstream)) {
this.in = new pushbackinputstream(in);
}
((pushbackinputstream)in).unread(c2);
}
break loop;
default:
if (–room < 0) {
buf = new char[offset + 128];
room = buf.length – offset – 1;
system.arraycopy(linebuffer, 0, buf, 0, offset);
linebuffer = buf;
}
buf[offset++] = (char) c;
break;
}
}
if ((c == -1) && (offset == 0)) {
return null;
}
return string.copyvalueof(buf, 0, offset);
}
在这个例子中,如果读出的字符中\r,还得再读一位判断是否是\n,如果不是\n,还得将这个字符放回到输入流中,因此使用了pushbackinputstream的功能。
如果对datainputstream的读操作已到stream的末尾,会抛出eofexception异常。在stream末尾,skipbytes不做任何动作;readline返回null;readutf抛出utfdataformatexception异常。
1.7 linenumberinputstream
同样linenumberinputstream是从filterinputstream继承下来的,该类可以跟踪行号,设置行号,对行作标记以便恢复等功能。一般不直接使用该类,而使用其它stream的嵌套间接对它进行使用,如:
linenumberinputstream alnis;
alnis = new linenumberinputstream(new fileinputstream("source"));
datainputstream s = new datainputstream(alnis);
string line;
while ((line = s.readline()) != null) {
. . . // process the line
system.out.println("did line number: " + alnis.getlinenumber());
}
在这个例子中,使用datainputstream读取行,使用linenumberinputstream来监控行。从这个子可以看出,嵌套流中数据的流动贯穿所有的流,即所以流中的数据在同步流动。
1.8 pushbackinputstream
pushbackinputstream也是从filterinputstream继承下来,它有一个特殊的方法unread,用于将读出来的数据放出流中。在例子readline就使用了这个方法,unread与read相对应有三个形态:unread()、unread(byte[])及unread(byte[],int,int)。
1.9 pipedinputstream
filterinputstream的派生类已介绍完毕,下面接着介绍inputstream的派生类。pipedinputstream一般与pipedoutputstream一起用构成线程之间双向的通信管道。因此在里先不介绍pipedinputstream的用法。
1.10 stringbufferinputstream
string buffer = "now is the time for all good men to come…";
inputstream s = new stringbufferinputstream(buffer);
1.11 bytearrayinputstream
bytearrayinputstream与stringbufferinputstream类似,只是它基于bytearry,而stringbufferinputstream基于string。
1.12 sequenceinputstream
sequenceinputstream用于将不同的inputstream按先后顺序串在一起,如将两个inputstream串起来:
inputstream s1 = new fileinputstream("thefirstpart");
inputstream s2 = new fileinputstream("therest");
inputstream s = new sequenceinputstream(s1, s2);
以上只能实现两个输入流的串接,要实现两个以上输入流串接,需要用到vector类,如下所示:
vector v = new vector(3);
enumeration e;
v.addelement(s1);
v.addelement(s2);
v.addelement(s2);
e = v.elements();
inputstream s = new sequenceinputstream(e) ;
1.13 outputstream
outputstream位于output stream类层次的最顶层,它是一个抽象类,它规定了output stream类的基本功能。
1.13.1 write
write方法与inputstream的read方法相对应,它有三个形态:
(1) write(byte[]):将指定byte数组中的数据输出到指定stream;
(2) write(byte[],int,int):将指定byte数组中的数据从第二个参数开始,输出第三个参数指定的长度到指定的stream;
(3) wirte(int);将一个int值输出到指定的stream;
1.13.2 flush和close
有些输出流在输出时先放在缓冲中,可以使用flush将这些数据真正写入指定的输出流中。close用于关闭指定的输出流。
1.14 bytearrayoutputstream
bytearrayoutputstream将一个输出流指向一个byte数组,但这个byte数组是bytearrayoutputstream内部内置的,不需要我们来定义。该类有两个构造函数:
(1) bytearrayoutputstream():该构造函数会在内部创建一个长度为32的byte数组;
(2) bytearrayoutputstream(int n):在对象内部创建一个长度为n的byte数组。
bytearrayoutputstream从outputstream类继承下来,所以它具write、flush及close等方法,同时它还具有以下几个特殊的方法:
(3) tostring():将对象内部的byte数组转化成字符串(string(buf,0,count);
(4) tostring(int n):将对象对部的byte数组转化成字符串,编码方式为n;
(5) tostring(string n):将对象对部的数组转化成字符串,编码方式为n;
(6) tobytearray():返回对象内部的byte数组;
(7) writeto(outputstream):将内部byte数组输出到指定的输出流;
(8) reset():将对象内部byte数组的长度设为0,{count = 0};
(9) size():返回byte数组长度;
1.15 fileoutputstream
fileoutputstream与fileinputstream相对应,它具有以下几个构造函数:
(1) fileoutputstream(file)
(2) fileoutputstream(file file, boolean append):如果append为真,以添加方式写入文件,如果为否(缺省),以新增方式写入文件;
(3) fileoutputstream(filedescriptor)
(4) fileoutputstream(string name)
(5) fileoutputstream(string name, boolean append)
其它的方法大多为标准方法,这里不再介绍。以下使用的例子:
filedescriptor fd = somefilestream.getfd();
outputstream s = new fileoutputstream(fd);
1.16 filteroutputstream
filteroutputstream与filterinputstream相对应,使用方法也相类似。
1.17 bufferedoutputstream
bufferedoutputstream从filteroutputstream中继承下来,它与bufferedinputstream相对应,作用也相类似,它主要为输出流做缓冲,如:
outputstream s = new bufferedoutputstream(new fileoutputstream("foo"));
由于bufferedoutputstream是缓冲数据的,所以必要时,需要使用flush方法强制将缓冲中的数据真正写入输出流中。
1.18 dataoutputstream
dataoutputstream与datainputstream相对应,在继承outputstream的同时,实现了dataoutput接口,因此它具有dataoutput接中所规定的方法,这些方法与datainput所规定的方法相反。
它还具有size方法,该方法返回向输出流写入的字节数。以下是实现复制功能的例子:
try { while (true) ado.writebyte(adi.readbyte()); }
finally { adi.close(); ado.close(); }
1.19 printstream
printstream是从filteroutputstream继承下来的。使用例子如下:
printstream s = new printstream(new fileoutputstream("foo"));
s.println("heres the first line of text in the file foo.");
这个例子说明可以使用printstream向文件写数据,并且该类提供了输出行的功能,弥补了dataoutputstream的空白(在dataoutputstream没有输出行的功能)。
printstream的构造函数:
(1) printstream(boolean autoflush, outputstream out)
(2) printstream(outputstream out)
(3) printstream(outputstream out, boolean autoflush)
(4) printstream(outputstream out, boolean autoflush, string encoding)
1.20 pipedoutputstream
pipedoutputstream与pipedinputsteam相互配合实现两个线程之间的通信,它们的定义如下:
pipedinputstream sin = pipedinputstream();
pipedoutputstream sout = pipedoutputstream(sin);
以下是使用例子,该例子接收标准输入的数据,并输出到标准输出:
import java.io.*;
class readthread extends thread implements runnable {
inputstream pi = null;
outputstream po = null;
string process = null;
readthread( string process, inputstream pi, outputstream po) {
this.pi = pi; this.po = po; this.process = process; }
public void run() {
int ch; byte[] buffer = new byte[12]; int bytes_read;
try {
for(;;) {
bytes_read = pi.read(buffer); //从指定流读入数据
if (bytes_read == -1) { return; }
po.write(buffer, 0, bytes_read);//向指定流写入数据
thread.yield();
}
} catch (exception e) { e.printstacktrace();
} finally { }
}
}
public class mypipe{
public static void main( string [] args) {
try {
int ch;
pipedinputstream writein = new pipedinputstream();
pipedoutputstream readout = new pipedoutputstream( writein );
fileoutputstream writeout = new fileoutputstream("out");
readthread rt = new readthread("reader", system.in, readout );
readthread wt = new readthread("writer", writein, system.out );
rt.start();
wt.start();
} catch (exception e) {
e.printstacktrace();
} }}
说明:
(1) 类readthread非常巧妙,它并没有指定输入输出流的具体类型
(2) 在mypipe类中new readthread("reader", system.in, readout )语句使得从标准输入设备中接收数据,而从readout输出,而readout是pipedoutputsteam,所以它可以被另一线程接收;
(3) new readthread("writer", writein, system.out ),从writein接收数据,writein是readout是成对的双向管道,它接收从readout发送过来的数据。再从标准设备中输出。
1.21 randomaccessfile
1.22 streamtokenizer
1.23 objectoutputstream
objectoutputstream从outputstream继承下来,并实现了objectoutput, objectstreamconstants这两个接口。它负责将指定对象输出到指定的输出流,可以将非static、非transient的属性及值,对象的类定义输出到指定的输出流。该类有一个非常用的方法:
writeobject (object obj);
该方法将obj输出到指定的输出流,以下是该类的例子:
fileoutputstream f = new fileoutputstream("tmp");
objectoutput s = new objectoutputstream(f);
s.writeobject("today");
s.writeobject(new date());
s.flush();
可以使用transient修饰符规定一些变量的值不被输出到指定的输出流,如:
public transient int transientvalue = 4;
这样transientvalue的值就不会被输出到输出流。
1.24 objectinputstream
objectinputstream与objectoutputstream相对应,它是将对象的值及类的定义等从指定的输入流读入,以便重新对象化:
fileinputstream in = new fileinputstream("tmp");
objectinputstream s = new objectinputstream(in);
string today = (string)s.readobject();
date date = (date)s.readobject();
objectoutputstream和objectinputstream就可以实现对象的持久化,即要先将对象序列化保存到介质中,在必要的时候重新恢复这些对象。
