Java基础:IO相关的类

1 缘起

Java的IO包是非常常用的,特别是文件操作相关的应用开发,
本文从源码全面解析IO相关的类,本着:先知道,后应用的理念(
如果不知道有这样的一个工具或者不是很清楚这个工具的相关特性,无法应用到实际的开发中)
先熟悉相关工具的特点,然后根据需要,选择合适的工具,
帮助读者轻松应对知识交流和考核。

2 结构

Java IO相关的类如下图所示,Java IO可分为字节流、字符流、文件和随机访问文件。
本文会逐一对这4个部分通过源码进行讲解。
在这里插入图片描述

3 输入流:InputStream

输入流InputStream是抽象类,表示字节输入流,其他表示输入流的类均继承该类。
提供的方法有如read、close等,如下图所示。
在这里插入图片描述

在这里插入图片描述

是输入数据->Object的媒介。
继承InputStream的类需要提供返回输入字节的方法。
继承InputStream抽象类的子类有FileInputStream、FilterInputStream等,具体类如下图所示。

在这里插入图片描述

3.1 FileInputStream

文件输入流。
从文件系统获取输入流(字节)。
FileInputStream用于读取原始字节流,如图像数据,如果要读取字符流,可考虑使用FileReader。

3.2 FilterInputStream

FilterInputStream包含一些其他输入流,用于基础数据转换或其他。
FilterInputStream只是简单重写了InputStream的所有方法,其子类会根据不同情况进一步重写,并提供其他字段和方法。
FilterInputStream子类有BufferedInputStream、DataInputStream和PushbackInputStream。

3.2.1 BufferedInputStream

BufferedInputStream为输入添加了新功能,即缓冲输入并支持标记和重置功能。
创建BufferedInputStream时,会在内部创建一个缓冲区数组,当读取或跳过流中的字节时,将根据需要从包含的输入流中重新填充内部缓冲区,每次填充多个字节。
标记操作会记录输入流的一个点,重置操作会从包含的输入流中读取新字节之前重新读取最近标记的所有字节。
应用:断点续传。

3.2.2 DataInputStream

DataInputStream允许应用程序以独立于机器的方式从输入流读取原始Java数据类型。
应用程序使用数据输出流来写入数据,这些数据可以有数据输入流读取。
DataInputStream不必多线程安全,线程安全使可选的,由该类的使用者维护。

3.2.3 PushbackInputStream

PushbackInputStream为输入流添加新功能,即“推回”或“未读”一个字节的能力。
在代码片段中读取特定字节分隔的无限数量字节的场景中非常有用。
在读取终止字节后,代码片段可以“未读”,这样在输入流下一次读取时,会重新读取被推回的字节。
如,由字符构成的字节可以使用运算符标识流终止,方法仅读取可以读取的,
遇到运算符时,然后将运算符推回,存入数组中,下次重新读取。

3.3 ObjectInputStream

ObjectInputStream反序列化之前使用ObjectOutputStream编写的原始数据和对象。
ObjectOutputStream和ObjectInputStream与FileOutputStream和FileInputStream一起使用时,
ObjectOutputStream和ObjectInputStream可以为应用程序提供对象图形持久化存储。
ObjectInputStream用于恢复以前序列化的对象。其他应用,如套接字流在主机之间传递对象,或远程通信系统中对参数的封装和解析。
ObjectInputStream确保从流创建的图形对象类型与Java虚拟机中的类型是匹配的,使用标准机制根据需要加载类。
只有支持Java.io.Serializable或Java.io.Externalizable接口的对象才能从流中读取。
readObject方法用于从流中读取对象。通常使用Java安全强制类型转换来获取相应的类型。
Java中,字符串和数组是对象,序列化时被视为对象,读取时需要将它们转换为预期的类型。
在DataInput上使用合适的方法可以从流中读取原始数据类型。
对象默认的的反序列化机制将对象字段的内容恢复为写入时的值和类型。
反序列化不会将声明为transient或static类型的字段进行反序列化(恢复数值和类型)。
对其他对象的引用会根据需要从流中读取这些对象。使用引用共享机制可以正确恢复图对象,反序列化时总是会新建对象,这样可以防止覆盖现有对象。
读取对象类似于通过构造函数新建对象。为对象分配内存并初始化为空对象。不可序列化类调用无参构造函数,然后从流中恢复可序列化类的字段,从最接近java.long.object的可序列化类开始,到具体的类结束。
实现Serializable接口允许对象序列化保存和恢复对象的全部状态,并允许类在写入流和读取流之间转化。
自动遍历对象间的引用,保存和恢复全部图。
在序列化和反序列化过程中需要特殊处理的可序列化类应实现如下方法:

