Java: IO

Java IO

java.io

文件类

  • File:文件类对象可表示一个文件,包括普通文件和目录文件,在文档中称为“抽象路径”
  • File类对象只能修改文件的属性,不能读写文件
  • File构造方法:提供文件路径或其它File类对象
  • File文件操作在大多数情况下不常用且不好用,更多是查询文件的属性,很多时候应该使用Filesapache提供的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
  • 按功能分类,分为节点流、处理流,节点流对象直接和数据源绑定,仅提供读取字节或字符的功能,处理流则是节点流的封装,具体体现在构造方法上,节点流需要资源描述符对象,而处理流需要流对象

字节流

  • 所有字节流类都继承InputStreamOutputStream
    • InputStream:仅提供读取单个或多个字节的方法,除此之外: long skip(long n):跳过最多n个字节,返回实际跳过的字节数 boolean markSupported():测试该输入流是否支持mark()reset() mark(int lim):对现在指针作标记,该标记在此后的lim个字节内才有效 reset():复位到上一个有效的标记,若不存在则抛出异常 通过mark()reset()配合可以实现重复读取某一段连续字节
    • OutputStream:仅提供写入单个或多个字节的方法,除此之外有flush()方法能快速清空缓冲区
  • 节点流最重要的是FileInputStreamFileOutputStream,可以提供文件路径或抽象文件对象构造文件字节流,FileOutputStream可以提供append布尔值参数表示是否以追加模式写入 文件流本身不支持标记,ByteArray系列的流支持但并不常用(因为只能处理小文件),更常用的是处理流Buffered系列的流
  • 处理流则非常多样:
    • BufferedInput/OutputStream:十分常用的缓冲处理流,本身不提供解析之类的方法,而是用于管理缓冲区的流,支持mark
    • DataInput/OutputStream:能将字节数据解析为一系列基本数据类型的处理流,和RandomAccessFile提供的输入/输出方法一致 readLine()方法已被弃用,建议使用BufferedReader提供的readLine()方法
    • ObjectInput/OutputStream:能将字节数据解析为Object对象的处理流,用于序列化和反序列化,要求类实现Serializable空接口;java原生的序列化有安全、性能问题
    • PipedInput/OutputStream:用于线程间通信,输入流或输出流通过connect()方法连接配套的输出流或输入流
    • SequenceInputStream:序列处理流,用于将一系列输入流串联起来,统一由SequenceInputStream对象管理 需要用到传统的Enumeration枚举器接口,调用Vector<InputStream>对象的elements()方法即可,但实际上现在已经很少使用了
    • PrintStream:提供的最常用的方法是print()println()的一系列重载方法,System.outPrintStream类的final对象

字符流

  • 所有字符流类都继承ReaderWriter
  • 处理对象数据或二进制数据时,字节流通常更好用,但当数据是字符时,用字符流类来处理更简单(例如编码问题),所以一般会用InputStreamReader将输入字节流转化为输入字符流,处理后通过OutputStreamWriter这个输出字符流将内容写入输出字节流

java.nio

  • java.io是阻塞型IO(BIO),是面向流的,而java.niojava 1.4+支持的New IO(或译为Non-Block IO),是面向通道、缓冲区、选择器的非阻塞IO 这两种IO都是同步的,需要程序主动检查IO任务是否完成,传统的IO优势在于小规模、低并发的操作(例如小文件操作),而在网络IOnio有较大优势
  • java.nio的读写是全双工的

