1、12 第十二章 高级IO流第十二章:高级I/O流学习目标 java.io包内类 理解流的概念和节点流,过滤流的使用 流与字符流并恰当使用和目录 更新文本和数据文件 用Serialization接口序列化编码对象的状态I/O基础知识Java语言中数据流是发送或接收数据的管道。通常,你的程序是流的一个端点,其它程序或文件是流的另一个端点。流的单向性:源端点和目的端点分别叫做input stream(输入流)和output stream(输出流)。你可以从输入流读,但你不能对它写;同样,你可以向输出流写,但不能从输出流读。数据流的分类如图InputStream和OutputStream:字节流。其它
2、字节流都是InputStream或OutputStream的子类。Reader和 Writer:字符流。其它字符流都是Reader或Writer的子类。字节流InputStreamInputStream有三个方法访问它的数据:int read():简单读方法,返回一个int值,它是从流里读出的一个字节。如果遇到文件结束则返回-1。int read(byte ):将数据读入到字节数组中,并返回所读的字节数。int read(byte, int offset,int length) 将数据读入到字节数组中,并返回所读的字节数。Offset是数组的偏移量,length是读取的长度。void clos
3、e() 你完成流操作之后,就关闭这个流。如果你有一个流所组成的栈,使用过滤器流,就关闭栈顶部的流。这个关闭操作会关闭其余的流。int available()这个方法报告立刻可以从流中读取的字节数。在这个调用之后的实际读操作可能返回更多的字节数。skip(long)这个方法丢弃了流中指定数目的字符。boolean markSupported()void mark(int) void reset()如果流支持回放操作,则这些方法可以用来完成这个操作。如果mark()和reset()方法可以在特定的流上操作,则markSupported()方法将返回ture。mark(int)方法用来指明应当标记流
4、的当前点和分配一个足够大的缓冲区,它最少可以容纳参数所指定数量的字节。在随后的read()操作完成之后,调用reset()方法来返回你标记的输入点。OutputStreamvoid write(int )void write(byte )void write(byte , int, int)这些方法写输出流。和输入一样,总是尝试以实际最大的块进行写操作。void close() 当你完成写操作后,就关闭输出流。如果你有一个流所组成的栈,就关闭栈顶部的流。这个关闭操作会关闭其余的流。void flush()有时一个输出流在积累了若干次之后才进行真正的写操作。flush()方法允许你强制执行写操作
5、。字符流Readerint read()int read(char)int read(char, int offset, int length)简单读方法返回一个int值,它包含从流里读出的一个字符或者-1,其中-1表明文件结束。其它两种方法将数据读入到字符数组中,并返回所读的字符数。第三个方法中的两个int参数指定了所要填入的数组的子范围。void close()boolean ready()void skip(long)boolean markSupported()void mark(int) void reset()这些方法与InputStream中的对应方法相似Writervoid w
6、rite(int c)void write(char )void write(char , int offset, int length)void write(String string)void write(String string, int offset, int length)void close()void flush()所有这些方法与OutputStream中的方法类似。节点流Java 2 SDK中有三种基本类型的节点:文件(file)、内存(memory)、管道(pipe)。如过程流过程流在其它流之上,完成排序、变换等操作。过程流也被称做过滤流。当你需要改变输入流的原始数据时,你
7、可以将一个过滤输入流连接到一个原始的输入流上。用过滤流将原始数据变换成你需要的格式。其分类如图基本字节流类分类如图FileInputStream和FileOutputStream这两个节点流用来操纵磁盘文件。这些类的构造函数允许你指定它们所连接的文件。要构造一个FileInputStream,所关联的文件必须存在而且是可读的。如果你要构造一个FileOutputStream而输出文件已经存在,则它将被覆盖。主要用于操作二进制或者带有格式的文件:如压缩文件,可执行文件等。FileInputStream infile = new FileInputStream(myfile.dat);FileOu
8、tputStream outfile = new FileOutputStream(results.dat);BufferInputStream和BufferOutputStream带有缓冲区的流,BufferInputStream一次可以读入一定长度的数据(默认2048字节),BufferOutputStream一次可以一定长度的数据(默认512字节),可以提高I/O操作的效率。需要和其它的流类配合使用。BufferOutputStream在使用时,为了确保把数据写出去,建议最后执行flush()将缓冲区中的数据全部写出去。PipedInputStream和PipedOutputStream
9、管道流用来在线程间进行通信。一个线程的PipedInputStream对象从另一个线程的PipedOutputStream对象读取输入。要使管道流有用,必须有一个输入方和一个输出方。DataInputStream和DataOutputStream用来对java的基本数据类型读写的类DataInputStream方法 byte readByte() long readLong() double readDouble() String readUTF(DataInput in)DataOutputStream方法 void writeByte(byte) void writeLong(long)
10、void writeDouble(double) void writeUTF(String str)PrintStream可以自动进行字符转换的动作,默认会使用操作系统的编码处理对应的字符。import java.io.*;public class PrintStreamDemo public static void main(String args) throws FileNotFoundException PrintStream out = new PrintStream(new FileOutPutStream(1.txt); out.println(1); out.close(); 基
11、本字符流类阐述了Reader和Writer字符流的体系结构。InputStreamReader 和 OutputStreamWriter用于字节流与字符流之间的转换接口。当你构造一个InputStreamReader或OutputStreamWriter时,转换规则定义了16位Unicode和其它平台的特定表示之间的转换。InputStreamReader从一个数据源读取字节,并自动将其转换成Unicode字符。如果你特别声明,InputStreamReade会将字节流转换成其它种类的字符流。OutputStreamWriter将字符的Unicode编码写到输出流,如果你的使用的不是Unico
12、de字符,OutputStreamWriter会将你的字符编码转换成Unicode编码。BufferedReader和BufferedWriter因为在各种格式之间进行转换和其它I/O操作很类似,所以在处理大块数据时效率最高。在InputStreamReader和OutputStreamWriter的结尾链接一个BufferedReader和BufferedWriter是一个好主意。记住对BufferedWriter使用flush()方法。FileReader和FileWriter以字符的方式操作文件的类,主要用于操作文本文件。PrintWriter与PrintStream相类似,使用prin
13、tln()输出内容。URL输入流除了基本的文件访问之外,Java技术提供了使用统一资源定位器(URL)来访问网络上的文件。当你使用Applet的getDocumentBase()方法来访问声音和图象时,你已经隐含地使用了URL对象。 String imageFile = new String (images/Duke/T1.gif); images0 = getImage(getDocumentBase(), imageFile);当然,你也可以直接使用URL如下:.URL imageSource;try imageSource = new URL(catch(MalformedURLExce
14、ption e) images0 = getImage(imageSource, Duke/T1.gif);使用RandomAccessFile随机访问文件你经常会发现你只想读取文件的一部分数据,而不需要从头至尾读取整个文件。你可能想访问一个作为数据库的文本文件,此时你会移动到某一条记录并读取它的数据,接着移动到另一个记录,然后再到其他记录每一条记录都位于文件的不同部分。Java编程语言提供了一个RandomAccessFile类来处理这种类型的输入输出。创建一个随机访问文件你可以用如下两种方法来打开一个随机存取文件:用文件名myRAFile = new RandomAccessFile(St
15、ring name, String mode);用文件对象myRAFile = new RandomAccessFile(File file, String mode);mode参数决定了你对这个文件的存取是只读(r)还是读/写(rw)。例如,你可以打开一个数据库文件并准备更新:RandomAccessFile myRAFile;myRAFile = new RandomAccessFile(db/stock.dbf,rw);存取信息RandomAccessFile对象按照与数据输入输出对象相同的方式来读写信息。你可以访问在DataInputStrem和DataOutputStream中所有的
16、read()和write()操作。Java编程语言提供了若干种方法,用来帮助你在文件中移动。long getFilePointer();返回文件指针的当前位置。void seek(long pos); 设置文件指针到给定的绝对位置。这个位置是按照从文件开始的字节偏移量给出的。位置0标志文件的开始。long length()返回文件的长度。位置length()标志文件的结束。添加信息你可以使用随机存取文件来得到文件输出的添加模式。myRAFile = new RandomAccessFile(java.log,rw);myRAFile.seek(myRAFile.length();对象串行化ja
17、va.io.Serializable接口支持将一个Java技术对象存放到一个流中。将一个对象存放到某种类型的永久存储器上称为保持。如果一个对象可以被存放到磁盘或磁带上,或者可以发送到另外一台机器并存放到存储器或磁盘上,那么这个对象就被称为可保持的。java.io.Serializable接口没有任何方法,它只作为一个标记,用来表明实现了这个接口的类可以串行化。类中没有实现Serializable接口的对象不能被保持。当一个对象被串行化时,只有对象的数据被保存;方法和构造函数不属于串行化流。如果一个数据变量是一个对象引用,那么这个对象的数据成员也会被串行化。树或者对象数据的结构,包括这些子对象,
18、构成了对象图。因为有些对象类所表示的数据在不断地改变,所以它们不会被串行化;例如,java.io.FileInputStream 、java.io.FileOutputStream和java.lang.Thread等流。如果一个可串行化对象包含某个不可串行化元素的引用,那么整个串行化操作就会失败,而且会抛出一个NotSerializableException。如果对象图包含一个不可串行化的引用,只要这个引用已经用transient关键字进行了标记,那么对象仍然可以被串行化。public class MyClass implements Serializable public transient
19、 Thread myThread; private String customerID; private int total;域存取修饰符对于被串行化的对象没有任何作用。写入到流的数据是字节格式,而且字符串被表示为UTF(文件系统安全的通用字符集转换格式)。transient关键字防止对象被串行化。public class MyClass implements Serializable public transient Thread myThread; private transient String customerID; private int total;实例分析例1:从第一个命令行参数
20、代表的文件中读字符,然后写入第二个参数代表的文件。问题分析本题中需要从文件读,写数据,需要使用到与文件有关的流FileReader/FileWriter。可以通过运行时参数提供文件的名称。使用带有Buffer功能的流为了提高读写数据的效率,可以使用带有buffer功能的流完成文件读写,并且可以以行为单位读写数据。使用类BufferedReader,BufferedWriterI/O流的链在程序中很少使用单独一个流对象,实际做法是将几个流对象串联起来处共同理数据。这样做会提高程序的效率。数据源- FileInputStream - BufferedInputStream - DataInputS
21、tream - 程序数据源- DataOutputStream - BufferedOutputStream - FileOutputStream -程序编写代码import java.io.*;public class TestBufferedStreams public static void main(String args) try FileReader input = new FileReader(args0); BufferedReader bufInput = new BufferedReader(input); FileWriter output = new FileWrite
22、r(args1); BufferedWriter bufOutput = new BufferedWriter(output); String line = bufInput.readLine(); while (line != null) bufOutput.write(line, 0, line.length(); bufOutput.newLine(); line = bufInput.readLine(); bufInput.close(); bufOutput.close(); catch (IOException e) e.printStackTrace(); 编译运行 javac
23、 TestBufferedStreams.java java TestBufferedStreams user.bat userbak.bat例2:使用管道流完成线程之间的通讯。1 问题分析本题中需要一个线程向管道写入数据,另外一个线程从管道中读出数据,需要使用到与管道有关的流PipedInputStream和PipedOutputStream。2 使用管道流PipedInputStream和PipedOutputStream管道流用来在线程间进行通信。一个线程的PipedInputStream对象从另一个线程的PipedOutputStream对象读取输入。要使管道流有用,必须有一个输入方和
24、一个输出方。 PipedInputStream in=new PipedInputStream(); PipedOutputStream out=new PipedOutputStream(in);3 编写代码import java.io.*;public class TestPipeStream public static void main(String args) try PipedInputStream in1 = new PipedInputStream(); PipedOutputStream out1 = new PipedOutputStream(in1); PipedInpu
25、tStream in2 = new PipedInputStream(); PipedOutputStream out2 = new PipedOutputStream(in2); ThreadC tc = new ThreadC(out1); ThreadZ tz = new ThreadZ(out2, in1); ThreadQ tq = new ThreadQ(in2); tc.start(); tz.start(); tq.start(); catch (Exception e) e.printStackTrace(); static class ThreadC extends Thr
26、ead DataOutputStream dos = null; public ThreadC(OutputStream os) dos = new DataOutputStream(os); public void run() while (true) try double d = Math.random(); dos.writeDouble(d); sleep(2); catch (Exception e) e.printStackTrace(); ; static class ThreadZ extends Thread DataOutputStream dos = null; Data
27、InputStream dis = null; public ThreadZ(OutputStream os, InputStream is) dos = new DataOutputStream(os); dis = new DataInputStream(is); public void run() while (true) try double d = dis.readDouble(); dos.writeDouble(d); catch (Exception e) e.printStackTrace(); ; static class ThreadQ extends Thread Da
28、taInputStream dis = null; public ThreadQ(InputStream is) dis = new DataInputStream(is); public void run() while (true) try double d = dis.readDouble(); System.out.println(d); catch (Exception e) e.printStackTrace(); ; 编译运行 javac TestPipeStream.java java TestPipeStream例3:保存所有的Person对象到文件并以对象的方式读出来1 问
29、题分析本题中需要对文件读,写对象数据,需要使用到与对象有关的流ObjectInputStream/ObjectOutputStream。 2 使用对象的读写流 ObjectOutputStream用于将一个对象输出,输出对象使用的方法为writeObject(Object obj) ObjectInputStream用于读取一个对象,读取对象使用的方法为readObject()注意: 被读写的对象必须是已序列化的类的对象,即要实现要Serializable接口。3 编写代码import java.io.*;import java.util.*;class Person implements Serializable String name = null; public Person(String s) name = s; public String toString() return name; public class TestObjectStream public static void main(String args) ObjectOutputStream oos = null; ObjectInputStream ois = null; try File f
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1