private void writeObject(java.io.ObjectOutputStream stream)
      throws IOException;
private void readObject(java.io.ObjectInputStream stream)
      throws IOException, ClassNotFoundException;
private void readObjectNoData()
      throws ObjectStreamException;

readObject方法负责从相应的writeObject方法写入的流数据读取和恢复特定类的状态。
readObject方法无需关心父类或子类的状态。
通过ObjectInputStream读取数据将字段数据分配个对应的属性恢复对象状态。DataInput支持读取原始数据类型。
如果读取数据超过writeObject方法写入数据的边界,则会抛出异常OptionalDataException,eof属性值为true。
非对象读取超过了数据分配空间表明已经读取到了数据流的尾部,将会返回-1或者字节数作为结尾的标记,原始读取会抛出EOFException异常。
如果没有对应的writeObject方法,使用默认序列化数据的尾部标记分配数据的尾部。
从readExternal方法中原始读取和对象读取调用方式相同,如果流已经位于writeExternal方法写入数据的尾部,对象读取抛出异常OptionalDataExceptions,eof设置为true。如果使用旧ObjectStreamConstants.PROTOCOL_VERSION_1协议写入的流,不适用,
该协议中writeExternal方法写入的数据尾部没有标记,无法检测。
readObjectNoData方法用于初始化对象。当序列化流没有给出反序列化对象父类时初始化为特定的对象。
这种情况可能发生在接收方使用了与发送方不同版本的反序列化对象,并且接收方扩展了发送方的类。
如果序列化流被篡改,也可能发生这种情况。
因此,readObjectNoData对于正确初始化反序列化对象非常有用,即使源数据流时不完整的。
序列化不会读取或向未实现java.io.Serializable接口的对象字段赋值。
不可序列化对象的子类可以时序列化的,这种情况下,不可序列化类必须具有无参构造函数,以允许初始化字段。
子类负责保存和恢复不可序列化类的状态,通常,该类的字段时可访问的(public、package或protected),或者可以使用get或set方法恢复状态。
反序列化对象时发生的任何异常都会被ObjectInputStream捕获并终止读取。
实现Externalizable接口允许对象完全控制对象序列化的内容和形式,调用Externalizable接口的writeExternal和readExternal方法来保存和恢复对象状态,当由类实现时,可以使用ObjectOutput和ObjectInput的方法写入和读取状态,由对象处理发生的版本变化。
枚举常量的反序列化不同于普通的可序列化或外部化对象,枚举常量的序列化形式仅由名称组成,不发送常量的字段值。
反序列化枚举常量,ObjectInputStream从流中读取常量名称,然后通过调用Enum.ValueOf(Class, String)获取反序列化常量(该方法使用枚举常量的基类型并接收常量名称作为参数)。
与其他可序列化或可外部化对象一样,枚举常量可以用作后续在序列化流中出现的反向引用目标。
无法自定义反序列枚举常量的过程:反序列化期间,忽略由枚举类型定义的任何特定的readObject、readObjectNoData和readResolve方法。同样,任何serialPersistentFields或serialVersionUID字段声明也会被忽略,所有枚举类型都有固定的serialVersionUID,即0L。

3.4 PipedInputStream

