1、第14章 流式IO和文件第14章 流式I/O和文件本模块讨论文件,socket和其他数据源使用的流式I/O机制。第一节 相关问题讨论 以下为与本模块内容有关的问题: Java编程语言中使用什么机制来读写文件?第二节 目 标在完成了本模块的学习后,你应当能够: 描述和使用java.io包的流式思想 构造文件和过滤器流,并恰当地使用它们 区别流与读者和作者,并进行合适的选择 考察并操作文件和目录 读、写和更新文本和数据文件 使用Serialization接口来保持对象的状态第三节 流式I/O流式I/O 流是字节的源或目的。 两种基本的流是: 输入流 输出流 结点流对特定的地方读写 过滤流使用结点流
2、作为输入或输出本模块考察了Java编程语言如何使用流来处理字节和字符I/O(包括stdio,stdout和stderr)。下面几节将考察有关处理文件和操作它们所包含的数据的特定细节。14.3.1 流的基础知识一个流是字节的源或目的。次序是有意义的。例如,一个需要键盘输入的程序可以用流来做到这一点。两种基本的流是:输入流和输出流。你可以从输入流读,但你不能对它写。要从输入流读取字节,必须有一个与这个流相关联的字符源。在java.io包中,有一些流是结点流,即它们可以从一个特定的地方读写,例如磁盘或者一块内存。其他流称作过滤器。一个过滤器输入流是用一个到已存在的输入流的连接创建的。此后,当你试图从
3、过滤输入流对象读时,它向你提供来自另一个输入流对象的字符。 14.3.2 InputStream方法InputStream方法 三个基本的read()方法 int read() int read(byte ) int read(byte, int ,int ) 其他方法 void close() int available() skip(long) boolean markSupported() void mark(int) void reset(int) int read() int read(byte ) int read(byte, int ,int )这三个方法提供对输入管道数据的存取
4、。简单读方法返回一个int值,它包含从流里读出的一个字节或者-1,其中后者表明文件结束。其它两种方法将数据读入到字节数组中,并返回所读的字节数。第三个方法中的两个int参数指定了所要填入的数组的子范围。注考虑到效率,总是在实际最大的块中读取数据。void close()你完成流操作之后,就关闭这个流。如果你有一个流所组成的栈,使用过滤器流,就关闭栈顶部的流。这个关闭操作会关闭其余的流。int available()这个方法报告立刻可以从流中读取的字节数。在这个调用之后的实际读操作可能返回更多的字节数。skip(long)这个方法丢弃了流中指定数目的字符。boolean markSupporte
5、d()void mark(int)void reset()如果流支持“回放”操作,则这些方法可以用来完成这个操作。如果mark()和reset()方法可以在特定的流上操作,则markSupported()方法将返回ture。mark(int)方法用来指明应当标记流的当前点和分配一个足够大的缓冲区,它最少可以容纳参数所指定数量的字符。在随后的read()操作完成之后,调用reset()方法来返回你标记的输入点。14.3.3 OutputStream方法OutputStream方法 三个基本的write()方法 int write() int write(byte ) int write(byte
6、, int ,int ) 其他方法 void close() void flush() void write(int) void write(byte ) void write(byte , int, int)这些方法写输出流。和输入一样,总是尝试以实际最大的块进行写操作。void close()当你完成写操作后,就关闭输出流。如果你有一个流所组成的栈,就关闭栈顶部的流。这个关闭操作会关闭其余的流。void flush()有时一个输出流在积累了若干次之后才进行真正的写操作。flush()方法允许你强制执行写操作。第四节 基本的流类在java.io包中定义了一些流类。下图表明了包中的类层次。一些
7、更公共的类将在后面介绍。基本的流类 FileInputStream和FileOutputStream BufferInputStream和BufferOutputStream DataInputStream和DataOutputStream PipedInputStream和PipedOutputStream14.4.1 FileInputStream和FileOutputStream这些类是结点流,而且正如这个名字所暗示的那样,它们使用磁盘文件。这些类的构造函数允许你指定它们所连接的文件。要构造一个FileInputStream,所关联的文件必须存在而且是可读的。如果你要构造一个FileOu
8、tputStream而输出文件已经存在,则它将被覆盖。FileInputStream infile =new FileInputStream(myfile.dat);FileOutputStream outfile =new FileOutputStream(results.dat);14.4.2 BufferInputStream和BufferOutputStream这些是过滤器流,它们可以提高I/O操作的效率。14.4.3 DataInputStream和DataOutputStream这些过滤器通过流来读写Java基本类。例如:DataInputStream方法byte readByte
9、()long readLong()double readDouble()DataOutputStream方法void writeByte(byte)void writeLong(long)void writeDouble(double)注意DataInputStream和DataOutputStream的方法是成对的。这些流都有读写字符串的方法,但不应当使用这些方法。它们已经被后面所讨论的读者和作者所取代。14.4.4 PipedInputStream和PipedOutputStream管道流用来在线程间进行通信。一个线程的PipedInputStream对象从另一个线程的PipedOutpu
10、tStream对象读取输入。要使管道流有用,必须有一个输入方和一个输出方。第五节 URL输入流URL输入流.URL imageSource;try imageSource = new URL( catch ( MalformedURLException e) images0 = getImage(imageSource, Duke/T1.gif);除了基本的文件访问之外,Java技术提供了使用统一资源定位器(URL)来访问网络上的文件。当你使用Applet的getDocumentBase()方法来访问声音和图象时,你已经隐含地使用了URL对象。String imageFile = new St
11、ring (images/Duke/T1.gif);images0 = getImage(getDocumentBase(), imageFile);然而,你必须象下面的程序那样提供一个直接的URL.URL imageSource;try imageSource = new URL( catch ( MalformedURLException e) images0 = getImage(imageSource, Duke/T1.gif);14.5.1 打开一个输入流你可以通过存储文档基目录下的一个数据文件来打开一个合适的URL输入流。 1.InputStream is = null; 2.St
12、ring datafile = new String(Data/data.1-96); 3.byte buffer = new byte24; 4.try 5./ new URL throws a MalformedURLException 6./ URL.openStream() throws an IOException 7.is = (new URL(getDocumentBase(), datafile).openStream(); 8. catch (Exception e) 现在,你可以就象使用FileInputStream对象那样来用it来读取信息: 1.try 2.is.rea
13、d(buffer, 0, buffer.length); 3. catch (IOException e1) 警告记住大多数用户进行了浏览器的安全设置,以防止Applet存取文件。第六节 读者和作者14.6.1 UnicodeJava技术使用Unicode来表示字符串和字符,而且它提供了16位版本的流,以便用类似的方法来处理字符。这些16位版本的流称为读者和作者。和流一样,它们都在java.io包中。读者和作者中最重要的版本是InputStreamReader和OutputStreamWriter。这些类用来作为字节流与读者和作者之间的接口。当你构造一个InputStreamReader或Ou
14、tputStreamWriter时,转换规则定义了16位Unicode和其它平台的特定表示之间的转换。14.6.2 字节和字符转换缺省情况下,如果你构造了一个连接到流的读者和作者,那么转换规则会在使用缺省平台所定义的字节编码和Unicode之间切换。在英语国家中,所使用的字节编码是:ISO 8859-1。你可以使用所支持的另一种编码形式来指定其它的字节编码。在native2ascii工具中,你可以找到一个关于所支持的编码形式的列表。使用转换模式,Java技术能够获得本地平台字符集的全部灵活性,同时由于内部使用Unicode,所以还能保持平台独立性。14.6.3 缓冲读者和作者因为在各种格式之间
15、进行转换和其它I/O操作很类似,所以在处理大块数据时效率最高。在InputStreamReader和OutputStreamWriter的结尾链接一个BufferedReader和BufferedWriter是一个好主意。记住对BufferedWriter使用flush()方法。14.6.4 读入字符串输入下面这个例子说明了从控制台标准输入读取字符串所应当使用的一个技术。 1.import java.io.*; 2.public class CharInput 3.public static void main (String args) throws 4.java.io.IOExceptio
16、n 5.String s; 6.InputStreamReader ir; 7.BufferedReader in; 8.ir = new InputStreamReader(System.in); 9.in = new BufferedReader(ir); 10. 11.while (s = in.readLine() != null) 12.System.out.println(Read: + s); 13. 14. 15.14.6.5 使用其它字符转换如果你需要从一个非本地(例如,从连接到一个不同类型的机器的网络连接读取)的字符编码读取输入,你可以象下面这个程序那样,使用显式的字符编码
17、构造ir=new InputStreamReader(System.in, “8859_1”);注如果你通过网络连接读取字符,就应该使用这种形式。否则,你的程序会总是试图将所读取的字符当作本地表示来进行转换,而这并不总是正确的。ISO 8859-1是映射到ASCII的Latin-1编码模式。第七节 文 件14.7.1 创建一个新的File对象创建一个新的File对象 File myFile;myFile = new File(mymotd); myFile = new File(/, mymotd);/ more useful if the directory or filename is/
18、a variable File myDir = new File(/);myFile = new File(myDir, mymotd);File类提供了若干处理文件和获取它们基本信息的方法。 File myFile;myFile = new File(mymotd); myFile = new File(/, mymotd);/ more useful if the directory or filename is/ a variable File myDir = new File(/);myFile = new File(myDir, mymotd);你所使用的构造函数经常取决于你所使用的
19、其他文件对象。例如,如果你在你的应用程序中只使用一个文件,那么就会使用第一个构造函数。如果你使用一个公共目录中的若干文件,那么使用第二个或者第三个构造函数可能更容易。File类提供了独立于平台的方法来操作由本地文件系统维护的文件。然而它不允许你存取文件的内容。注你可以使用一个File对象来代替一个String作为FileInputStream和FileOutputStream对象的构造函数参数。这是一种推荐方法,因为它独立于本地文件系统的约定。第八节 文件测试和工具文件测试和工具 文件名String getName()String getPath()String getAbsolutePath
20、()String getParent()boolean renameTo(File newName) 文件测试boolean exists()boolean canWrite()boolean canRead()boolean isFile()boolean isDirectory()boolean isAbsolute()当你创建一个File对象时,你可以使用下面任何一种方法来获取有关文件的信息:14.8.1 文件名 String getName() String getPath() String getAbsolutePath() String getParent() boolean re
21、nameTo(File newName)14.8.2 文件测试 boolean exists() boolean canWrite() boolean canRead() boolean isFile() boolean isDirectory() boolean isAbsolute()14.8.3 通用文件信息和工具 long lastModified() long length() boolean delete()14.8.4 目录工具 boolean mkdir() String list()第九节 随机存取文件14.9.1 创建一个随机存取文件创建一个随机存取文件 用文件名myRAF
22、ile = new RandomAccessFile(String name, String mode); 用文件对象myRAFile = new RandomAccessFile(File file, String mode);你经常会发现你只想读取文件的一部分数据,而不需要从头至尾读取整个文件。你可能想访问一个作为数据库的文本文件,此时你会移动到某一条记录并读取它的数据,接着移动到另一个记录,然后再到其他记录每一条记录都位于文件的不同部分。Java编程语言提供了一个RandomAccessFile类来处理这种类型的输入输出。你可以用如下两种方法来打开一个随机存取文件: 用文件名myRAFi
23、le = new RandomAccessFile(String name, String mode); 用文件对象myRAFile = new RandomAccessFile(File file, String mode);mode参数决定了你对这个文件的存取是只读(r)还是读/写(rw)。例如,你可以打开一个打开一个数据库文件并准备更新:RandomAccessFile myRAFile;myRAFile = new RandomAccessFile(db/stock.dbf,rw);14.9.2 存取信息随机存取文件 long getFilePointer(); void seek(l
24、ong pos); long length();RandomAccessFile对象按照与数据输入输出对象相同的方式来读写信息。你可以访问在DataInputStrem和DataOutputStream中所有的read()和write()操作。Java编程语言提供了若干种方法,用来帮助你在文件中移动。 long getFilePointer();返回文件指针的当前位置。 void seek(long pos);设置文件指针到给定的绝对位置。这个位置是按照从文件开始的字节偏移量给出的。位置0标志文件的开始。 long length()返回文件的长度。位置length()标志文件的结束。14.9.
25、3 添加信息你可以使用随机存取文件来得到文件输出的添加模式。 myRAFile = new RandomAccessFile(java.log,rw);myRAFile.seek(myRAFile.length();/ Any subsequent write()s will be appended to the file第十节 串行化串行化 将一个对象存放到永久存储器上称为保持。 只有对象的数据被串行化。 标记为transient关键字的数据不被串行化。从JDK1.1开始具有的新特性包括导入java.io.Serializable接口和改变JVM使之支持将一个Java技术对象存放到一个流的能
26、力。将一个对象存放到某种类型的永久存储器上称为保持。如果一个对象可以被存放到磁盘或磁带上,或者可以发送到另外一台机器并存放到存储器或磁盘上,那么这个对象就被称为可保持的。java.io.Serializable接口没有任何方法,它只作为一个“标记者”,用来表明实现了这个接口的类可以考虑串行化。类中没有实现Serializable的对象不能保存或恢复它们的状态。14.10.1 对象图当一个对象被串行化时,只有对象的数据被保存;方法和构造函数不属于串行化流。如果一个数据变量是一个对象,那么这个对象的数据成员也会被串行化。树或者对象数据的结构,包括这些子对象,构成了对象图。因为有些对象类所表示的数据
27、在不断地改变,所以它们不会被串行化;例如,java.io.FileInputStream 、java.io.FileOutputStream和java.lang.Thread等流。如果一个可串行化对象包含对某个不可串行化元素的引用,那么整个串行化操作就会失败,而且会抛出一个NotSerializableException。如果对象图包含一个不可串行化的引用,只要这个引用已经用transient关键字进行了标记,那么对象仍然可以被串行化。 public class MyClass implements Serializable public transient Thread myThread;p
28、rivate String customerID; private int total;域存取修饰符对于被串行化的对象没有任何作用。写入到流的数据是字节格式,而且字符串被表示为UTF(文件系统安全的通用字符集转换格式)。transient关键字防止对象被串行化。 public class MyClass implements Serializable public transient Thread myThread;private transient String customerID;private int total;第十一节 读写一个对象流14.11.1 写对一个文件流读写对象是一个简单
29、的过程。考虑如下代码段,它将一个java.util.Data对象的实例发送到一个文件: 1.public class SerializeDate 2.SerializeDate() 3.Date d = new Date (); 4.try 5.FileOutputStream f = new 6.FileOutputStream(date.ser); 7.ObjectOutputStream s = new 8.ObjectOutputStream(f); 9.s.writeObject (d); 10.f.close (); 11. catch (IOException e) 12.e.p
30、rintStackTrace (); 13. 14. 15. 16.public static void main (String args) 17.new SerializeDate(); 18. 19. 14.11.2 读读对象和写对象一样简单,只需要说明一点readObject()方法将流作为一个Object类型返回,而且在使用那个类的方法之前,必须把它转换成合适的类名。 1.public class UnSerializeDate 2.UnSerializeDate () 3.Date d = null; 4.try 5.FileInputStream f = new 6.FileInputStream(
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1