Java: IO
Java IO
java.io
文件类
File:文件类对象可表示一个文件,包括普通文件和目录文件,在文档中称为“抽象路径”File类对象只能修改文件的属性,不能读写文件File构造方法:提供文件路径或其它File类对象File文件操作在大多数情况下不常用且不好用,更多是查询文件的属性,很多时候应该使用Files或apache提供的io方法,因此这里不介绍RandomAccessFile:随机访问文件类和文件类完全不同,不能修改文件的元属性,但是能随机访问或修改文件的内容RandomAccessFile构造方法:提供文件路径或其它File类对象,并提供用字符串表示的模式参数"r":只读打开,文件不存在或发生写操作会抛出IOException受检异常"rw":读写打开,文件不存在会创建,此处的“写”是覆盖"rwd":读写打开,并在缓存满时或调用close()时同步"rws":读写打开,并在每一次写操作后同步
RandomAccessFile常用方法:long getFilePointer():返回当前的文件指针seek(long pos):将文件指针移到pos处close():关闭并释放这个随机访问流Type readType()和writeType(Type x):支持读写并自动解析一个基本数据类型的数据String readLine():读一行文本readFully(byte[] b, int off, int len):读取当前指针后最多len个字节,读入b+off中 此函数有readFully(byte[] b)重载,将当前指针后的所有字节读入b中writeBytes(String)和writeChars(String):将字符串以byte序列形式、Unicode编码形式写入文件String readUTF()和writeUTF(String s):以UTF8编码读写字符串getFD():获取FileDescriptor对象
FileDescriptor:不含任何操作功能,而是文件资源在java中抽象的描述符boolean valid():检查描述符是否有效sync():将缓冲区内容同步到硬盘,上述RandomAccessFile的同步就是调用getFD().sync()完成的
流的概念
- 流是
java提供的另一种操作数据输入输出的机制,和RandomAccessFile不同,它们是顺序读写的,曾经读过的数据无法再读(除非支持标记),在读取数据前必须将所有前面的数据全部读取 - 按数据流向分类,分为输入流、输出流
- 按数据单位分类,分为字符流、字节流,字符流以两个字节为单位、字节流以单个字节为单位
java对这两个分类依据有较易区分的命名风格,输入字符流为XxxReader、输出字符流为XxxWriter、输入字节流为XxxInputStream、输出字节流为XxxOutputStream - 按功能分类,分为节点流、处理流,节点流对象直接和数据源绑定,仅提供读取字节或字符的功能,处理流则是节点流的封装,具体体现在构造方法上,节点流需要资源描述符对象,而处理流需要流对象
字节流
- 所有字节流类都继承
InputStream或OutputStreamInputStream:仅提供读取单个或多个字节的方法,除此之外:long skip(long n):跳过最多n个字节,返回实际跳过的字节数boolean markSupported():测试该输入流是否支持mark()和reset()mark(int lim):对现在指针作标记,该标记在此后的lim个字节内才有效reset():复位到上一个有效的标记,若不存在则抛出异常 通过mark()和reset()配合可以实现重复读取某一段连续字节OutputStream:仅提供写入单个或多个字节的方法,除此之外有flush()方法能快速清空缓冲区
- 节点流最重要的是
FileInputStream和FileOutputStream,可以提供文件路径或抽象文件对象构造文件字节流,FileOutputStream可以提供append布尔值参数表示是否以追加模式写入 文件流本身不支持标记,ByteArray系列的流支持但并不常用(因为只能处理小文件),更常用的是处理流Buffered系列的流 - 处理流则非常多样:
BufferedInput/OutputStream:十分常用的缓冲处理流,本身不提供解析之类的方法,而是用于管理缓冲区的流,支持markDataInput/OutputStream:能将字节数据解析为一系列基本数据类型的处理流,和RandomAccessFile提供的输入/输出方法一致readLine()方法已被弃用,建议使用BufferedReader提供的readLine()方法ObjectInput/OutputStream:能将字节数据解析为Object对象的处理流,用于序列化和反序列化,要求类实现Serializable空接口;java原生的序列化有安全、性能问题PipedInput/OutputStream:用于线程间通信,输入流或输出流通过connect()方法连接配套的输出流或输入流SequenceInputStream:序列处理流,用于将一系列输入流串联起来,统一由SequenceInputStream对象管理 需要用到传统的Enumeration枚举器接口,调用Vector<InputStream>对象的elements()方法即可,但实际上现在已经很少使用了PrintStream:提供的最常用的方法是print()、println()的一系列重载方法,System.out是PrintStream类的final对象
字符流
- 所有字符流类都继承
Reader或Writer - 处理对象数据或二进制数据时,字节流通常更好用,但当数据是字符时,用字符流类来处理更简单(例如编码问题),所以一般会用
InputStreamReader将输入字节流转化为输入字符流,处理后通过OutputStreamWriter这个输出字符流将内容写入输出字节流
java.nio
java.io是阻塞型IO(BIO),是面向流的,而java.nio是java 1.4+支持的New IO(或译为Non-Block IO),是面向通道、缓冲区、选择器的非阻塞IO这两种IO都是同步的,需要程序主动检查IO任务是否完成,传统的IO优势在于小规模、低并发的操作(例如小文件操作),而在网络IO中nio有较大优势java.nio的读写是全双工的
Buffer
- 在
nio中,Buffer用于处理数据,其子类包含IntBuffer、ByteBuffer、CharBuffer、LongBuffer,最常用的自然是ByteBuffer Buffer及上述几个子类都是抽象类,不能被实例化但其子类可以通过以下静态方法获取Buffer对象:allocate(int):获取一个自动分配静态数组的Buffer实例对象(HeapXxxBuffer) 使用JVM堆内内存wrap(type[])将提供的静态数组封装为Buffer对象allocateDirect(int):获取一个自动分配静态数组的Buffer实例对象(DirectXxxBuffer) 使用堆外的本地内存,本地内存在分配与回收时消耗更大,但面对大量、频繁的读写更快
Buffer有三个重要属性:mark(标记)、position(指向当前指针)、limit(现存数据大小),mark<=position<=limitBuffer也提供随机读写的功能,允许对象在position属性小于limit属性时读写,并提供了以下和position与mark有关的方法:position()和position(int):获取或设置position属性limit()和limit(int):获取或设置limit属性mark():标记当前的position为markreset():设置当前的position为当前的markflip():设置limit为当前的position,再将position设置为0,相当于buf.limit(buf.position()).position(0)rewind():设置mark为当前的positionremaining()和hasRemaining():返回limit与position的差值、判断该差值是否大于零 它们均返回对象本身,因此可以级联操作,其中flip()很常用,因为通常需要向Buffer对象写入数据后紧接着读取这些数据
ByteBuffer提供的读写方法在命名上和流有所区别:read变为get、write变为put,它们会自动更新position和limit除了相对存取,还允许提供index参数作为第一个参数,称为绝对存取,绝对存取不影响position和limitBuffer提供duplicate()方法创建一个共享同一个数据数组的缓冲区对象XxxBuffer提供asXxxBuffer()和asReadOnlyBuffer()创建一个其它类型的、或只读的本类型的Buffer对象,仍然共享同一个数据数组
FileChannel
Channel是一个接口,实现该接口的实例对象表示数据源到缓冲区的通道,它不能处理数据,而是负责存取和传输数据- 有以下类实现了该接口:
FileChannel:文件通道,可以通过RandomAccessFile对象或流对象的getChannel()方法获取,也可以通过静态方法open()获取,其中open()方法实际上是nio.2提供的,将在下一节介绍SocketChannel:客户端的TCP套接字通道,可以通过Socket对象的getChannel()方法获取ServerSocketChannel:服务端TCP套接字连接的通道,可以通过ServerSocket对象的getChannel()方法获取DatagramChannel:UDP套接字的通道 在这里仅介绍FileChannel提供的方法,其余通道将在网络编程中详细介绍
read(Buffer dest)和write(Buffer src)是通道类最重要的两个方法force(false)方法允许强制刷盘,立刻同步通道的更改到硬盘中,force(true)不仅会同步内容,还会同步文件元数据truncate(long size):截断通道的内容,丢弃第size个字节及之后的数据
MappedByteBuffer
MappedByteBuffer:文件映射缓冲,是文件在内存中的直接映射,避免了传统IO那样需要切换内核态进行存取操作,效率更高,但和硬盘上的数据不是随时同步的,可以通过force()立刻刷新缓冲区 通常通过通道对象调用map()方法创建,同样是本地内存,因此读写更快而分配与回收较慢,适用于大型文件- 映射有三种模式:
FileChannel.MapMode的READ_ONLY与READ_WRITE与PRIVATE其中READ_WRITE模式的更改会同步到文件但不会同步到其它缓冲区,PRIVATE模式的更改只会修改当前的缓冲区,必须通过force()刷盘同步更新 MappedByteBuffer和DirectByteBuffer的区别是它和通道有直接关联,所以可以不通过FileChannel进行read()和write(),而是依靠缺页中断直接在内存映射文件中读写数据
java.nio.charset
nio2
AsynchronousChannel
nio2或称AIO,支持异步操作和更全面的跨文件系统支持,主体为java.nio.file包- 除此之外提供异步的通道,名称均以
Asynchronous开头,它们和nio的通道的方法一致,不再赘述
Path和Paths
Paths是工具类,仅提供静态方法用于创建Path对象,但在新版本中由于接口允许含有静态方法,Paths.get()内部被替换为Path.of()所以直接调用Path.of()创建Path对象即可,需要提供URI(网络资源)或String(本地资源)- 在
Path中,.表示classpath Path和File起的作用类似,均表示一个抽象路径,提供以下实例方法:getFileName():获取String表示的文件名getFileSystem():获取FileSystem对象getNameCount():获取该Path上级目录的个数getName(int idx):获取第idx层上级目录的Path对象,idx从0开始表示最上级目录iterator():获取用于遍历上级目录的迭代器getParent():获取绝对路径中上级目录的Path对象,若为相对路径则返回nullgetRoot():获取绝对路径中根目录的Path对象,若为相对路径则返回nullisAbsolute():判断是否为绝对路径normalize():规范化路径,将所有.和..解析并去除relative(Path p):返回该对象和p之间的相对路径resolve(Path p):若p为绝对路径则返回p,否则拼接在本Path对象之后并返回resolveSibling(Path p):若p为绝对路径则返回p,否则拼接在本Path对象的getParent()对象之后并返回startsWith()和endsWith():判断该对象是否以某个Path或某个String起始、结尾toAbsolutePath():返回对应的绝对路径toUri():返回对应的URI
Files
Files是工具类,提供更丰富的文件操作copy(Path, Path, CopyOption...):复制文件,默认行为为非原子地复制文件内容和最后修改时间到未存在文件,不保留硬链接、若源文件为软链接则取其指向文件的数据 其中CopyOption是空接口,有枚举StandardCopyOption实现ATOMIC_MOVE:原子地移动,仅支持move()REPLACE_EXISTING:若目标已存在则覆盖内容COPY_ATTRIBUTES:复制文件元数据,包括权限和所有者等
move(Path, Path, CopyOption...):移动文件,默认行为为非原子性地移动文件内容到未存在文件,若为软链接则复制软链接本身