管道输入流应与管道输出流连接,管道输入流提供写入管道输出流的(字节)数据。
通常,一个线程从PipedInputStream对象读取数据,另一个线程将数据写入PipedOutputStream。
不推荐单线程同时使用这两个对象,可能导致死锁。
管道输入流包含一个缓冲区,在限制范围内将读操作和写操作分离。
如果向连接的管道输出流提供(字节)数据的线程不再存活,则称管道断开。

3.5 SequenceInputStream

SequenceInputStream表示其他输入流的逻辑连接。
从输入流的有序集合开始,从第一个流开始读取,直到最后文件结尾,然后读取第二个流,以此类推,直到读取到包含的输入流文件结尾。

3.6 StringBufferInputStream

StringBufferInputStream允许应用程序创建一个输入流,从字符串中读取字节。
应用程序还可以使用ByteArrayInputStream从字节数组中读取字节。
StringBufferInputStream只能使用每个字符的低8位。
已弃用。
StringBufferInputStream无法正确将字符转换为字节。从JDK1.1开始,从字符串创建流的首选方法时StringReader类。

3.7 ByteArrayInputStream

ByteArrayInputStream包含一个内部缓冲区,该缓冲区包含可从流中读取的字节。
ByteArrayInputStream读取方法提供内部计数器用于跟踪下一个字节。
ByteArrayInputStream关闭是无效的,可以在流关闭后调用方法而不抛出异常IOException。

4 输出流:OutputStream

是Object->输出数据的媒介。
OutputStream是所有表示字节输出流类的父类。输出流接收输出字节并将他们发送到某个接收器。
需要定义OutputStream子类的应用程序必须提供至少一个写入字节的方法。
输出流的方法如下图所示。
在这里插入图片描述

在这里插入图片描述

OutputStream抽象类的子类有FileOutputStream、FilterOutputStream和ObjectOutputStream,
常用的子类列表如下图所示。

在这里插入图片描述

4.1 FileOutputStream

文件输出流是将数据写入文件或文件描述的输出流。文件是否可用或是否可以创建取决于底层平台。
一些平台中,一次只允许一个FileOutputStream(或其他文件写入对象)打开文件进行写入。
这种情况下,如果涉及的文件已经打开,此类的构造函数会失败。
FileOutputStream用于原始字节流写入,如图像数据,对于字符流写入,可使用FileWriter。

4.2 FilterOutputStream

FilterOutputStream是过滤输出流类的父类。
这些流位于已存在的输出流(底层输出流)之上,将其作为基础数据,但是可能会转换数据或提供附加功能。
FilterOutputStream类本身只是将所有请求传递到基础输出流OutputStream的重写方法上。
FilterOutputStream的子类可进一步覆盖方法,并可提供新的方法或字段。

4.2.1 BufferedOutputStream

BufferedOutputStream实现缓存输出流。
通过设置缓存输出流,应用程序可以向底层输出流写入字节,而不用依赖底层系统。

4.2.2 DataOutputStream

数据输出流可以方便地将原始Java数据类型写入输出流。
然后,应用程序可以使用数据输入流读取。

4.2.3 PrintStream

PrintStream为输出流添加了新的功能,可以方便地打印各种数值。
并且提供了其他两个功能,不像其他的输出流,PrintStream不会抛出异常IOException。
PrintStream的异常会在内部设置一个标识,可以通过checkError方法进行测试。
PrintStream可以自动刷新,意味着,当写入字节数组、调用println方法或者写入换行符后会自动调用flush方法。

4.3 ObjectOutputStream

