一、随机文件流
1. 模式介绍
RandomAccessFile
独立于基本的 IO
流,是专门为文件读写提供的一种设计,与之前的提到的不同的是 RandomAccessFile
可通过指定模式使之既可读又可写,从而提高代码效率。
在初始化对象时传入文件路径并指定操作模式,其中操作模式包含下列四种
(1) r 模式
以只读方式打开指定文件,如果试图对该 RandomAccessFile
指定的文件执行写入方法则会抛出 IOException
。
(2) rw 模式
以读取、写入方式打开指定文件,如果该文件不存在,则尝试创建文件。
(3) rws 模式
以读取、写入方式打开指定文件,相对于 rw
模式,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备,默认情形下(rw
模式下)是使用 buffer
的,只有 cache
满的或者使用 close()
时候才真正的写到文件。
(4) rwd 模式
与 rws
类似,只是仅对文件的内容同步更新到磁盘,而不修改文件的元数据。
2. 基本操作
RandomAccessFile
类的基本基本使用示例如下:
public void initDemo() {
String location = "src\\main\\resources\\info.txt";
try (RandomAccessFile raf = new RandomAccessFile(location, "rw")) {
// 获取当前指针位置
raf.getFilePointer();
// 设置当前指针位置
raf.seek(10);
// 读取单字节数据
raf.read();
// 写入内容
raf.write("message".getBytes());
// 读取 int 值, 同理还有 readLong() 等等
raf.readInt();
// 写入 int 值, 同理还有 writeLong() 等等
raf.writeInt(1);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
3. 文件读取
通过 RandomAccessFile
实现本地文件内容读取。
public void readDemo() {
String sourcePath = "src\\main\\resources\\info.txt";
try (RandomAccessFile raf = new RandomAccessFile(sourcePath, "r")) {
int ch;
// 设置每次读取大小
byte[] buffer = new byte[1024];
while ((ch = raf.read(buffer)) != -1) {
System.out.write(ch);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
4. 文件写入
通过 RandomAccessFile
实现内容数据的写入。
public void writeDemo() {
String msg = "The message from RandomAccessFile.";
String targetPath = "src\\main\\resources\\info.txt";
try (RandomAccessFile raf = new RandomAccessFile(targetPath, "w")) {
// Write data
raf.write(msg.getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
二、基本输入流
1. InputStream
InputStream
是所有输入流的父类,提供基础的 IO
读取服务,通过 read()
方法即可读取文件内容,当读到最后一位时返回 -1
。
注意 InputStream
为无缓冲读取,即读取的字节内容后需要立即写入,在读取大文件时则会导致产生大量的磁盘 IO
操作,因此在实际应用中使用更多的通常是后续提到的缓存流。
InputStream
较为常见的读取方式有如下两种:
- read(): 一次只能读取一个字节数据,当内容较大时则会产生较多的
IO
操作从而影响性能。- read(byte[]): 可指定单次读取的字节大小,通过增加单次读取的数量从而降低读取频次,进而提升性能。
public void inputDemo() {
File file = new File("src\\main\\resources\\info.txt");
try (InputStream in = new FileInputStream(file)) {
// read single byte
in.read();
// read with buffer
in.read(new byte[1024]);
// read specify size from byte array
in.read(new byte[1024], 0, 1024);
// skip specify size from resource
in.skip(4);
} catch (IOException e) {
e.printStackTrace();
}
}
2. FileInputStream
FileInputStream
继承自 InputStream
,用于读取文件内容,其提供两类文件初始化方式,选择传入文件路径或传入 File
类。
下述示例即为一个简单的文件内存读取输出样例:
public void fileInputDemo(){
File file = new File("src\\main\\resources\\info.txt");
try(FileInputStream is = new FileInputStream(file)) {
int ch;
// batch: 单次读取大小
byte[] batch = new byte[256];
while ((ch = is.read(batch)) != -1) {
System.out.write(ch);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
3. InputStreamReader
InputStreamReader
是字节流 (byte)
与字符流 (char)
之间的桥梁,能将字节流输出为字符流,并且能为字节流指定字符集,可输出一个个的字符。
字节流操作汉字或特殊符号语言的时候容易乱码,因此读取文本时可使用字符流实现,但操作二进制文件(比如图片、音频、视频)必须使用字节流。
public void readerDemo(){
File file = new File("src\\main\\resources\\info.txt");
try (
FileInputStream fis = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8)
) {
int ch;
while ((ch = isr.read()) != -1) {
System.out.write(ch);
}
} catch (IOException e) {
e.printStackTrace();
}
}
4. DataInputStream
DataInputStream
支持以 Java
原始数据类型的形式从输入流中读取数据,提供了一组 readXXX()
方法读取不同类型的原始数据类型。
public void dataInputStreamDemo() {
File file = new File("src\\main\\resources\\info.txt");
try (
FileInputStream fis = new FileInputStream(file);
DataInputStream dis = new DataInputStream(fis)
) {
int ch;
while ((ch = dis.read()) != -1) {
System.out.write(ch);
}
} catch (Exception e) {
e.printStackTrace();
}
}
5. ByteArrayInputStream
ByteArrayInputStream
用于读取内存数据,可以从字节数组中读取数据。
public void byteInputStreamDemo() {
String msg = "The message from ByteArrayInputStream.";
byte[] data = msg.getBytes();
try (ByteArrayInputStream bis = new ByteArrayInputStream(data)) {
int ch;
while ((ch = bis.read()) != -1) {
System.out.write(ch);
}
} catch (Exception e) {
e.printStackTrace();
}
}
三、基本输出流
1. OutputStream
OutputStream
为所有输出流的父类,可以实现数据的写入服务,与 InputStream
一样其同样没有缓冲机制会导致大量 IO
操作。
通过 write()
方法即可实现数据的写入,与 read()
类似可通过传入 byte
数组一次性写入多字节数据。
public void outputStreamDemo() {
String location = "src\\main\\resources\\info.txt";
try (FileOutputStream fos = new FileOutputStream(location)) {
// write single data
fos.write(10);
// write specify byte
fos.write(new byte[1024]);
// write specify size from byte array
fos.write(new byte[1024], 0, 1024);
// send cache data to destination
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
2. FileOutputStream
FileOutputStream
继承于 OutputStream
,可以实现文件媒体文件的写入服务。
需要注意其只能写入字节数据,如将字符串写入文件需要先将其转为 byte
类型。
public void fileOutputDemo(){
String location = "src\\main\\resources\\info.txt";
String message = "Message from FileOutputStream.";
byte[] bytes = message.getBytes();
try (OutputStream out = new FileOutputStream(location)) {
// write data
out.write(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println("write complete");
}
3. OutputStreamWriter
OutputStreamWriter
与 InputStreamReader
相对应,可以直接将字符直接写入文件。
public void fileOutputWriteDemo() {
String location = "src\\main\\resources\\info.txt";
try (
FileOutputStream fos = new FileOutputStream(location);
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8")
) {
// Don't have to convert data type
osw.write(message);
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println("write complete");
}
4. DataOutputStream
DataOutputStream
与 DataInputStream
类似,允许以机器无关方式的方式从写入基本 Java
数据类型。
5. ByteArrayOutputStream
ByteArrayOutputStream
可用于往内存写入数据。
四、缓冲输入流
1. BufferedReader
BufferedReader
字符缓冲输入流,提供通用的缓冲方式文本读取。
通过 readLine()
读取一个文本行,从字符输入流中读取文本,缓冲各个字符,从而提供字符、数组和行的高效读取。
public void bufferReadDemo(){
File file = new File("src\\main\\resources\\info.txt");
try (
FileReader fr = new FileReader(file);
BufferedReader bf = new BufferedReader(fr)
) {
String line;
StringBuilder builder = new StringBuilder();
while ((line = bf.readLine()) != null) {
builder.append(line);
}
System.out.println(builder);
} catch (IOException e) {
e.printStackTrace();
}
}
2. BufferedInputStream
在之前介绍了 InputStream
是与目标对象建立了一个连接,再通过 read()
与 write()
进行数据的读取与写入,当读取到数据后必须立即进行写入操作。
而 BufferedInputStream
则在内部自动维护大小为 8192
的缓存区,如果缓存区没有数据或者数据不足才会从底层 InputStream
中读取数据,能有效的减少磁盘 IO
操作从而提高性能。
public void fileInputDemo(){
File sourceFile = new File("src\\main\\resources\\info.txt");
try (
FileInputStream fis = new FileInputStream(sourceFile);
BufferedInputStream bis = new BufferedInputStream(fis)
) {
int ch;
byte[] buffer = new byte[1024];
while ((ch = bis.read(buffer)) != -1) {
System.out.write(buffer, 0, chs);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
五、缓冲输出流
1. BufferedWriter
BufferedWriter
与 BufferedReader
对应,提供带缓存区的写入。
public void bufferWriteDemo() {
String msg = "This is a test from BufferedWriter.";
File file = new File("src\\main\\resources\\test.txt");
try (
FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw)
) {
bw.append(msg);
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println("write complete");
}
2. BufferedOutputStream
BufferedOutputStream
与 BufferedInputStream
对应,内部实现了一个缓冲区数组从而实现更高效的写入服务。
public void bufferedOutputStreamDemo() {
String msg = "This is message from BufferedOutputStream.";
File file = new File("src\\main\\resources\\test.txt");
try (
FileOutputStream fos = new FileOutputStream(file);
BufferedOutputStream bos = new BufferedOutputStream(fos)
) {
bos.write(msg.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Message 4 write complete.");
}
六、数据序列化
在计算机中任何数据都是以二进制的形式存储,因此当项目中涉及到数据对象的传输存储时,通过我们需要将复杂的对象数据转化为字节数据,下面介绍几类常见的对象数据转化。
1. Object转Byte
将 Java
对象序列化为 byte
数组。
public <T> byte[] objectToByte(T t) {
byte[] bytes = null;
try (
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos)
) {
out.writeObject(t);
out.flush();
bytes = bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return bytes;
}
2. Byte转Object
将 byte
数组反序列化为 Java
对象。
public <T> T byteToObject(byte[] bytes, Class<T> tClass) {
Object obj;
try (
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis)
) {
obj = ois.readObject();
} catch (Exception e) {
throw new RuntimeException(e);
}
return (T) obj;
}
3. Byte转InputStream
将 byte
数组转为 InputStream
流对象。
public InputStream byteToInputStream(byte[] bytes) {
return new ByteArrayInputStream(bytes);
}
4. InputStream转Byte
将 InputStream
转为 byte
数组。
public byte[] inputStreamToByte(InputStream in) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
int ch;
byte[] buff = new byte[100];
while ((ch = in.read(buff, 0, 100)) > 0) {
bos.write(buff, 0, ch);
}
return bos.toByteArray();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
5. Byte转OutputStream
将 byte
数组转为 OutputStream
流对象。
public OutputStream byteToOutputStream(byte[] bytes) {
try (OutputStream out = new ByteArrayOutputStream()) {
out.write(bytes);
return out;
} catch (IOException e) {
throw new RuntimeException(e);
}
}