Java IO
一. 输入和输出
可将Java库的IO类分割为输入与输出两个部分。通过继承,从InputStream(输入流)衍生的所有类都拥有名为read()的基本方法,用于读取单个字节或者字节数组。类似地,从OutputStream衍生的所有类都拥有基本方法write(),用于写入单个字节或者字节数组。Java类库的设计者将所有与输入有关的类都从InputStream继承,而与输出有关的所有类都从OutputStream继承。
*1.InputStream的类型
InputStream的作用是标志那些从不同起源地产生输入的类。这些起源地包括(每个都有一个相关的InputStream子类):
(1) 字节数组
(2) String对象
(3) 文件
(4) “管道”。
(5) 一系列其他流,以便我们将其统一收集到单独一个流内。
(6) 其他起源地,如Internet连接等。
除此之外,FilterInputStream 也属于InputStream 的一种类型,用它可为“破坏器”类提供一个基础类,以便将属性或者有用的接口同输入流连接到一起。
类 | 功能 | 构造器参数 / 如何使用 |
---|---|---|
ByteArrayInputStream | 允许内存中的一个缓存区作为InputStream使用 | 缓冲区,字节将从中取出 / 作为一种数据源:将其同一个FilterInputStream对象相连以提供一个有用的接口。 |
StringBufferInputStream | 将String 转换成InputStream | 字符串。底层实现实际使用StringBuffer / 作为一种数据源:通过将其同一个FilterInputStream 对象相连以提供一个有用的接口 |
FileInputStream | 用于从文件读取信息 | 字符串,表示文件名、文件或FileDescriptor对象 / 作为一种数据源:将其同一个FilterInputStream对象相连以提供一个有用的接口。 |
PipedInputStrem | 产生用于写入相关PipleOutputStream的数据。实现“管道化”概念 | PipleOutputStream / 作为多线程中的数据源:将其与FilterInputStream 对象相连以提供一个有用的接口 |
SequenceInputStream | 将两个或更多的InputStream 对象转换成单个InputStream | 两个InputStream 对象或一个容纳InputStream对象的容器Enumeration / 作为一种数据源:将其与FilterInputStream 对象相连以提供一个有用的接口 |
FilterInputSTream | 抽象类,作为“装饰器”的接口。其中“装饰器”为其他的InputStream类提供有用功能,见下表FilterInputStream | 见下表FilterInputStream |
*2.OutputStream的类型
这一类别包括的类决定了我们的输入往何处去:一个字节数组(但没有String;假定我们可用字节数组创建一个);一个文件;或者一个“管道”。
类 | 功能 | 构造器参数 / 如何使用 |
---|---|---|
ByteArrayInputStream | 在内存中创建缓冲区。所有送往“流”的数据都要放置在此缓存区 | 缓冲区初始化尺寸(可选的) / 用于指定数据的目的地:将其与FilterInputStream对象相连以提供有用接口 |
FileOutputStream | 用于将信息写至文件 | 字符串,表示文件名、文件或FileDescription对象 / 用于指定数据的目的地:将其与FilterInputStream对象相连以提供有用接口 |
PipleOutputStream | 任何写入其中的信息都会自动作为相关PipedInputStrem 的输出。实现管道化的概念 | PipedInputStrem / 用于指定数据的目的地:将其与FilterInputStream对象相连以提供有用接口 |
FilterOutStream | 抽象类,作为“装饰器”的接口。其中,“装饰器”为其他OutputStream提供有用功能 | 见下表FilterOutStream |
二.增添属性和有用的接口
FilterInputStream 和FilterOutputStream 提供了相应的装饰器接口,用于控制一个特定的输入流(InputStream)或者输出流(OutputStream)。它们分别是从InputStream 和OutputStream 衍生出来的。此外,它们都属于抽象类,在理论上为我们与一个流的不同通信手段都提供了一个通用的接口。事实上,FilterInputStream 和FilterOutputStream 只是简单地模仿了自己的基础类,它们是一个装饰器的基本要求。
1.通过FilterInputStream从InputStream里读入数据
FilterInputStream 类要完成两件全然不同的事情。其中,DataInputStream 允许我们读取不同的基本类型数据以及String 对象(所有方法都以“read”开头,比如readByte(),readFloat()等等)。伴随对应的DataOutputStream,我们可通过数据“流”将基本类型的数据从一个地方搬到另一个地方。这些“地方”是由下表那些类决定的。
其他FilterInputStream类则在内部修改InputStream的行为方式:是否缓冲,是否保留它所读过的行(允许我们查询行数或设置行数),以及是否把单一字符推回输入流等等。最后两个类看起来更像是为了创建一个编译器(它们被添加进来可能是为了对“用Java构建编译器”实验提供支持),因此我们一般编程中不会用到它们。
FilterInputStream类型
类 | 功能 | 构造器参数 / 如何使用 |
---|---|---|
DataInputStream | 与DatatOutputStream搭配使用,因此我们可以按照可移植方式从流读取基本数据类型(int 、char、long等) | InputStream / 包含用于读取基本类型数据的全部接口 |
BufferedInputStream | 使用它可以防止每次读取时都得进行实际写操作。代表“使用缓冲区” | InputStream,可以指定缓冲区的大小(可选的)/ 本质上不提供接口,只不过向进程中添加缓冲区所必须的。与接口对象搭配。 |
LineNumberInputStream | 跟踪输入流中的行号,可用getLineNumber()和SetLineNumber(int) | InputStream / 仅增加了行号,因此可能要与接口对象搭配使用 |
PushbackInputStream | 具有“能弹出一个字节的缓冲区”。因此可以将读到的最后一个字符回退。 | InputStream / 通常作为编译器的扫描器,之所以包含在内是因为Java编译器的需要,我们可能永远用不到 |
2. 通过FilterOutputStream向OutputStream写入
与DataInputStream对应的是DataOutputStream,它可以将各种基本数据类型以及String对象格式化输出“流”中,这样以来,任何机器上的任何DataInputStream都能够读取它们。所有方法都以“write”开头,例如writeByte()、writeFloat()等。
PrintStream最初的目的表示为了可视化格式打印所有的基本数据类型以及String对象。这和DataOutpuStream不同,后者的目的是将数据元素置入“流”中,使DataOutputStream能够可移植地重构它们。
PrintStream可能会有些问题,因为它捕捉了所有IOException(因此,我们必须使用checkError()自行测试错误状态,如果出现错误它返回true)。另外,PrintStream也未完全国际化,不能以平台无关的方式处理换行动作(这些问题在printWrite中得到了解决)。
BufferOutputStream是一个修改过的OutputStream,它对数据流使用缓冲技术;因此当我们每次向流写入时,不必每次都进行实际的物理写动作。所以在进行输出时,我们可能更经常的是使用它。
FilterOutputStream:
类 | 功能 | 构造器参数 / 如何使用 |
---|---|---|
DataOutputStream | 与DatatOutputStream搭配使用,因此我们可以按照可移植方式向流中写入基本数据类型(int 、char、long等) | OutputStream / 包含用于写入基本数据类型的全部接口 |
PrintStream | 用于产生格式化输出。其中DataOutputStream处理数据的存储,PringStream处理显示 | OutputStream / 可以用boolean指示是否在每次换行时清空缓冲区(可选的)应该是对OutputStream对象的“final”封装。可能会经常使用它。 |
BufferedOutputStream | 使用它以避免每次发送数据时都要进行实际的写操作。代表“使用缓冲区”。 | OutputStream,可以指定缓冲区的大小(可选的) / 本质上并不提供接口,只不过是向进程中添加缓冲区所必须的。与接口对象搭配 |
三.Reader和Writer
InputStream 和 OutputStream在以面向字节形式的I/O中仍可以提供极有价值的功能,Reader和Writer则提供兼容Unicode与面向字符的I/O功能。另外:
1) Java1.1向InputStream和OutputStream的继承层次结构中添加了一些新类,所以显然这两个类是不会被取代的。
2)有时我们必须把来自于“字节”层次结构中的类和“字符”层次结构中的类结合起来使用。我了实现这个目的,要用到“适配器”(adapter)类:InputStreamReader可以把InputStream转换为Reader,而OutputStreamWriter可以把OutputStream转换为Writer。
设计Reader和Writer继承层次结构主要是为了国际化。老的I/O流继承层次结构仅支持8位字节流,并且不能很好的处理16位的Unicode字符。由于Unicode用于字符国际化(Java本身的char也是16位的Unicode),所以添加Reader和Writer继承层次结构就是为了在所有的I/O操作中都支持Unicode。
补充:字符编码
编码表的由来
计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。
常见的编码表
ASCII 码
ASCII 码总共有128个,用1个字节的低七位表示,0~31 是控制字符如换行、回车、删除等,32~126 是打印字符,可以通过键盘输入并且能够显示出来。
ISO-8859-1
128个字符显然是不够用的,于是ISO组织在ASCII 码基础上又制定了一系列标准来拓展 ASCII 编码,它们是ISO-8859-1 至 ISO-8859-15,其中 ISO-8859-1 涵盖了大多数西欧语言字符,所以应用的最广泛。ISO-8859-1 仍然是单字节编码,它总共能表示 256 个字符。
GB2312
GB2312 的全称是 《信息技术 中文编码字符集》,它是双字节编码,总的编码范围是 A1~F7,其中A1 ~ A9 是符号区,总共包含682个字符; B0~F7 是汉字区,包含6763个汉字。
GBK
全称是《汉字内码扩展规范》,它的出现是为了拓展 GB2312 ,并加入更多的汉字。它的编码是和GB2312 兼容的,也就是说用GB2312 编码的汉字可以用GBK 来解码,并且不会有乱码。
UTF-16
说到UTF 必须提到 Unicode ,ISO 试图创建一个全新的超语言字典,世界上所有的语言都可以通过这个字典来相互翻译。UTF-16 具体定义了Unicode 在计算中的存取方法。UTF-16 用两个字节来表示 Unicode 的转化格式,采用定长的表示方法,即不论什么字符都可以用两个字节表示。
UTF-8
UTF-16 存在存储空间浪费。UTF-8 采用了一种变长技术,每个编码区域有不同的字码长度。不同类型的字符可以由1~6 个字节组成。如果是一个字节,最高位为0,则表示这是 1 个 ASCII 字符,可见,所有ASCII 编码已经是UTF-8了。
编码:字符串à字节数组
解码:字节数组à字符串
转换流的编码应用
可以将字符按指定编码格式存储。
可以对文本数据按指定编码格式来解读。
指定编码表的动作由构造器完成。
1.数据的来源和去处
下面的表展示了在两个继承层次结构中,信息的来源和去处之间的对应关系。
字节 | 字符 |
---|---|
InputStream | Reader,适配器InputStreamReader |
OutputStream | Writer, 适配器OutStreamReader |
FileInputStream | FileReader |
FileOutputStream | FileWriter |
StringBufferInputStream(已弃用) | StringReader |
无相应的类 | StringWriter |
ByteArrayInputStream | CharArrayReader |
ByteArrayOutputStream | CharArrayWriter |
PipedInputStream | PipleReader |
PipedOutputStream | PipedWriter |
2.更改流的行为
对于InputStream和OutputStream来说,我们会使用FilterInputStream
和FilterOutputStream的装饰器子类来修改“流”以满足特殊要求。Reader和Writer的类继承层次结构继续沿用相同的思想———但是并不完全相同。
在下表中,相对于前一个表格来说,左右之间的对应关系的近似程度更加粗略一些。造成这种差别的原因是因为类的组织形式不同;尽管BufferedOutputStream是FilterOutputStream的子类,但是BufferedWriter并不是FilterWriter的子类(尽管FilterWriter是抽象类,没有任何子类,把它放在那里也只是把它作为一个占位符,或仅仅让我们不会对他所在的地方产生疑惑)。然而,这些类的接口却十分相似。
字节过滤器 | 字符过滤器 |
---|---|
FilterOutputStream | FilterReader |
FilterIntputStream | FilterWriter(抽象类,没有子类) |
BufferedIntputStream | BufferedWriter |
BufferedOutputStream | BufferedWriter |
DataInputStream | 使用DataInputStream(除了当需要使用readLine()时以外,这时应该使用BufferedReader) |
PrintStream | PrintWriter |
LineNumberInputStream(已弃用) | LineNumberReader |
StreamTokenzier | StreamTokenzier(使用接受Reader的构造器) |
PushbackInputStream | PushbackReader |
有一点很清楚,无论我们何时使用readLine(),都不应该使用DataInputStream(这会遭到编译器的强烈反对),而应该使用BufferedReader。除了这一点外,DataInputStream仍是I/O类库的首选成员。
为了更容易的过度到使用PrintWriter,它提供了一个既能接受Writer对象又能接受任何OutputStream对象的构造器。PrintWriter的格式化接口实际上是与PrintStream相同。
四.自我独立的类:RandomAccessFile
RandomAccessFile适用于由大小已知的记录组成的文件,以便我们能用seek()从一条记录移至另一条;然后读取或修改那些记录。各记录的长度并不一定相同;只要知道它们有多大以及置于文件何处即可。
有用。
首先,我们有点难以相信RandomAccessFile 不属于InputStream 或者OutputStream 分层结构的一部分。除了恰巧实现了DataInput 以及DataOutput(这两者亦由DataInputStream 和DataOutputStream 实现)接口之外,它们与那些分层结构并无什么关系。它甚至没有用到现有InputStream 或OutputStream 类的功能——采用的是一个完全不相干的类。该类属于全新的设计,含有自己的全部(大多数为固有)方法。之所以要这样做,是因为RandomAccessFile 拥有与其他IO 类型完全不同的行为,因为我们可在一个文件里向前或向后移动。不管在哪种情况下,它都是独立运作的,作为Object 的一个“直接继承人”使用。从根本上说,RandomAccessFile 类似DataInputStream 和DataOutputStream 的联合使用。其中,getFilePointer()用于了解当前在文件的什么地方,seek()用于移至文件内的一个新地点,而length()用于判断文件的最大长度。此外,构建器要求使用另一个自变量(与C 的fopen()完全一样),指只是随只是随机读(”r”),还是读写兼施(”rw”)。这里没有提供对“只写文件”的支持。也就是说,假如是从DataInputStream 继承的,那么RandomAccessFile 也有可能能很好工作。还有更难对付的。很容易想象我们有时要在其他类型的数据流中搜索,比如一个ByteArrayInputStream,但搜索方法只有RandomAccessFile 才会提供。而后者只能针对文件才能操作,不能针对数据流操作。此时,BufferedInputStream 确实允许我们标记一个位置(使用mark(),它的值容纳于单个内部变量中),并用reset()重设那个位置。但这些做法都存在限制,并不是特别有用。
五.Java IO中常用的类
以上图片来源:https://www.cnblogs.com/ylspace/p/8128112.html
六 .File类
- java.io.File类:文件和目录路径名的抽象表示形式,与平台无关
- File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。
- File对象可以作为参数传递给流的构造函数
- File类的常见构造方法:public File(String pathname)
以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
public File(String parent,String child)
以parent为父路径,child为子路径创建File对象。
File的静态属性String separator存储了当前系统的路径分隔符。
在UNIX中,此字段为’/’,在Windows中,为’\’
此节内容转载自:https://www.cnblogs.com/baixl/p/4170599.html
七.IO流的典型应用
1.缓冲输入文件.
如果想要打开一个文件用于字符输入,可以使用String或File对象作为文件名的FileReader。为了提高速度,我么希望对那个文件进行缓冲,那么我们将所产生的引用传给一个BufferedReader构造器,由于BufferedReader也提供readLine()方法,所以这是我们的最终对象和进行读取的接口。当readLine()返回null时,你就打到了文件的末尾。
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderFile {
public static String read(String fileName) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(fileName));
String s;
StringBuffer buffer = new StringBuffer();
while ((s = reader.readLine()) != null) {
buffer.append(s + "\n");
}
reader.close();
return buffer.toString();
}
public static void main(String[] args) throws IOException {
System.out.println(read("BufferedReaderFile.java"));
}
}
2.从内存输入
在下面的示例中,从上面的BuffereredInputFile.read()读入的String结果被用来创建一个StringReader。然后调用read()每次读取一个字符,并把它发送到控制台。
public static String read(String fileName) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(fileName));
String s;
StringBuffer buffer = new StringBuffer();
while ((s = reader.readLine()) != null) {
buffer.append(s + "\n");
}
reader.close();
return buffer.toString();
}
public static void main(String[] args) throws IOException {
StringReader buffer = new StringReader(read("MemoryInput.java"));
int c;
while ((c = buffer.read()) != -1) {
System.out.println((char) c);
}
}
}
注意read()是以int形式返回下一字节,因此必须类型转换为char才能正确打印。
3.格式化内存输入
要读取格式化数据,可以使用DataInputStream,它是一个面向自己的I/O类(不是面向字符的)。因此我们必须使用InputStream类而不是Reader类。当然,我们可以用InputStream以字节的形式读取任何数据(例如一个文件),不过,在这里使用的是字符串。
public class FormattedMemoryInput {
public static String read(String fileName) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(fileName));
String s;
StringBuffer buffer = new StringBuffer();
while ((s = reader.readLine()) != null) {
buffer.append(s + "\n");
}
reader.close();
return buffer.toString();
}
public static void main(String[] args) throws IOException{
try {
DataInputStream input = new DataInputStream(
new ByteArrayInputStream(read("FormattedMemoryInput.java").getBytes()));
while (true)
System.out.println((char) input.readByte());
} catch (EOFException e) {
System.out.println("End of stream"); // 读到文件结尾时会抛出异常
}
}
}
必须为ByteArrayInputStream提供字节数组,为了产生该数组String包含了一个可以实现此项工作的getBytes()方法。所产生的ByteArrayInpuStream是一个适合传递给DataInputStream的InputStream。
如果我们从DataInputStream用readByte()一次一个字节的读取,那么任何字节的值都是合法的结果,因此返回值不能用来检测输入是否结束。相反,我们可以使用available()方法查看还有多少可供存取的字符。下面这个例子演示了怎样一次一个字节地读取文件:
public class TestEOF {
public static void main(String[] args) throws IOException {
DataInputStream in = new DataInputStream(
new BufferedInputStream(new FileInputStream("TestEOF.java")));
while (in.available() != 0)
System.out.println((char) in.readByte());
}
}
注意,available()的工作方式会随着所读取的媒介类型的不同而有所不同,字面意思就是“在没有阻塞的情况下所能读取的字节数”。对于文件,这意味着整个文件;但是对于不同类型的流,可能就不是这样的,因此要谨慎使用。
我们来看看哪些地方可以用available()方法来获取文件大小,进而用来定义缓冲数组的长度:
1.在本地文件文件中,这里我一般是直接使用的。
2.网络中的文件
a.比如web 中http 里面的文件流里面,第一种情况有content-length,那么小文件的情况,我们是直接可以通过request 获取该属性,也就知道文件的大小了。
b.在某些情况下,比如文件比较大,采用采用分段方式,长连接的方式发送,不能一次知道文件大小,http 的header 里面就没有上面的content-length 属性,而是变成了Transfer-Encoding: chunked属性,这表示分段发送信息,但是对整个文件的接受,可以通过一些标志位,或者一些超时限制等方法处理,这里不具体研究了。
c.socket 传输文件,是没重写available方法的,也就是说我们不能通过这个知道流的字节数,一般情况下,我们可以先发送一段自定义的header 过去,描述文件大小,然后再循环持续获取流信息。
我们也可以通过捕获异常(EOFException)来检测输入的末尾。但是,使用异常进行流控制,被认为是对异常特性的错误使用。
4.基本的文件输出
FileWriter对象可以向文件写入数据。首先,创建一个与指定文件连接的FileWriter。实际上,我们通常会用BufferedWriter将其包装起来用以缓冲输出(尝试移除此包装来感受对性能的影响——缓冲往往能显著地增加IO操作的性能)
public class BasicFileOutput {
static final String file = "BasicFileOutput.java";
public static String read(String fileName) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(fileName));
String s;
StringBuffer buffer = new StringBuffer();
while ((s = reader.readLine()) != null) {
buffer.append(s + "\n");
}
reader.close();
return buffer.toString();
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new StringReader(read(file)));
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
int lineCount = 1;
String s;
while ((s = reader.readLine()) != null) {
out.println(lineCount++ + ":" + s);
}
out.close();
System.out.println(read(file));
}
}
一旦读完输入数据流,readLine()会返回null。我们可以看到要为out显示调用close()。如果我们不为所有的输出文件调用close(),就会发现缓冲区内容不会被刷新清空,那么它们也就不完整。
文本文件输出的快捷方式
JavaSE5 在PrintWriter中添加了一个辅助构造器,使得你不必在每次希望创建文本文件并向其中写入时,都去执行所有的装饰工作。下面是这种快捷方式重写的BasicFileOutput.java
public class BasicFileOutput {
static final String file = "BasicFileOutput.java";
public static String read(String fileName) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(fileName));
String s;
StringBuffer buffer = new StringBuffer();
while ((s = reader.readLine()) != null) {
buffer.append(s + "\n");
}
reader.close();
return buffer.toString();
}
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new StringReader(read(file)));
PrintWriter out = new PrintWriter(file); // 这地方未执行所有的装饰工作
int lineCount = 1;
String s;
while ((s = reader.readLine()) != null) {
out.println(lineCount++ + ":" + s);
}
out.close();
System.out.println(read(file));
}
}
查看PrintWriter的带有String类型的参数的构造函数,源码如下:
public PrintWriter(String fileName) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),
false);
}
通过源码可知装饰工作已在源码中完成了。
5.存储和恢复数据
PrintWriter可以对数据进行格式化(既根据数据的实际类型输出和或写入),以便人们的阅读。但是为了输出可供一个“流”恢复的数据,我们需要用DatatOutputStream写入数据,并用DataInputStream恢复数据。当然,这些流可以是任何形式,但在下面的示例中使用的是一个文件,并且对于读和写都进行了缓存处理。注意DataOutputStream和DataInputStream是面向字节的,因此要使用InputStream和OutputStream
public class StoringAndRecoveringData {
public static void main(String[] args) throws IOException {
DataOutputStream out = new DataOutputStream(
new BufferedOutputStream(new FileOutputStream("D:\\xx.txt")));
out.writeDouble(2423.343);
out.writeInt(1314);
out.writeChars("d");
out.writeUTF("utf-8");
out.writeDouble(4545.5656);
out.close();
DataInputStream in = new DataInputStream(
new BufferedInputStream(new FileInputStream("D:\\xx.txt")));
System.out.println(in.readDouble());
System.out.println(in.readInt());
System.out.println(in.readChar());
System.out.println(in.readUTF());
System.out.println(in.readDouble());
// 下面读取的顺序必须和上面写入的顺序一样
}
}
如果我们使用DataOutputStream写入数据,Java保证我们可以使用DataInputStream准确地读取数据——无论读和写数据的平台多么不同。
6.读写随机访问文件
使用RandomAccessFile,类似于组合使用了DatInpuStream和DataOutputStream(因为它实现了相同的接口:DataInput和DataOutput)。另外我们可以看到,利用seek()可以在文件中到处移动,并修改文件中的某个值。
在使用RandomAccessFile时,你必须知道文件排版,这样才能准确地操作它。RandomAccessFile拥有读取基本类型和UTF-8字符串的各种具体方法。
public class UsingRandomAccessFile {
static String file = "rtest.dat";
static void display() throws IOException {
RandomAccessFile rf = new RandomAccessFile(file, "r");
for (int i = 0; i < 7; i++) {
System.out.println("Value " + i + " : " + rf.readDouble());
}
System.out.println(rf.readUTF());
rf.close();
}
public static void main(String[] args) throws IOException {
RandomAccessFile rf = new RandomAccessFile(file, "rw");
for (int i = 0; i < 7; i++) {
rf.writeDouble(i * 1.44);
}
rf.writeUTF("the end of the file");
rf.close();
display();
rf = new RandomAccessFile(file, "rw");
rf.seek(5 * 8);
rf.writeDouble(47.0001);
rf.close();
display();
}
}
display()方法打开了一个文件,并以double值的形式显示了其中的七个元素。在main()中,首先创建了文件,然后打开并修改了它。因为double总是8字节长,所以为了用seek()查找第5个双精度值,你只需5*8来产生查找位置。
正如先前所指,RandomAccessFile除了实现DataInput和DataOutput接口之外,有效地与I/O继承层次结构的其他部分实现了分离。因为它不支持装饰,所以不能将其与InputStream及OutputStream子类的任何部分组合起来。
可以自行选择的是第二个构造器参数:我们可指定以“只读”(r)方式或“读写”(rw)方式打开一个RandomAccessFile文件重点内容