ObjectOutputStream将Java对象的原始数据类型和图写入OutputStream,可以使用ObjectInputStream读取(重建)对象。
对象的持久化可以通过流文件实现,如果流是网络套接字,可以在另一个主机或另一个进程中重建对象。
只有支持java.io.Serializable接口的对象才能写入流。
每个可序列化的对象被编码,包括类名和类的签名、对象的属性值、数组值以及初始化对象时引用其他对象的闭包。
writeObject方法用于将对象写入流,任何对象(包括字符串和数组)都是用writeObject编写的。
可以将多个对象或原始数据类型写入流,从ObjectInputStream读取对象时必须按照写入对象时的类型和顺序。
可以使用DataOutput提供的方法将原始数据类型写入流,使用writeUTF方法将字符串写入流。
对象默认序列化机制将对象的类、类签名和所有非瞬时和非静态属性的值写入流。
对其他对象的引用(除transient和static属性)也会被写入流。
使用应用共享机制对单个对象的多个引用进行编码,以便将对象的图进行恢复。
如编写可以有ObjectInputStream读取的对象:

FileOutputStream fos = new FileOutputStream("t.tmp");
ObjectOutputStream oos = new ObjectOutputStream(fos);
 
oos.writeInt(12345);
oos.writeObject("Today");
oos.writeObject(new Date());
 
oos.close();

序列化和反序列化过程中需要特殊处理的类必须实现这些方法:

private void readObject(java.io.ObjectInputStream stream)
      throws IOException, ClassNotFoundException;
private void writeObject(java.io.ObjectOutputStream stream)
      throws IOException
private void readObjectNoData()
      throws ObjectStreamException;

writeObject方法为特定的类写入对象状态以便readObject方法可以恢复对象。
writeObject不需要关心对象的父类或子类的状态。
通过writeObject方法或支持原始类型数据DataOutput的相关方法将各个字段写入ObjectOutputStream保存对象状态。
序列化不会写任何没有实现java.io.Serializable接口对象的字段。
不可序列化对象的子类仍然是可以序列化的,不过这要求父类必须有无参构造器,以初始化参数。
这种情况下(父类不可序列化,子类可以序列化),子类负责保存和恢复不可序列化类的状态。
一般,父类的字段是可访问的(如public、package或protected),或者可以使用get或set恢复状态。
通过实现抛出异常(NotSerializableException)的writeObject和readObject方法可以防止(阻止)对象序列化。
ObjectOutputStream会捕获异常并终止序列化。
实现Externalizable接口允许对象完全控制对象序列化的内容和格式。
Externalizable的writeExternal和readExternal方法用于保存和恢复对象。
通过类ObjectOutput和ObjectInput可以实现保存和恢复状态。
对象负责处理版本变更。
枚举常量的序列化方式不同于普通的可序列化和可外部化对象。枚举常量的序列化仅由名称组成,不携带属性常量值。
序列化枚举常量,ObjectOutputStream写入常量名方法返回的字符串。
与其他序列化或可外部序列化对象一样,枚举常量可用作后续在序列化流中出现的反向引用目标。
无法自定义枚举常量的序列化过程,序列化期间,会忽略枚举类型定义的任何特定writeObject和writeReplace方法。
任何serialiPersistentFields或serialVersionUID字段不会被序列化,所有枚举类型都有固定的serialVersionUID,即0L。
基础数据(不包括可序列化字段和外部化数据)以块的形式写入ObjectOutputStream。
块数据由头部和数据组成,块数据头部有标记和数据组成,连续的原始数据写入被合并到一个块记录中。
用于块记录的块因子为1024字节,每个块将都会填充1024字节,或在块终止时写入。
开始调用ObjectOutputStream的writeObject、defaultWrite和wirteFields的方法会停止向已存在的块中写入数据。

4.4 PipedOutputStream

管道输出流通过连接到管道输入流建立通信管道。
管道输出流是管道的发送端,一般,一个线程将数据写入PipedOutputStream对象,
另一个线程从连接的PipedInputStream读取数据。
不建议使用单个线程同时使用者两个对象(PipedInputStream和PipedOutputStream),可能导致死锁。
如果从连接的管道中读取数据的线程不再存活,称管道断开。

4.5 ByteArrayOutputStream

