IO流顺序读写数据,单向流动,称IO流。IO流以byte
(字节)为最小单位,因此也称为字节流。
快速引入
InputStream / OutputStream
在Java中,InputStream
代表输入字节流,OuputStream
代表输出字节流,这是最基本的两种IO流。
IO流以byte
(字节)为最小单位,因此也称为字节流。
Reader / Writer
Java提供了Reader
和Writer
表示字符流,字符流传输的最小数据单位是char
。
Reader
和Writer
本质上是一个能自动编解码的InputStream
和OutputStream
。
究竟使用Reader
还是InputStream
,要取决于具体的使用场景。如果数据源不是文本,就只能使用InputStream
,如果数据源是文本,使用Reader更方便一些。Writer
和OutputStream
是类似的。
同步和异步
同步IO是,读写IO时代码必须等待数据返回后才继续执行后续代码,优点是代码编写简单,缺点是CPU执行效率低。
异步IO是,读写IO时仅发出请求,然后立刻执行后续代码,优点是CPU执行效率高,缺点是代码编写复杂。
Java标准库的包java.io
提供了同步IO,而java.nio
则是异步IO。
InputStream
、OutputStream
、Reader
和Writer
都是同步IO的抽象类,对应的具体实现类,以文件为例,有FileInputStream
、FileOutputStream
、FileReader
和FileWriter
。
File
Java的标准库java.io
提供了File
对象来操作文件和目录。
1 | import java.io.*; |
构造File对象时,既可以传入绝对路径,也可以传入相对路径。
Windows平台使用\
作为路径分隔符,在Java字符串中需要用\\
表示一个\
,前一个\表示转义。
Linux平台使用/
作为路径分隔符
1 | File f = new File("/usr/bin/javac"); |
File对象有3种形式表示的路径,一种是getPath()
,返回构造方法传入的路径,一种是getAbsolutePath()
,返回绝对路径,一种是getCanonicalPath
,它和绝对路径类似,但是返回的是规范路径。
规范路径就是把.
和..
转换成标准路径
因为Windows和Linux的路径分隔符不同,File对象有一个静态变量用于表示当前平台的系统分隔符:
1 | System.out.println(File.separator); // 根据当前平台打印"\"或"/" |
文件和目录
调用isFile()
,判断该File
对象是否是一个已存在的文件,调用isDirectory()
,判断该File
对象是否是一个已存在的目录。
用File
对象获取到一个文件时,还可以进一步判断文件的权限和大小:
boolean canRead()
:是否可读;boolean canWrite()
:是否可写;boolean canExecute()
:是否可执行;long length()
:文件字节大小。
对目录而言,是否可执行表示能否列出它包含的文件和子目录。
创建和删除文件
createNewFile()创建新文件,delete()删除文件
1 | File file = new File("/path/file"); |
File对象提供了createTempFile()
来创建一个临时文件,以及deleteOnExit()
在JVM退出时自动删除该文件。
1 | import java.io.*; |
遍历文件和目录
File对象表示一个目录时,可以使用list()
和listFiles()
列出目录下的文件和子目录名。
1 | import java.io.*; |
File对象如果表示一个目录,可以通过以下方法创建和删除目录:
boolean mkdir()
:创建当前File对象表示的目录;boolean mkdirs()
:创建当前File对象表示的目录,并在必要时将不存在的父目录也创建出来;boolean delete()
:删除当前File对象表示的目录,当前目录必须为空才能删除成功。
Path
位于java.nio.file
包
1 | import java.io.*; |
使用path适用于目录复杂的拼接、遍历等。
inputStream
java.io
包提供了所有同步IO的功能。InputStream
就是Java标准库提供的最基本的输入流。
FileInputStream
就是从文件流中读取数据。
1 | public void readFile() throws IOException { |
InputStream
和OutputStream
都是通过close()
方法来关闭流。
所有与IO操作相关的代码都必须正确处理IOException
。
需要用try ... finally
来保证InputStream
在无论是否发生IO错误的时候都能够正确地关闭:
1 | public void readFile() throws IOException { |
利用Java 7引入的新的try(resource)
的语法,只需要编写try
语句,让编译器自动为我们关闭资源。推荐的写法如下:
1 | public void readFile() throws IOException { |
try(resource = ...)
中的对象是否实现了java.lang.AutoCloseable
接口,如果实现了,就自动加上finally
语句并调用close()
方法。InputStream
和OutputStream
都实现了这个接口。
InputStream实现类
1 | import java.io.*; |
OutputStream
OutputStream
还提供了一个flush()
方法,它的目的是将缓冲区的内容真正输出到目的地,能强制把缓冲区内容输出。
OutputStream实现类
1 | import java.io.*; |
操作zip文件
ZipInputStream
是一种FilterInputStream
,它可以直接读取zip包的内容,JarInputStream
是从ZipInputStream
派生,它增加的主要功能是直接读取jar文件里面的MANIFEST.MF
文件。
读取zip包
我们要创建一个ZipInputStream
,通常是传入一个FileInputStream
作为数据源,然后,循环调用getNextEntry()
,直到返回null
,表示zip流结束。
一个ZipEntry
表示一个压缩文件或目录,如果是压缩文件,我们就用read()
方法不断读取,直到返回-1
:
1 | try (ZipInputStream zip = new ZipInputStream(new FileInputStream(...))) { |
写入zip包
ZipOutputStream
是一种FilterOutputStream
,它可以直接写入内容到zip包。我们要先创建一个ZipOutputStream
,通常是包装一个FileOutputStream
,然后,每写入一个文件前,先调用putNextEntry()
,然后用write()
写入byte[]
数据,写入完毕后调用closeEntry()
结束这个文件的打包。
1 | try (ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(...))) { |
读取classpath资源
1 | try (InputStream input = getClass().getResourceAsStream("/default.properties")) { |
调用getResourceAsStream()
就可以直接从classpath读取任意的资源文件。
序列化
1 | 序列化是指把一个Java对象变成二进制内容,本质上就是一个byte[]数组。 |
一个Java对象要能序列化,必须实现一个特殊的java.io.Serializable
接口,它的定义如下:
1 | public interface Serializable { |
Serializable
接口没有定义任何方法,它是一个空接口。
序列化
1 | import java.io.*; |
反序列化
1 | try (ObjectInputStream input = new ObjectInputStream(...)) { |
Java的序列化允许class定义一个特殊的serialVersionUID
静态变量,用于标识Java类的序列化“版本”,通常可以由IDE自动生成。如果增加或修改了字段,可以改变serialVersionUID
的值,这样就能自动阻止不匹配的class版本:
1 | public class Person implements Serializable { |
要特别注意反序列化的几个重要特点:
反序列化时,由JVM直接构造出Java对象,不调用构造方法,构造方法内部的代码,在反序列化时根本不可能执行。
通用的序列化方法,例如JSON。
发布时间: 2019-09-07
最后更新: 2019-09-15
本文链接: https://juoyo.github.io/posts/9d0114be.html
版权声明: 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!