/ 编程语言Java

Java IO 笔记

文件 IO:用于处理数据。将数据保存到文件等其他物理设备上,保证数据可以在断电后持久保存。

File 类

File 代表了文件或者目录,用于操作文件或目录本身,不能读取文件内容。

IO流

流的分类

按照 方向 分:

  • 输入流:读取数据;
  • 输出流:写出数据。

按照 数据单元 分:

  • 字节流:每次读写一个字节,可以读写任意文件(以 Stream 结尾);
  • 字符流:每次读写一个字符,可以方便地读写文本文件(以 Reader/Writer 结尾)。

按照 角色 分:

  • 节点流:直接关联代表数据源的 IO 节点
  • 处理流(包装流、过滤流):建立在其他流的基础之上,用于对其他流进行包装。

输入流的基本方法:

  • read():读取一个单位;
  • int read(Object[] buff):读取N个单位到数组中,返回实际读取的单位数。

输出流的基本方法:

  • write(int):输出一个单位;
  • void write(Obejct[] buff):输出数组中所有数据。

节点流 主要如下:

InputStreamOutputStreamReaderWriter
文件FileInputStreamFileOutputStreamFileReaderFileWriter
数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
字符串StringReaderStringWriter

处理流 主要如下:

FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
打印流PrintStreamPrintWriter
转换流InputStreamReaderOutputStreamWriter
特殊流DataInputStreamDataOutputStream
对象流ObjectInputStreamObjectOutputStream

缓冲流

内存速度 > 外设速度

如果让内存输出一个数据单元,然后外设处理一个数据单元,必须等外设处理好数据单元之后,内存才会在此输出下一个单元。此时必然会造成性能浪费。

使用缓冲流,内存首先将所有数据输出到缓冲区,缓冲区中的数据留给外设慢慢处理。

使用缓冲流时,需要使用 flush() 方法将数据冲到外设中。

如果使用 close() 关闭流,程序保证会先执行 flush()。

打印流

System.out、System.err 均为 PrintStream 对象。

总结:

一般而言,输入使用 BufferedReader,输出使用 PrintStream。

转换流

将字节流转换成字节流。很多情况下,程序拿到的是字节流,但确认数据只可能为字符,此时将字节流转换为字符流。

特殊流

DataInputStream 提供一系列 readXxx() 方法用于读取不同类型的数据。

DataOutputStream 提供一系列 writeXxx() 方法用于写出不同类型的数据。

DataInputStream 和 DataOutputStream 是使用 Java 内部格式来记录数据的,因此 DataOutputStream 输出的数据应该使用 DataInputStream 来读取。

重定向标准输入输出

标准输出默认为屏幕。System.setOut(PrintStream out) 方法可以将输出重定向到另一个流。

标准输入默认为键盘。System.setIn(InputStream in) 方法可以将输入重定向到另一个流。

读取其他进程的数据

Runtime 类中的 exec() 方法可用于运行平台上的程序,该方法返回运行的 Process 对象。

Process 提供如下当法:

  • InputStream getErrorStream():返回进程错误的输入流。
  • InputStream getInputStream():返回进程标准输入流。
  • OutputStream getOutputStream():返回进程的标准输出流。

RandomAccessFile

RandomAccessFile 指任意访问,只能读写文件。

输出流不能追加内容,但 RandomAccess 可以。

RandomAccessFile 通常可用于实现多线程、断点下载工具。

对象序列化

序列化:将内存中的 Java 对象转换成字节文件(二进制文件),该二进制文件内容既可以保存到磁盘,也可以通过网络传输。 反序列化:将二进制数据恢复为原始的 Java 对象。

广义上来说,Java 序列化还包括 XML 序列化、JSON 序列化。

Serializable:标志接口,实现该接口的对象即可被序列化。

transient 关键字

有些时候,程序希望将某些 field 排除在序列化机制之外,可使用 transient 关键字修饰。

Java 不会序列化:

  • 方法
  • transient 修饰的实例变量
  • static 变量

自定义序列化

类可以通过实现如下方法实现自定义序列化:

 private void writeObject(java.io.ObjectOutputStream out)
	 throws IOException
 private void readObject(java.io.ObjectInputStream in)
     throws IOException, ClassNotFoundException;

序列化版本

当程序要反序列化时,需要两个东西才能反序列化成功:

  • 序列化的数据
  • 类文件

Java 序列化机制识别一个类是否发生了改变,依赖的是 Java 类的序列化版本。

一个可序列化的 Java 类,总有一个序列化版本号:

  1. 如果没有显式指定,系统会为他分配一个序列化版本号。只要 Java 源程序发生修改,编译后会重新生成一个新的版本号。
  2. 显式指定一个序列化版本号。

序列化版本号可以使用 serialver 命令查看。

推荐开发者为自己的类指定序列化版本号,只有当增加、删除实例变量时才需要增加版本号。

NIO

NIO 的出现,进一步简化了 IO 操作

Path

Path 代表一条路径(既可是文件的路径,也可是目录的路径)

可以通过 Path.of() 方法获取 Path 对象。

Files 工具类

Files 类为文件 IO 的工具类。文件复制、移动、删除、重命名等操作都可以实现。

可以使用 Files.walk() / Files.walkFileTree() 方法遍历目录。