ByteArrayOutputStream实现输出流,其中数据被写入数组。
缓冲区在写入数据时会自动增长,可以使用toByteArray()和toString()检索数据。
关闭ByteArrayOutputStream是无效的。可以在流关闭后调用类中的方法,而不抛出异常IOException。

5 字符读取器:Reader

Reader是读取字符流的抽象类。子类必须实现的方法是read(char[], int, int)和close()。
大多数子类将覆盖Reader的一些方法,以提供更高效或更多的功能。
Reader提供的方法如下图所示。
在这里插入图片描述

在这里插入图片描述
Reader抽象类的子类有BufferedReader、InputStreamReader和StringReader,
常用的子类列表如下图所示。

在这里插入图片描述

5.1 BufferedReader

从字符输入读取文本,缓冲字符,以便有效读取字符、数组和行数据。
可以指定缓冲区大小,也可以使用默认大小,对于大多数场景,默认大小(尺寸)足够使用。
通常,Reader发出的每个读取请求都会有对应的底层字符或字节流进行读取,因此,建议使用BufferedReader包装高消耗的read(),如FileReader和InputStreamReader。

BufferedReader in
    = new BufferedReader(new FileReader("foo.in"));

BufferedReader将会缓存指定文件的输入。如果没有缓存,每次调用read方法或readLine方法都会直接读取文件,再转换为字符,然后返回,是非常低效的。
使用DataInputStream进行文本输入可以通过适当的BufferedReader替换每个DataInputStream进行本地化处理。

5.2 InputStreamReader

InputStreamReader是字节流转换为字符流的桥梁:通过指定的字符集将字节转换为字符。
字符集可以通过名称指定,也可以显式指定或者使用平台可使用的默认字符集。
每次调用InputStreamReader的read方法可能从底层字节输入流中读取一个或多个字节,为了实现字节到字符的高效转换,
可以从底层流中提前读取更多的字节。
为获取最高的读取转换效率,可使用BufferedReader包装InputStreamReader,如:

BufferedReader in
    = new BufferedReader(new InputStreamReader(System.in));

5.2.1 FileReader

读取字符文件的类。
该类的构造函数假定默认字符编码和默认字节缓冲尺寸是合适的。
如要指定字符编码和缓存大小,可在FileInputStream中构造InputStreamReader。
FileReader用于读取字符流,对于原始字节流,可使用FileInputStream。

5.3 StringReader

读取源为字符串的字符流。

5.4 PipedReader

管道字符输入流。

5.5 FilterReader

读取过滤字符流的抽象类。
提供将所有请求传递到包含流的默认方法,子类会覆盖这些方法,可能提供其他的方法和字段。

5.5.1 PushbackReader

字符流读取器,允许字符推回到字符流中。

6 字符写入器:Writer

Writer是将字符流的抽象类。
子类必须实现的方法为write(char[], int, int)、fulsh()和close()。
大多数子类可以覆盖一些方法,以提高性能或提供更多功能。
Writer提供的方法如下图所示。
在这里插入图片描述

在这里插入图片描述

Writer抽象类的子类有BufferedWriter、CharArrayWriter和FilterWriter,
常用的子类列表如下图所示。
在这里插入图片描述

6.1 BufferedWriter

将文本写入字符输入流,缓冲字符可以高效写入单个字符、数组和字符串。
缓冲区尺寸可以指定也可以使用默认配置,大多数情况,默认值足够用。
BufferedWriter提供newLine方法,该方法使用平台自身的行分隔符,如果系统属性line.separator。
不是所有平台都使用\n作为换行符,因此,调用此方法终止输出优于直接写入换行符。
一般,Writer会立即将输出发送到基础字符或字节流,除非需要提示输出,否则建议将BufferedWriter包装在有性能损耗的write()中,
如FileWriter和OutputStreamWriter。

PrintWriter out
    = new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));

上例,将PrintWriter的输出缓存到文件,如果没有缓存,每次调用print方法都会将字符转换为字节,然后理解写入文件,这是非常低效的。