Buffer

  • nio中,Buffer用于处理数据,其子类包含IntBufferByteBufferCharBufferLongBuffer,最常用的自然是ByteBuffer
  • Buffer及上述几个子类都是抽象类,不能被实例化但其子类可以通过以下静态方法获取Buffer对象:
    • allocate(int):获取一个自动分配静态数组的Buffer实例对象(HeapXxxBuffer) 使用JVM堆内内存
    • wrap(type[])将提供的静态数组封装为Buffer对象
    • allocateDirect(int):获取一个自动分配静态数组的Buffer实例对象(DirectXxxBuffer) 使用堆外的本地内存,本地内存在分配与回收时消耗更大,但面对大量、频繁的读写更快
  • Buffer有三个重要属性:mark(标记)、position(指向当前指针)、limit(现存数据大小),mark<=position<=limit
  • Buffer也提供随机读写的功能,允许对象在position属性小于limit属性时读写,并提供了以下和positionmark有关的方法:
    • position()position(int):获取或设置position属性
    • limit()limit(int):获取或设置limit属性
    • mark():标记当前的positionmark
    • reset():设置当前的position为当前的mark
    • flip():设置limit为当前的position,再将position设置为0,相当于buf.limit(buf.position()).position(0)
    • rewind():设置mark为当前的position
    • remaining()hasRemaining():返回limitposition的差值、判断该差值是否大于零 它们均返回对象本身,因此可以级联操作,其中flip()很常用,因为通常需要向Buffer对象写入数据后紧接着读取这些数据
  • ByteBuffer提供的读写方法在命名上和流有所区别:read变为getwrite变为put,它们会自动更新positionlimit 除了相对存取,还允许提供index参数作为第一个参数,称为绝对存取,绝对存取不影响positionlimit
  • Buffer提供duplicate()方法创建一个共享同一个数据数组的缓冲区对象 XxxBuffer提供asXxxBuffer()asReadOnlyBuffer()创建一个其它类型的、或只读的本类型的Buffer对象,仍然共享同一个数据数组

FileChannel

  • Channel是一个接口,实现该接口的实例对象表示数据源到缓冲区的通道,它不能处理数据,而是负责存取和传输数据
  • 有以下类实现了该接口:
    • FileChannel:文件通道,可以通过RandomAccessFile对象或流对象的getChannel()方法获取,也可以通过静态方法open()获取,其中open()方法实际上是nio.2提供的,将在下一节介绍
    • SocketChannel:客户端的TCP套接字通道,可以通过Socket对象的getChannel()方法获取
    • ServerSocketChannel:服务端TCP套接字连接的通道,可以通过ServerSocket对象的getChannel()方法获取
    • DatagramChannelUDP套接字的通道 在这里仅介绍FileChannel提供的方法,其余通道将在网络编程中详细介绍
  • read(Buffer dest)write(Buffer src)是通道类最重要的两个方法
  • force(false)方法允许强制刷盘,立刻同步通道的更改到硬盘中,force(true)不仅会同步内容,还会同步文件元数据
  • truncate(long size):截断通道的内容,丢弃第size个字节及之后的数据

MappedByteBuffer

  • MappedByteBuffer:文件映射缓冲,是文件在内存中的直接映射,避免了传统IO那样需要切换内核态进行存取操作,效率更高,但和硬盘上的数据不是随时同步的,可以通过force()立刻刷新缓冲区 通常通过通道对象调用map()方法创建,同样是本地内存,因此读写更快而分配与回收较慢,适用于大型文件
  • 映射有三种模式:FileChannel.MapModeREAD_ONLYREAD_WRITEPRIVATE 其中READ_WRITE模式的更改会同步到文件但不会同步到其它缓冲区,PRIVATE模式的更改只会修改当前的缓冲区,必须通过force()刷盘同步更新
  • MappedByteBufferDirectByteBuffer的区别是它和通道有直接关联,所以可以不通过FileChannel进行read()write(),而是依靠缺页中断直接在内存映射文件中读写数据

java.nio.charset

nio2

AsynchronousChannel

  • nio2或称AIO,支持异步操作和更全面的跨文件系统支持,主体为java.nio.file
  • 除此之外提供异步的通道,名称均以Asynchronous开头,它们和nio的通道的方法一致,不再赘述

PathPaths

  • Paths是工具类,仅提供静态方法用于创建Path对象,但在新版本中由于接口允许含有静态方法,Paths.get()内部被替换为Path.of() 所以直接调用Path.of()创建Path对象即可,需要提供URI(网络资源)或String(本地资源)
  • Path中,.表示classpath
  • PathFile起的作用类似,均表示一个抽象路径,提供以下实例方法:
    • getFileName():获取String表示的文件名
    • getFileSystem():获取FileSystem对象
    • getNameCount():获取该Path上级目录的个数
    • getName(int idx):获取第idx层上级目录的Path对象,idx0开始表示最上级目录
    • iterator():获取用于遍历上级目录的迭代器
    • getParent():获取绝对路径中上级目录的Path对象,若为相对路径则返回null
    • getRoot():获取绝对路径中根目录的Path对象,若为相对路径则返回null
    • isAbsolute():判断是否为绝对路径
    • 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...):移动文件,默认行为为非原子性地移动文件内容到未存在文件,若为软链接则复制软链接本身

java.nio.file.attribute

PathMatcher