Java笔记之文件
本笔记来自 计算机程序的思维逻辑 系列文章
字节流
InputStream
-
int read()
从流中读取下一个字节,返回类型为
int
,但取值在 0 到 255 之间当读到流结尾的时候,返回值为 -1 ,如果流中没有数据,该方法会阻塞直到数据到来、流关闭或异常出现
异常出现时,该方法抛出异常,类型为
IOException
,是受检异常,调用者必须进行处理 -
int read(byte b[])
一次读取多个字节,读入的字节放入参数数组中,第一个字节存入
b[0]
,第二个字节存入b[1]
,依此类推一次最多读入的字节个数为数组的长度,但实际读入的个数可能小于数组长度,返回值为实际读入的字节个数
当读到流结尾时,返回值同样为 -1 ,如果流中没有数据,同样会阻塞,异常时抛出
IOException
-
long skip(long n)
跳过多少字节,返回实际跳过的字节数 -
int available()
返回下一次不需要阻塞就能读取到的字节数,默认为 0一般用于网络读取,有足够数据量才开始读取
-
boolean markSupported()
判断是否支持mark
和reset
方法 -
void mark(int readlimit)
设置标记,并设置最多能够读取的字节数 -
void reset()
重置,回到标记的起始位置
markSupported
mark
reset
这3个方法用于支持从读过的流中重复读取;先用mark
方法将当前位置标记下来,读取了一些字节后,希望重新从标记处读取时,调用reset
方法
OutputStream
-
void write(int b)
向流中写入一个字节 -
void write(byte b[])
void write(byte b[], int off, int len)
向流中写入一个字节数组 -
void flush()
将缓冲而未实际写的数据进行实际写入
FileInputStream
用于读取文件字节流
FileOutputStream
-
FileOutputStream(String name, boolean append)
FileOutputStream(File file, boolean append)
append
指定追加还是覆盖
ByteArrayInputStream
- 将字节数组包装成输入流
- 支持
mark
和reset
重复读取
ByteArrayOutputStream
-
void writeTo(OutputStream out)
将内容写到另一个输出流中 -
byte[] toByteArray()
以字节数组输出 -
String toString()
String toString(String charsetName)
以字符串输出 -
int size()
返回当前写入的字节个数 -
void reset()
重置当前写入字节个数为 0 ,字节数组可重用
BufferedInputStream
- 常用于包装
FileInputStream
- 缓冲区大小为 8192
- 支持
mark
和reset
重复读取
BufferedOutputStream
- 常用于包装
FileOutputStream
- 缓冲区大小为 8192
字符流
Reader Writer
字符流基类,抽象类
InputStreamReader OutputStreamWriter
将字节流转换为字符流,适配器类
FileReader FileWriter
输入输出为文件的字符流
CharArrayReader CharArrayWriter
输入输出为字符数组的字符流
StringReader StringWriter
输入输出为字符串的字符流
BufferedReader BufferedWriter
- 提供缓冲区,默认大小为 8192 ,默认行长度为 80
- 提供按行读取功能
- 装饰类
PrintWriter
将基本类型和对象转换为其字符串形式输出
Scanner
可以读取基本类型的字符串形式
小结
- 写文件时,可以优先考虑
PrintWriter
,因为它使用方便,支持自动缓冲、支持指定编码类型、支持类型转换等 - 读文件时,如果需要指定编码类型,需要使用
InputStreamReader
,不需要,可使用FileReader
,但都应该考虑在外面包上缓冲类BufferedReader
文件和目录
File类的操作
文件元数据、文件操作和目录操作
File
- 可以表示文件,也可以表示目录
- 通过
new
创建File
对象,不会实际创建一个对象,只是创建一个表示文件或目录的对象,new
之后,File
对象中的路径是不可变的
文件元数据
-
String getName()
返回文件或目录名称 -
String getParent()
返回父目录路径 -
File getParentFile()
返回父目录的File
对象 -
String getPath()
返回构造File
对象时的完整路径,包括路径和文件名 -
boolean isAbsolute()
判断File
中路径是否绝对路径 -
String getAbsolutePath()
返回完整的绝对路径 -
File getAbsoluteFile()
返回完整绝对路径的File
对象 -
String getCanonicalPath()
返回标准的完整路径,不含.
..
等 -
boolean exists()
判断文件或目录是否存在 -
boolean isDirectory()
判断是否为目录 -
boolean isFile()
判断是否为文件 -
long length()
返回文件大小,字节数;目录没有该属性 -
long lastModified()
返回文件最后修改时间,从纪元时开始的毫秒数 -
boolean isHidden()
判断是否为隐藏文件 -
boolean canRead()
判断文件是否可读 -
boolean canWrite()
判断文件是否可写 -
separator
separatorChar
文件路径分隔符,如:/
和\
-
pathSeparator
pathSeparatorChar
多个文件路径的分隔符,如::
和;
当构建
File
对象时是相对路径,则可能获取不到parent
和parentFile
,需使用getAbsoluteFile
或getCanonicalFile
获取绝对路径的File
对象,再获取父对象
文件操作
-
boolean createNewFile()
创建实际文件,文件内容为空;如果文件已存在则不会创建 -
boolean delete()
删除文件或目录;如果File
是目录且不为空,则删除不成功,要删除目录,必须先删除所有子目录和文件 -
void deleteOnExit()
将File
加入待删列表,在JVM
正常退出时进行实际删除 -
boolean renameTo(File dest)
重命名文件 -
boolean canExecute()
判断文件是否可执行 -
boolean setReadOnly()
设置文件为只读文件
目录操作
-
boolean mkdir()
创建目录 -
boolean mkdirs()
创建目录及必需的中间目录 -
String[] list()
返回一个目录下的直接子目录和文件的文件名数组 -
File[] listFiles()
返回一个目录下的直接子目录和文件的File
对象数组 -
String[] list(FilenameFilter filter)
筛选
随机读写文件
构造方法
RandomAccessFile(String name, String mode)
RandomAccessFile(File file, String mode)
mode 打开模式
-
r
只读 -
rw
读写 -
rwd
读写,要求文件内容的任何更新都同步到设备上 -
rws
读写,要求文件内容和元数据的任何更新都同步到设备上
其它方法
-
void readFully(byte b[])
可以确保读够期望的长度,如果到了文件结尾也没读够,会抛出EOFException
-
long getFilePointer()
获取当前文件指针,该指针指向当前读写的位置,读写操作会自动更新该指针 -
void seek(long pos)
更新当前文件指针 -
int skipBytes(int n)
跳过 n 个字节,通过更改文件指针实现 -
long length()
返回文件大小,字节数 -
void setLength(long newLength)
当前文件长度较小则扩展;大则多出的部分会被截取,长度变成 newLength
实际应用
键值数据库
- 提供类似于
Map
的接口,可以按键保存、查找、删除,但数据可以持久化保存到文件 - 只把元数据(如索引信息)保存在内存,值的数据保存在文件,内存消耗大大降低
内存映射文件
概念
- 将文件映射到内存,文件对应于内存中的一个字节数组,对文件的操作变为对这个字节数组的操作,而字节数组的操作直接映射到文件上
- 映射可以是映射文件全部区域,也可以是只映射一部分区域
- 文件不会马上加载到内存,而是当发生读写时,才会按需加载
- 按需加载的方式,方便处理大文件
- 比普通的读写效率更高,只有一次拷贝,且内存分配在操作系统内核
用法
-
FileChannel getChannel()
获取FileChannel
对象,由FileInputStream
或FileOutputStream
或RandomAccessFile
类持有该方法 -
MappedByteBuffer map(MapMode mode, long position, long size)
mode 映射模式
READ_ONLY
只读READ_WRITE
读写PRIVATE
私有,更改不映射到文件,也不被其它程序看到
映射完成后,文件就可以关闭,后续对文件的读写可以通过
MappedByteBuffer
MappedByteBuffer
-
int position()
获取当前读写位置 -
Buffer position(int newPosition)
修改当前读写位置 -
byte get()
从当前位置获取一个字节 -
ByteBuffer get(byte[] dst)
从当前位置拷贝dst.length
长度的字节到dst
-
ByteBuffer put(byte[] src)
将字节数组src
写入当前位置
实际应用
消息队列
- 持久化保存在文件中,重启程序消息不会丢失
- 可以供不同的程序进行协作,比如:两个不同的程序,一个生产者,一个消费者,生产者只将消息放入队列,消费者只从队列取消息
序列化
Serializable
- 标记接口,没有定义任何方法
- 实现该接口,即可使用
ObjectInputStream
或ObjectOutputStream
进行读写对象
复杂对象的序列化
- 如果不同对象引用了同一对象,序列化后该对象只保存一份,反序列化后,仍指向同一对象
- 如果不同对象互相引用,反序列化后仍保持原来的引用关系
定制序列化
- 类中将字段声明为
transient
,默认序列化机制将忽略该字段,不会进行保存和恢复 - 实现
writeObject
方法,以自定义该类对象的序列化过程- 声明为
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException
-
s.defaultWriteObject();
调用默认的序列化机制,默认机制会保存所有没声明为transient
的字段,即使类中所有字段都是transient
,也应该写这行,因为Java的序列化机制不仅会保存纯粹的数据信息,还会保存一些元数据描述等隐藏信息
- 声明为
- 实现
readObject
方法,以自定义该类对象的反序列化过程- 声明为
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException
-
s.defaultReadObject();
调用默认的反序列化机制,必需
- 声明为
用途
- 对象持久化
- 跨网络的数据交换,远程过程调用
局限
- 不能实现跨语言的数据交换
- 保存了很多描述信息,格式比较大
- 使用反射遍历对象,性能比较低
- 二进制格式,不方便查看和修改
JSON/XML/MessagePack
目前市场上通用的数据格式有 JSON 和 XML ,容易阅读和理解
而 MessagePack 是一种二进制形式的JSON,编码更为精简高效
工具库
-
JSON:
gson
fastjson
-
XML:
SAX
DOM
Poll
-
MessagePack:
Jackson
常见文件处理
属性文件
使用Properties
类处理此类文件
优点
- 自动处理空格
- 自动忽略空行
- 可以添加注释,以字符
#
或!
开头的行会被视为注释而忽略
限制
- 不能直接处理中文,在配置文件中,所有非 ASCII 字符都需要使用 Unicode 编码
- 可以使用 JDK 的
native2ascii
命令转换为 Unicode 编码
CSV文件
Comma-Separated Values
表示逗号分隔值
一行表示一条记录,一个记录包含多个字段,字段之间用逗号分隔
使用Apache Commons CSV
处理此类文件
Excel文件
使用Apache POI
类库处理此类文件
-
xls:使用
HSSFWorkbook
类 -
xlsx:使用
XSSFWorkbook
类
HTML文件
使用jsoup
处理此类文件
压缩文件
Java SDK 支持两种格式 gzip 和 zip
gzip 只能压缩一个文件,而 zip 文件中可以包含多个文件
如果需要更多格式,考虑Apache Commons Compress
-
gzip:
GZIPInputStream
GZIPOutputStream
-
zip:
ZipInputStream
ZipOutputStream