Java高级IO流.docx
《Java高级IO流.docx》由会员分享,可在线阅读,更多相关《Java高级IO流.docx(17页珍藏版)》请在冰豆网上搜索。
Java高级IO流
模块十五高级I/O流
本模块讨论Java语言是怎样通过流来操纵字节、字符和对象I/O的。
此外,本模块还要讨论节点流。
在完成了本模块的学习后,你应当能够:
●描述和使用java.io包的流式思想
●构造文件和过程流,并恰当地使用它们
●区别字节流与字符流,并进行合适的选择
●考察并操作文件和目录
●读、写和更新文本和数据文件
●使用Serialization接口来编码对象的状态
1.I/O基础知识
在Java语言中数据流是发送或接收数据的管道。
通常,你的程序是流的一个端点,其它程序或文件是流的另一个端点。
源端点和目的端点分别叫做inputstream(输入流)和outputstream(输出流)。
你可以从输入流读,但你不能对它写;同样,你可以向输出流写,但不能从输出流读。
图15-1数据流分类
bytestream
characterstreams
Sourcestreams
InputStream
Reader
Sinkstream
OutputStream
Writer
如图15-1所示:
Java技术支持两种数据类型的流:
InputStream和OutputStream:
字节流。
其它字节流都是InputStream或OutputStream的子类。
Reader和Writer:
字符流。
其它字符流都是Reader或Writer的子类。
2.字节流(InputStream)
2.1.InputStream的方法
InputStream有三个方法访问它的数据:
●intread():
简单读方法返回一个int值,它是从流里读出的一个字节。
如果遇到文件结束则返回-1。
●intread(byte[]):
将数据读入到字节数组中,并返回所读的字节数。
●intread(byte[],intoffset,intlength)将数据读入到字节数组中,并返回所读的字节数。
Offset是数组的偏移量,length是读取的长度。
voidclose()
你完成流操作之后,就关闭这个流。
如果你有一个流所组成的栈,使用过滤器流,就关闭栈顶部的流。
这个关闭操作会关闭其余的流。
intavailable()
这个方法报告立刻可以从流中读取的字节数。
在这个调用之后的实际读操作可能返回更多的字节数。
skip(long)
这个方法丢弃了流中指定数目的字符。
booleanmarkSupported()
voidmark(int)
voidreset()
如果流支持“回放”操作,则这些方法可以用来完成这个操作。
如果mark()和reset()方法可以在特定的流上操作,则markSupported()方法将返回ture。
mark(int)方法用来指明应当标记流的当前点和分配一个足够大的缓冲区,它最少可以容纳参数所指定数量的字节。
在随后的read()操作完成之后,调用reset()方法来返回你标记的输入点。
2.2.OutputStream方法
voidwrite(int)
voidwrite(byte[])
voidwrite(byte[],int,int)
这些方法写输出流。
和输入一样,总是尝试以实际最大的块进行写操作。
voidclose()
当你完成写操作后,就关闭输出流。
如果你有一个流所组成的栈,就关闭栈顶部的流。
这个关闭操作会关闭其余的流。
voidflush()
有时一个输出流在积累了若干次之后才进行真正的写操作。
flush()方法允许你强制执行写操作。
3.字符流
3.1.Reader方法
intread()
intread(char[])
intread(char[],intoffset,intlength)
简单读方法返回一个int值,它包含从流里读出的一个字符或者-1,其中-1表明文件结束。
其它两种方法将数据读入到字符数组中,并返回所读的字符数。
第三个方法中的两个int参数指定了所要填入的数组的子围。
voidclose()
booleanready()
voidskip(long)
booleanmarkSupported()
voidmark(int)
voidreset()
这些方法与InputStream中的对应方法相似
3.2.Writer方法
voidwrite(intc)
voidwrite(char[])
voidwrite(char[],intoffset,intlength)
voidwrite(Stringstring)
voidwrite(Stringstring,intoffset,intlength)
voidclose()
voidflush()
所有这些方法与OutputStream中的方法类似。
4.节点流
Java2SDK中有三种基本类型的节点:
文件(file)、存(memory)、管道(pipe)。
type
CharacterStreams
ByteStreams
File
FileReader
FileWriter
FileInputStream
FileOutputStream
Memory:
Array
CharArrayReader
CharArrayWriter
ByteArrayInputStream
ByteArrayOutputStream
Memory:
String
StringReader
StringWriter
Pipe
PipedReader
PipedWriter
PipedInputStream
PipedOutputStream
5.AReader/WriterExample
下面的例子从第一个命令行参数代表的文件中读字符,然后写入第二个参数代表的文件。
本例题阐述了Reader/Writer的子类FileReader/FileWriter用法。
importjava.io.*;
publicclassTestNodeStreams{
publicstaticvoidmain(String[]args){
try{
FileReaderinput=newFileReader(args[0]);
FileWriteroutput=newFileWriter(args[1]);
char[]buffer=newchar[128];
intcharsRead;
//readthefirstbuffer
charsRead=input.read(buffer);
while(charsRead!
=-1){
//writethebufferouttotheoutputfile
output.write(buffer,0,charsRead);
//readthenextbuffer
charsRead=input.read(buffer);
}
input.close();
output.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
}
6.ABufferedReader/WriterExample
本例题完成的功能与上一节的例题相同。
BufferedReader能够按行读取,BufferedWriter能够按行写出。
importjava.io.*;
publicclassTestBufferedStreams{
publicstaticvoidmain(String[]args){
try{
FileReaderinput=newFileReader(args[0]);
BufferedReaderbufInput=newBufferedReader(input);
FileWriteroutput=newFileWriter(args[1]);
BufferedWriterbufOutput=newBufferedWriter(output);
Stringline;
//readthefirstline
line=bufInput.readLine();
while(line!
=null){
//writethelineouttotheoutputfile
bufOutput.write(line,0,line.length());
bufOutput.newLine();
//readthenextline
line=bufInput.readLine();
}
bufInput.close();
bufOutput.close();
}catch(IOExceptione){
e.printStackTrace();
}
}
}
7.I/O流的链
在程序中很少使用单独一个流对象,实际做法是将几个流对象串联起来处理共同数据。
这样做会提高程序的效率。
一个输入流链
Input.read()读一个字节
Input.read(char[])读入一个字符数组返回字符的个数
Input.readLine()读入一行
Output.write()写入一个字节
Output.writer(char[],0,length)写入一个字符数组把从0到length个字符写到指定的文件中
Output.newLine()写入换行符
一个输出流链
Output.write(double)写入一个字节型double数据
In.read()读一个字符
In.read(char[])读一个字符数组
In.readLine()读一行
Out.write()写一个字符
Out.write(char[],0,length)写多个字符
Out.newLine()换行
In.readObject()读一个序例化的对象
Out.writeObject()写入一个序例化的对象
f=newRandomAccessFile(“path”,”rw”)创建一个随机存储文件类的实例
f.readDouble();
f.writerDouble(3.1415);
8.过程流
过程流在其它流之上,完成排序、变换等操作。
过程流也被称做过滤流。
当你需要改变输入流的原始数据时,你可以将一个过滤输入流连接到一个原始的输入流上。
用过滤流将原始数据变换成你需要的格式。
过程流的类型
9.
基本字节流类
图15-4,图15-5阐述了输入,输出流的体系结构:
图15-4
图15-5
9.1.FileInputStream和FileOutputStream
这两个节点流用来操纵磁盘文件。
这些类的构造函数允许你指定它们所连接的文件。
要构造一个FileInputStream,所关联的文件必须存在而且是可读的。
如果你要构造一个FileOutputStream而输出文件已经存在,则它将被覆盖。
FileInputStreaminfile=newFileInputStream("myfile.dat");
FileOutputStreamoutfile=newFileOutputStream("results.dat");
9.2.BufferInputStream和BufferOutputStream
这些是过滤器流,它们可以提高I/O操作的效率。
9.3.PipedInputStream和PipedOutputStream
管道流用来在线程间进行通信。
一个线程的PipedInputStream对象从另一个线程的PipedOutputStream对象读取输入。
要使管道流有用,必须有一个输入方和一个输出方。
9.4.DataInputStream和DataOutputStream
这些过滤器通过流来读写Java基本类。
例如:
DataInputStream方法
BytereadByte()
longreadLong()
doublereadDouble()
DataOutputStream方法
voidwriteByte(byte)
voidwriteLong(long)
voidwriteDouble(double)
10.基本字符流类
图15-6,图15-7阐述了Reader和Writer字符流的体系结构。
图15-6
图15-7
10.1.InputStreamReader和OutputStreamWriter
用于字节流与字符流之间的转换接口。
当你构造一个InputStreamReader或OutputStreamWriter时,转换规则定义了16位Unicode和其它平台的特定表示之间的转换。
InputStreamReader从一个数据源读取字节,并自动将其转换成Unicode字符。
如果你特别声明,InputStreamReade会将字节流转换成其它种类的字符流。
OutputStreamWriter将字符的Unicode编码写到输出流,如果你的使用的不是Unicode字符,OutputStreamWriter会将你的字符编码转换成Unicode编码。
10.2.缓冲读者和作者
因为在各种格式之间进行转换和其它I/O操作很类似,所以在处理大块数据时效率最高。
在InputStreamReader和OutputStreamWriter的结尾一个BufferedReader和BufferedWriter是一个好主意。
记住对BufferedWriter使用flush()方法。
10.3.读入字符串输入
下面这个例子说明了从控制台标准输入读取字符串所应当使用的一个技术。
1.importjava.io.*;
2.
3.publicclassCharInput{
4.publicstaticvoidmain(Stringargs[])throwsjava.io.IOException{
5.Strings;
6.InputStreamReaderir;
7.BufferedReaderin;
9.in=new
BufferedReader(newInputStreamReader(System.in));
10.
11.while((s=in.readLine())!
=null){
12.System.out.println("Read:
"+s);
13.}
14.}
15.}
10.4.使用其它字符转换
如果你需要从一个非本地(例如,从连接到一个不同类型的机器的网络连接读取)的字符编码读取输入,你可以象下面这个程序那样,使用显式的字符编码构造ir=newInputStreamReader(System.in,“8859_1”);
注:
如果你通过网络连接读取字符,就应该使用这种形式。
否则,你的程序会总是试图将所读取的字符当作本地表示来进行转换,而这并不总是正确的。
ISO8859-1是映射到ASCII的Latin-1编码模式。
11.URL输入流
除了基本的文件访问之外,Java技术提供了使用统一资源定位器(URL)来访问网络上的文件。
当你使用Applet的getDocumentBase()方法来访问声音和图象时,你已经隐含地使用了URL对象。
StringimageFile=newString("images/Duke/T1.gif");
images[0]=getImage(getDocumentBase(),imageFile);
当然,你也可以直接使用URL如下:
.URLimageSource;
try{
imageSource=newURL("mysite./~info");
}catch(MalformedURLExceptione){}
images[0]=getImage(imageSource,"Duke/T1.gif");
11.1.打开一个URL输入流
下面的代码片段打开了文档基目录下的一个数据文件。
1.InputStreamis=null;
2.Stringdatafile=newString("Data/data.1-96");
3.bytebuffer[]=newbyte[24];
4.try{
5.//newURLthrowsaMalformedURLException
6.//URL.openStream()throwsanIOException
7.is=(newURL(getDocumentBase(),datafile)).openStream();
8.}catch(Exceptione){}
现在,你可以就象使用FileInputStream对象那样来用it来读取信息:
1.try{
2.is.read(buffer,0,buffer.length);
3.}catch(IOExceptione1){}
12.随机访问文件
你经常会发现你只想读取文件的一部分数据,而不需要从头至尾读取整个文件。
你可能想访问一个作为数据库的文本文件,此时你会移动到某一条记录并读取它的数据,接着移动到另一个记录,然后再到其他记录――每一条记录都位于文件的不同部分。
Java编程语言提供了一个RandomAccessFile类来处理这种类型的输入输出。
12.1.创建一个随机访问文件
你可以用如下两种方法来打开一个随机存取文件:
●用文件名
myRAFile=newRandomAccessFile(Stringname,Stringmode);
●用文件对象
myRAFile=newRandomAccessFile(Filefile,Stringmode);
mode参数决定了你对这个文件的存取是只读(r)还是读/写(rw)。
例如,你可以打开一个数据库文件并准备更新:
RandomAccessFilemyRAFile;
myRAFile=newRandomAccessFile("db/stock.dbf","rw");
12.2.存取信息
RandomAccessFile对象按照与数据输入输出对象相同的方式来读写信息。
你可以访问在DataInputStrem和DataOutputStream中所有的read()和write()操作。
Java编程语言提供了若干种方法,用来帮助你在文件中移动。
●longgetFilePointer();
返回文件指针的当前位置。
●voidseek(longpos);
设置文件指针到给定的绝对位置。
这个位置是按照从文件开始的字节偏移量给出的。
位置0标志文件的开始。
●longlength()
返回文件的长度。
位置length()标志文件的结束。
12.3.添加信息
你可以使用随机存取文件来得到文件输出的添加模式。
myRAFile=newRandomAccessFile("java.log","rw");
myRAFile.seek(myRAFile.length());
//Anysubsequentwrite()swillbeappendedtothefile
13.对象串行化
java.io.Serializable接口支持将一个Java技术对象存放到一个流中。
将一个对象存放到某种类型的永久存储器上称为“保持”。
如果一个对象可以被存放到磁盘或磁带上,或者可以发送到另外一台机器并存放到存储器或磁盘上,那么这个对象就被称为可保持的。
java.io.Serializable接口没有任何方法,它只作为一个“标记”,用来表明实现了这个接口的类可以串行化。
类中没有实现Serializable接口的对象不能被保持。
13.1.对象图
当一个对象被串行化时,只有对象的数据被保存;方法和构造函数不属于串行化流。
如果一个数据变量是一个对象引用,那么这个对象的数据成员也会被串行化。
树或者对象数据的结构,包括这些子对象,构成了对象图。
因为有些对象类所表示的数据在不断地改变,所以它们不会被串行化;例如,java.io.FileInputStream、java.io.FileOutputStream和java.lang.Thread等流。
如果一个可串行化对象包含某个不可串行化元素的引用,那么整个串行化操作就会失败,而且会抛出一个NotSerializableException。
如果对象图包含一个不可串行化的引用,只要这个引用已经用transient关键字进行了标记,那么对象仍然可以被串行化。
publicclassMyClassimplementsSerializable{
publictransientThreadmyThread;
privateStringcustomerID;
privateinttotal;
域存取修饰符对于被串行化的对象没有任何作用。
写入到流的数据是字节格式,而且字符串被表示为UTF(文件系统安全的通用字符集转换格式)。
transient关键字防止对象被串行化。
publicclassMyClassimplementsSerializable{
publictransientThreadmyThread;
privatetransientStringcustomerID;
privateinttotal;
13.2.读写一个对象流
13.2.1.写
对一个文件流读写对象是一个简单的过程。
下面的代码将一个java.util.Date对象的实例发送到一个文件:
1.publicclassSerializeDate{
2.SerializeDate(){
3.Dated=newDate();
4.try{
5.FileOutputStreamf=newFileOutputStream("date.ser");
7.ObjectOutputStreams=newObjectOutputStream(f);
9.s.writeObject(d);
10.f.close();
11.}catch(IOExceptione){
12.e.printStackTrace();
13.}
1