6.2 CharArrayWriter

CharArrayWriter实现了可以用作Writer的字符缓存。
当数据写入流时,缓存区会自动增长,可以使用toCharArray()和toString()检索数据。
close方法是无效的,当关闭流后调用其他方法,不会抛出异常IOException。

6.3 FilterWriter

FilterWriter是编写过滤字符流的抽象类。提供将请求传递到包含流的默认方法,FilterWriter子类会覆盖部分方法,甚至提供其他的方法和属性。

6.4 OutputStreamWriter

OutputStreamWriter是字符流转换为字节流的桥梁:写入OutputStreamWriter的字符使用指定的字符集编码为字节。
使用的字符集可以通过名称指定,也可以显式指定,或者使用平台的默认字符集。
每次调用write方法都会调用编码转化器为字符编码,生成的字节被写入底层输出流前会保存在缓存中。
虽然可以指定缓存尺寸,但是,默认情况的缓存尺寸可以满足大多数场景。
传递给write方法的字符没有缓存。
为获取最高执行效率,考虑在BufferedWriter中包装OutputStreamWriter,避免频繁调用编码转换器。

Writer out
    = new BufferedWriter(new OutputStreamWriter(System.out));

代理对是一个由两个字符值序列表示的字符:高代理项范围是\uD800~\uDBFF,低代理项范围是\uDC00~\uDFFF
格式不正确的代理元素:高代理项后没有低代理项或低代理项没有高代理项。
OutputStreamWriter默认会用字符集替代格式错误的代理元素和不可映射的字符序列,当需要对编码过程进行更多的控制时,应使用CharsetEncoder。

6.4.1 FileWriter

FileWriter可以非常方便地写入字符文件,FileWriter类的构造函数假定默认字符编码和默认字节缓存大小是可以接受的。
如要指定编码和缓存大小,可以在FileOutputStream上构造OutpuStreamWriter。
文件是否可用或是否可以创建取决于底层平台,特别是一些平台,一次只允许一个FileWriter(或其他文件写入对象)打开文件进行写入,
如果涉及的文件已经打开,此类的构造函数会失败。
FileWriter用于写入字符文件,对于原始字节流,使用FileOutputStream。

6.5 PrintWriter

PrintWriter将对象格式化打印到文本输出流。
PrintWriter实现了PrintStream中所有的打印方法。但是,不包含写入原始字节的方法,程序应该使用位编码的字节流。
与PrintStream类不同,如果启动了自动刷新,只有在调用println、printf或format方法时才会自动刷新,而不是在输出换行符时刷新。
这些方法使用平台自身的分隔符,而不是换行符。
PrintWriter中的方法不会抛出IO异常,虽然一些构造函可能抛出异常,客户端可以通过调用checkError查询是否发生错误。

6.6 PipedWriter

管道化字符输出流。

6.7 StringWriter

StringWriter是一种字符流,将输出收集到字符串缓冲区,然后用于构造字符串。
关闭StringWriter是无效的,StringWriter关闭后调用方法,不会抛出异常IOException。

7 File

File文件和目录路径的抽象表达。
用户接口和操作系统依赖路径名称命名文件和目录。File提供抽象、系统独立的层次路径名视图。
File提供的方法和属性如下图所示。
在这里插入图片描述

在这里插入图片描述

