本笔记来自 计算机程序的思维逻辑 系列文章
字节流
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()判断文件是否可写 -
separatorseparatorChar文件路径分隔符,如:/和\ -
pathSeparatorpathSeparatorChar多个文件路径的分隔符,如::和;
当构建
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:
gsonfastjson -
XML:
SAXDOMPoll -
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:
GZIPInputStreamGZIPOutputStream -
zip:
ZipInputStreamZipOutputStream