抽象路径名有两个组件:
(1)可选的系统相关前缀字符串,如磁盘驱动说明符,UNIX系统根目录为/,Windows UNC路径名为\\\\
(2)空字符串或者多个字符串名称的序列。
抽象出来的路径名第一个名称可以是目录名,如果是Windows系统,UNC路径名可以是主机名称。
抽象路径名中的每个后续名称表示一个目录,最后的名称表示目录或文件。空的抽象路径没有前缀和空的名称序列。
路径名称字符串与抽象路径名之间转换取决于系统。
抽象路径名转换为路径名字符串时,每个名称都由默认分隔符进行分隔。
默认分隔符由系统属性file.separator定义,File类提供了静态字段separator和separatorChar作为分隔符使用。
路径名转换为抽象路径时,名称可以使用默认分隔符或者系统支持的分隔符进行分隔。
无论是抽象或者字符串形式的路径名,可以是相对路径亦可是绝对路径。
绝对路径名是完整的,不需要其他信息就可以定位文件。
相对路径名名必须根据从路径名称获取的信息进行解析,一般情况,java.io包中的类总是根据当前用户目录解析相对路径名。
该目录由系统属性user.dir命名,目录一般由Java虚拟机调用。
抽象路径名的父级可以通过调用File的getParent方法获得,由路径名前缀和路径名中除最后一个名称组成。
每个目录的绝对路径名是任何File对象的父级,File对象有一个绝对抽象路径名,该绝对路径名以目录的绝对路径开头。
如抽象路径名/usr表示/usr/local/bin绝对路径的父级。
前缀概念用于处理UNIX平台的根目录,以及Windows平台的驱动器说明符、根目录和UNC路径名,如下:
(1)UNIX平台,绝对路径前缀始终为/,相对路径没有前缀,表示根目录的抽象路径有前缀/和空名称序列;
(2)Windows平台,前缀为驱动器号组合:,如D:,如果路径名是绝对路径,后面会拼接\\,如D:\\,UNC路径名前缀为\\\\,主机名和共享名是名称序列中的前两个名称,没有指定驱动器的相对路径名称没有前缀。
File类实例可以表示实际的文件系统对象,也可不表示文件系统对象,如文件或目录。
如果File类实例表示文件系统对象,则该对象驻留在分区中,分区是操作系统用于存储的文件系统,单个存储设备(如物理磁盘驱动、闪存,CD-ROM)可以包括多个分区。
File类实例(如果有)存在于该路径名的父级分区上。
文件系统对实际的文件系统操作可以进行限制,如读、写和执行。这些限制统称为访问权限。
文件系统可以对单个对象具有多组访问权限,如,一组可以用于对象的所有者,另一组可以用于所有其他用户。
对象的访问权限可能会导致File类中的某些方法失败。
File类实例是不可变的,一旦创建,File对象表示的抽象路径名永远不会改变。
与java.nio.file包的相关性:
java.nio.file包定义了Java虚拟机访问文件、文件属性和文件系统的接口和类。
java.nio.file这个API克服了java.io.File类的许多限制,toPath方法可以获取文件对象(依据抽象路径定位文件的文件对象)的路径。
生成的Path可以与java.nio.file.Files类一起使用,以提供对其他文件操作、文件属性和IO异常等访问,从而在文件操作失败时帮助诊断错误。

8 RandomAccessFile

RandomAccessFile实例支持随机写入和读取文件。
RandomAccessFile提供的方法如下图所示。
在这里插入图片描述

在这里插入图片描述

随机访问文件类似于在文件系统中存储大量字节。数组中的游标或索引称为文件指针,输入操作从指针开始的地方读取字节,然后通过推进文件指针读取字节。如果随机访问文件通过read/write模式创建,输出操作也是可用的,输出操作会在文件指针开始的地方写入字节,通过移动文件指针写入字节。
写入字节超过当前数组尾部时,会扩展数组。可以通过getFilePointer方法获取文件指针,通过seek方法配置文件指针。
RandomAccessFile类中所有读取操作,如果在读取所需字节前到达文件尾部,会抛出异常EOFException。
如果文件结尾以外的任何原因无法读取字节,会抛出EOFException以外的异常IOException。
特别地,如果流关闭,则会抛出IOException异常。

9 小结

(1)字节流:使用InputStream和OutputStream抽象类的子类;
(2)字符流:使用Reader和Writer抽象类的子类;
(3)File:文件和目录路径的抽象表达,用于操作文件和目录;
(4)RandomAccessFile:随机写入和读取文件。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天然玩家

坚持才能做到极致

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值