文章目录
前言
本文主要介绍了Java中的IO流,包括IO流的基本概念、文件操作流缓冲操作流和其他流的内容。
1.IO流的概念
(1)IO流的概念和分类
IO是Input和Output的简写,也就是输入和输出的含义,用于进行数据的读和写。
IO流是指读写数据时像流水一样从一端流到另外一端,因此得名为“流”。
按照不同的维度,可以对IO流进行不同的分类,如下:
- 按照读写数据的基本单位不同:
- 字节流
主要指以字节为单位进行数据读写的流,可以读写任意类型的文件 。
- 字符流
主要指以字符(2个字节)为单位进行数据读写的流,只能读写文本文件 。
之所以要使用字符流,是为了满足汉字等多字节文字的需要,提供了一个直接操作字符的接口,方便对字符进行流操作。
- 按照读写数据的方向不同:
- 输入流
主要指从文件中读取数据内容输入到程序中,也就是读文件 。
- 输出流
主要指将程序中的数据内容输出到文件中,也就是写文件 。
- 按照流的角色不同:
- 节点流
主要指直接和输入输出源对接的流。
- 处理流
主要指需要建立在节点流的基础之上的流。
(2)IO流的框架结构
IO流的框架结构如下:
其中,最顶层是抽象基类;
下面分别是处理文件、数组、管道、字符串、缓冲、对象、打印、输入和基本数据类型的流。
最核心的IO流如下:
2.文件操作流
(1)FileWriter类的概念和使用
java.io.FileWriter
类主要用于将文本内容写入到文本文件。
FileWriter类中常用的方法如下:
方法声明 | 功能介绍 |
---|---|
FileWriter(String fileName) | 根据参数指定的文件名构造对象 |
FileWriter(String fileName, boolean append) | 以追加的方式根据参数指定的文件名来构造对象 |
void write(int c) | 写入单个字符 |
void write(char[] cbuf, int off, int len) | 将指定字符数组中从偏移量off开始的len个字符写入此文件输出流 |
void write(char[] cbuf) | 将cbuf字符数组写入文件输出流中 |
void flush() | 刷新流 |
void close() | 关闭流对象并释放有关的资源 |
先测试普通创建模式:
package com.stage1.module4;
import java.io.FileWriter;
import java.io.IOException;
/**
* @author Corley
* @date 2021/8/4 17:06
* @description javase-com.stage1.module4
*/
public class FileWriterTest {
public static void main(String[] args) {
FileWriter fw = null;
try {
// 1.构造FileWriter类型对象与E:/Test/tmp.txt文件关联
fw = new FileWriter("E:/Test/tmp.txt");
// 2.通过流对象写入数据内容
fw.write('a');
System.out.println("写入数据成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != fw) {
// 3.关闭流对象并释放有关的资源
try {
fw.close(); // 自带刷新流的功能
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
写入数据成功
此时在E:/Test
下会新建文件,同时文件的内容为单个a
;
可以得到,选择FileWriter(String fileName)
构造方法时,若文件不存在,FileWriter流会自动创建新的空文件,若文件存在则会清空原有的内容。
再测试追加模式,如下:
package com.stage1.module4;
import java.io.FileWriter;
import java.io.IOException;
/**
* @author Corley
* @date 2021/8/4 17:06
* @description javase-com.stage1.module4
*/
public class FileWriterTest {
public static void main(String[] args) {
FileWriter fw = null;
try {
// 1.构造FileWriter类型对象与E:/Test/tmp.txt文件关联
// fw = new FileWriter("E:/Test/tmp.txt");
fw = new FileWriter("E:/Test/tmp.txt", true);
// 2.通过流对象写入数据内容
fw.write('a');
System.out.println("写入数据成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != fw) {
// 3.关闭流对象并释放有关的资源
try {
fw.close(); // 自带刷新流的功能
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
写入数据成功
多次执行后,会看到文件中的内容为多个a
,说明此时多次写的时候是在后面追加内容的;
选择FileWriter(String fileName, true)
构造方法时,是以追加的方式去创建对象,若文件不存在则自动创建新的空文件并写入内容,若文件存在则保留原有的数据内容、并在后面追加内容。
再测试写入字符数组,如下:
package com.stage1.module4;
import java.io.FileWriter;
import java.io.IOException;
/**
* @author Corley
* @date 2021/8/4 17:06
* @description javase-com.stage1.module4
*/
public class FileWriterTest {
public static void main(String[] args) {
FileWriter fw = null;
try {
// 1.构造FileWriter类型对象与E:/Test/tmp.txt文件关联
// fw = new FileWriter("E:/Test/tmp.txt");
fw = new FileWriter("E:/Test/tmp.txt", true);
// 2.通过流对象写入数据内容
fw.write('a');
// 3.写入数组
// 声明字符数组并初始化
char[] cArr = new char[]{'C', 'o', 'r', 'l', 'e', 'y'};
// 写入部分数组
fw.write(cArr, 1, 3);
// 写入整个数组
fw.write(cArr);
// 刷新流
fw.flush();
System.out.println("写入数据成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != fw) {
// 3.关闭流对象并释放有关的资源
try {
fw.close(); // 自带刷新流的功能
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
写入数据成功
再查看文件,内容为aaaaaorlCorley
,说明成功写入了字符数组。
其中,write()
方法每写入一个字符后文件中的读写位置后移一位。
(2)FileReader类的概念和使用
java.io.FileReader
类主要用于从文本文件读取文本数据内容。
FileReader类常用的方法如下:
方法声明 | 功能介绍 |
---|---|
FileReader(String fileName) | 根据参数指定的文件名构造对象 |
int read() | 读取单个字符的数据并返回,返回-1表示读取到末尾 |
int read(char[] cbuf, int offset, int length) | 从输入流中将最多length个字符数据读入字符数组cbuf中offset开始的位置,返回读取到的字符个数,返回-1表示读取到末尾 |
int read(char[] cbuf) | 从输入流中将字符数据读入字符数组cbuf中,返回读取到的字符个数,返回-1表示读取到末尾 |
void close() | 关闭流对象并释放有关的资源 |
先进行简单使用如下:
package com.stage1.module4;
import java.io.FileReader;
import java.io.IOException;
/**
* @author Corley
* @date 2021/8/4 18:51
* @description javase-com.stage1.module4
*/
public class FileReaderTest {
public static void main(String[] args) {
FileReader fr = null;
try {
// 1.构造FileReader类型的对象与E:/Test/tmp.txt文件关联
fr = new FileReader("E:/Test/tmp.txt");
// 2.读取数据内容
int res = fr.read();
System.out.println("读取到的单个字符是:" + (char) res);
while ((res = fr.read()) != -1) {
System.out.println("读取到的单个字符是:" + (char) res);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != fr) {
// 3.关闭流对象并释放有关的资源
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
读取到的单个字符是:a
读取到的单个字符是:a
读取到的单个字符是:a
读取到的单个字符是:a
读取到的单个字符是:a
读取到的单个字符是:o
读取到的单个字符是:r
读取到的单个字符是:l
读取到的单个字符是:C
读取到的单个字符是:o
读取到的单个字符是:r
读取到的单个字符是:l
读取到的单个字符是:e
读取到的单个字符是:y
可以看到,实现了读取单个字符,并遍历读完了所有字符。
再使用其他方法,如下:
package com.stage1.module4;
import java.io.FileReader;
import java.io.IOException;
/**
* @author Corley
* @date 2021/8/4 18:51
* @description javase-com.stage1.module4
*/
public class FileReaderTest {
public static void main(String[] args) {
FileReader fr = null;
try {
// 1.构造FileReader类型的对象与E:/Test/tmp.txt文件关联
fr = new FileReader("E:/Test/tmp.txt");
// 2.读取数据内容
/*int res = fr.read();
System.out.println("读取到的单个字符是:" + (char) res);
while ((res = fr.read()) != -1) {
System.out.println("读取到的单个字符是:" + (char) res);
}*/
// 定义一个字符数组,用于保存数据内容
char[] cArr = new char[5];
// 读取字符数组的部分空间,读取3个字符放入数组cArr中下标从1开始的位置上
int res = fr.read(cArr, 1, 3);
System.out.println("实际读取到的字符个数为:" + res);
for (char c : cArr) {
System.out.println("读取到的单个字符是:" + (char) c);
}
System.out.println("---------------------------------------");
// 读满整个字符数组
fr = new FileReader("E:/Test/tmp.txt");
res = fr.read(cArr);
System.out.println("实际读取到的字符个数为:" + res);
for (char c : cArr) {
System.out.println("读取到的单个字符是:" + (char) c);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != fr) {
// 3.关闭流对象并释放有关的资源
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
实际读取到的字符个数为:3
读取到的单个字符是:
读取到的单个字符是:a
读取到的单个字符是:a
读取到的单个字符是:a
读取到的单个字符是:
---------------------------------------
实际读取到的字符个数为:5
读取到的单个字符是:a
读取到的单个字符是:a
读取到的单个字符是:a
读取到的单个字符是:a
读取到的单个字符是:a
可以看到,将读取到的内容存到了数组中。
(3)文件字符流实现文件的拷贝
可以使用FileReader和FileWriter类实现文件的拷贝。
如下:
package com.stage1.module4;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* @author Corley
* @date 2021/8/5 7:20
* @description javase-com.stage1.module4
*/
public class FileCharCopyTest {
public static void main(String[] args) {
FileReader reader = null;
FileWriter writer = null;
try {
// 1.创建FileReader类型的对象与E:/Test/a.txt文件关联
reader = new FileReader("E:/Test/a.txt");
// 2.创建FileWriter类型的对象与E:/Test/b.txt文件关联
writer = new FileWriter("E:/Test/b.txt");
// 3.不断地从输入流中读取数据内容并写入到输出流中
System.out.println("开始拷贝...");
int res = 0;
while ((res = reader.read()) != -1) {
writer.write(res);
}
System.out.println("拷贝成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭流对象并释放相关的资源
if (null != writer) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != reader) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
开始拷贝...
拷贝成功!
查看本地文件,新建了b.txt文件,并将a.txt中的内容拷贝到了b.txt中。
关闭IO流的顺序没有明确的规定, 但是一般情况下先创建的流对象后关闭、后创建的流对象先关闭。
(4)文件字节流实现文件的拷贝
前面通过字符流实现了文本文件的拷贝,但是不能成功实现二进制文件(图片、音视频文件等)的拷贝、拷贝得到的文件无效,此时需要使用文件字节流类来实现。
java.io.FileOutputStream
类主要用于将图像数据之类的原始字节流写入到输出流中,常用方法如下:
方法声明 | 功能介绍 |
---|---|
FileOutputStream(String name) | 根据参数指定的文件名来构造对象 |
FileOutputStream(String name, boolean append) | 以追加的方式根据参数指定的文件名来构造对象 |
void write(int b) | 将指定字节写入文件输出流 |
void write(byte[] b, int off, int len) | 将指定字节数组中从偏移量off开始的len个字节写入文件输出流 |
void write(byte[] b) | 将指定字节数组中的全部字节数据写入文件输出流中 |
void flush() | 刷新此输出流并强制写出任何缓冲的输出字节 |
void close() | 关闭流对象并释放有关的资源 |
java.io.FileInputStream
类主要用于从输入流中以字节流的方式读取图像数据等,常用方法如下:
方法声明 | 功能介绍 |
---|---|
FileInputStream(String name) | 根据参数指定的文件路径名来构造对象 |
int read() | 从输入流中读取单个字节的数据并返回,返回-1表示读取到末尾 |
int read(byte[] b, int off, int len) | 从输入流中将最多len个字节的数据读入字节数组中off开始的位置,返回读取到的字节个数,返回-1表示读取到末尾 |
int read(byte[] b) | 从输入流中将最多 b.length 个字节的数据读入字节数组中,返回读取到的字节个数,返回-1表示读取到末尾 |
void close() | 关闭流对象并释放有关的资源 |
int available() | 获取输入流所关联文件的大小 |
文件字节流和文件字符流的方法使用类似。
现在使用文件字节流实现二进制文件的拷贝,如下:
package com.stage1.module4;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author Corley
* @date 2021/8/5 7:41
* @description javase-com.stage1.module4
*/
public class FileByteCopyTest {
public static void main(String[] args) {
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
try {
// 1.创建FileInputStream类型的对象与E:/Test/io-stream.png文件关联
inputStream = new FileInputStream("E:/Test/io-stream.png");
// 2.创建FileOutputStream类型的对象与E:/Test/io-stream-2.png文件关联
outputStream = new FileOutputStream("E:/Test/io-stream-2.png");
// 3.不断从输入流中读取数据内容并写到输出流中
System.out.println("开始拷贝...");
int res = 0;
while (-1 != (res = inputStream.read())) {
outputStream.write(res);
}
System.out.println("拷贝成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭流对象并释放相关的资源
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
开始拷贝...
拷贝成功!
可以看到,成功将二进制文件图片拷贝到副本中,并且查看本地会发现文件是有效的。
(5)文件字节流拷贝文件的分析和改进
前面使用文件字节流方式拷贝文件时,是以单个字节为单位进行拷贝,也就是每次读写一个字节后再写入另一个字节,当文件较小时,可以正常拷贝,但是当文件较大时拷贝的效率很低、需要花很长时间才能复制成功。
此时可以进行改进,准备一个和文件大小一样的缓冲区,一次性将文件的所有内容读入到缓冲区,然后一次性写入输出流。
如下:
package com.stage1.module4;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author Corley
* @date 2021/8/5 7:41
* @description javase-com.stage1.module4
*/
public class FileByteCopyTest {
public static void main(String[] args) {
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
try {
// 1.创建FileInputStream类型的对象与E:/Test/凤凰于飞.flac文件关联
inputStream = new FileInputStream("E:/Test/凤凰于飞.flac");
// 2.创建FileOutputStream类型的对象与E:/Test/凤凰于飞-2.flac文件关联
outputStream = new FileOutputStream("E:/Test/凤凰于飞-2.flac");
// 3.不断从输入流中读取数据内容并写道输出流中
System.out.println("开始拷贝...");
// 方式一:以单个字节为单位进行拷贝,大文件效率较低
/*int res = 0;
while (-1 != (res = inputStream.read())) {
outputStream.write(res);
}*/
// 方式二:使用和文件大小相同的缓冲区,先读入缓冲区、再写入输出流
int size = inputStream.available();
System.out.println("获取到的文件大小:" + size);
byte[] bArr = new byte[size];
int res = inputStream.read(bArr);
System.out.println("实际读取到的文件大小:" + res);
outputStream.write(bArr);
System.out.println("拷贝成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭流对象并释放相关的资源
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
开始拷贝...
获取到的文件大小:37562183
实际读取到的文件大小:37562183
拷贝成功!
此时,再拷贝大文件时,效率更高。
但是这种方式也有一些缺点,当文件过大时,需要申请和文件大小一样的缓冲区,会占用较大的内存,甚至会出现真实物理内存不足的的情况。
再进行优化,可以准备一个大小相对适当的缓冲区(可以以1024的整数倍作为缓冲区大小),分多次完成文件拷贝。
如下:
package com.stage1.module4;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author Corley
* @date 2021/8/5 7:41
* @description javase-com.stage1.module4
*/
public class FileByteCopyTest {
public static void main(String[] args) {
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
try {
// 1.创建FileInputStream类型的对象与E:/Test/凤凰于飞.flac文件关联
inputStream = new FileInputStream("E:/Test/凤凰于飞.flac");
// 2.创建FileOutputStream类型的对象与E:/Test/凤凰于飞-2.flac文件关联
outputStream = new FileOutputStream("E:/Test/凤凰于飞-2.flac");
// 3.不断从输入流中读取数据内容并写道输出流中
System.out.println("开始拷贝...");
// 方式一:以单个字节为单位进行拷贝,大文件效率较低
/*int res = 0;
while (-1 != (res = inputStream.read())) {
outputStream.write(res);
}*/
// 方式二:使用和文件大小相同的缓冲区,先读入缓冲区、再写入输出流,文件过大时会占用较大内存
/*int size = inputStream.available();
System.out.println("获取到的文件大小:" + size);
byte[] bArr = new byte[size];
int res = inputStream.read(bArr);
System.out.println("实际读取到的文件大小:" + res);
outputStream.write(bArr);*/
// 方式三:准备大小适当的缓冲区,多次完成拷贝
byte[] bArr = new byte[1024 * 5];
int res = 0;
while (-1 != (res = inputStream.read(bArr))) {
outputStream.write(bArr, 0, res);
}
System.out.println("拷贝成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭流对象并释放相关的资源
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
开始拷贝...
拷贝成功!
显然,这种方式更合理、安全,效率也更高。
3.缓冲操作流
(1)缓冲字节流实现文件的拷贝
前面的方式是使用自定义缓冲区来提高拷贝的效率,也可以使用Java官方提供的缓冲字节流来实现。
java.io.BufferedOutputStream
类主要用于描述缓冲输出流,此时不用为写入的每个字节调用底层系统,常用方法如下:
方法声明 | 功能介绍 |
---|---|
BufferedOutputStream(OutputStream out) | 根据参数指定的引用构造对象 |
BufferedOutputStream(OutputStream out, int size) | 根据参数指定的引用和缓冲区大小构造对象 |
void write(int b) | 写入单个字节 |
void write(byte[] b, int off, int len) | 写入字节数组中的一部分数据 |
void write(byte[] b) | 写入参数指定的整个字节数组 |
void flush() | 刷新流 |
void close() | 关闭流对象并释放有关的资源 |
java.io.BufferedInputStream
类主要用于描述缓冲输入流,常用方法如下:
方法声明 | 功能介绍 |
---|---|
BufferedInputStream(InputStream in) | 根据参数指定的引用构造对象 |
BufferedInputStream(InputStream in, int size) | 根据参数指定的引用和缓冲区大小构造对象 |
int read() | 读取单个字节 |
int read(byte[] b, int off, int len) | 读取len个字节到b数组中 |
int read(byte[] b) | 读取b.length个字节到b个数组中 |
void close() | 关闭流对象并释放有关的资源 |
现在再使用缓冲字节流实现文件拷贝,如下:
package com.stage1.module4;
import java.io.*;
/**
* @author Corley
* @date 2021/8/5 8:43
* @description javase-com.stage1.module4
*/
public class BufferedByteCopyTest {
public static void main(String[] args) {
BufferedInputStream inputStream = null;
BufferedOutputStream outputStream = null;
try {
// 1.创建BufferedInputStream类型的对象与E:/Test/凤凰于飞.flac文件关联
inputStream = new BufferedInputStream(new FileInputStream("E:/Test/凤凰于飞.flac"));
// 2.创建BufferedOutputStream类型的对象与E:/Test/凤凰于飞-2.flac文件关联
outputStream = new BufferedOutputStream(new FileOutputStream("E:/Test/凤凰于飞-2.flac"));
// 3.不断从输入流中读取数据并写入到输出流中
System.out.println("开始拷贝...");
byte[] bArr = new byte[1024 * 3];
int res = 0;
while (-1 != (res = inputStream.read(bArr))) {
outputStream.write(bArr, 0, res);
}
System.out.println("拷贝成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭流对象并释放有关的资源
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
执行效果与前面相同;
查看源码可以看到,BufferedInputStream提供了默认的缓冲区大小DEFAULT_BUFFER_SIZE
,为8192,也可以在构造BufferedInputStream对象时自定义缓冲区的大小。
(2)缓冲字节流和文件字节流效率比较
现在通过计算文件字节流和缓冲字节流的运行时间,来比较两者的执行效率。
文件字节流测试如下:
package com.stage1.module4;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author Corley
* @date 2021/8/5 7:41
* @description javase-com.stage1.module4
*/
public class FileByteCopyTest {
public static void main(String[] args) {
// 获取当前系统时间距离1970年1月1日0时0分0秒的毫秒数
long t1 = System.currentTimeMillis();
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
try {
// 1.创建FileInputStream类型的对象与E:/Test/demo.mp4文件关联
inputStream = new FileInputStream("E:/Test/demo.mp4");
// 2.创建FileOutputStream类型的对象与E:/Test/demo-2.mp4文件关联
outputStream = new FileOutputStream("E:/Test/demo-2.mp4");
// 3.不断从输入流中读取数据内容并写道输出流中
System.out.println("开始拷贝...");
// 方式一:以单个字节为单位进行拷贝,大文件效率较低
/*int res = 0;
while (-1 != (res = inputStream.read())) {
outputStream.write(res);
}*/
// 方式二:使用和文件大小相同的缓冲区,先读入缓冲区、再写入输出流,文件过大时会占用较大内存
/*int size = inputStream.available();
System.out.println("获取到的文件大小:" + size);
byte[] bArr = new byte[size];
int res = inputStream.read(bArr);
System.out.println("实际读取到的文件大小:" + res);
outputStream.write(bArr);*/
// 方式三:准备大小适当的缓冲区,多次完成拷贝
byte[] bArr = new byte[1024];
int res = 0;
while (-1 != (res = inputStream.read(bArr))) {
outputStream.write(bArr, 0, res);
}
System.out.println("拷贝成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭流对象并释放相关的资源
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
long t2 = System.currentTimeMillis();
System.out.println("使用文件字节流拷贝文件消耗的时间:" + (t2 - t1));
}
}
输出:
开始拷贝...
拷贝成功!
使用文件字节流拷贝文件消耗的时间:1343
缓冲字节流测试如下:
package com.stage1.module4;
import java.io.*;
/**
* @author Corley
* @date 2021/8/5 8:43
* @description javase-com.stage1.module4
*/
public class BufferedByteCopyTest {
public static void main(String[] args) {
// 获取当前系统时间距离1970年1月1日0时0分0秒的毫秒数
long t1 = System.currentTimeMillis();
BufferedInputStream inputStream = null;
BufferedOutputStream outputStream = null;
try {
// 1.创建BufferedInputStream类型的对象与E:/Test/demo.mp4文件关联
inputStream = new BufferedInputStream(new FileInputStream("E:/Test/demo.mp4"));
// 2.创建BufferedOutputStream类型的对象与E:/Test/demo-2.mp4文件关联
outputStream = new BufferedOutputStream(new FileOutputStream("E:/Test/demo-2.mp4"));
// 3.不断从输入流中读取数据并写入到输出流中
System.out.println("开始拷贝...");
byte[] bArr = new byte[1024];
int res = 0;
while (-1 != (res = inputStream.read(bArr))) {
outputStream.write(bArr, 0, res);
}
System.out.println("拷贝成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭流对象并释放有关的资源
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
long t2 = System.currentTimeMillis();
System.out.println("使用缓冲字节流拷贝文件消耗的时间:" + (t2 - t1));
}
}
输出:
开始拷贝...
拷贝成功!
使用缓冲字节流拷贝文件消耗的时间:337
可以看到,在拷贝相同的文件时,文件字节流需要1343毫秒、缓冲字节流需要337毫秒,执行时间存在一定的差距。
所以缓冲字节流拷贝文件的效率更高,在以后的使用中推荐使用缓冲字节流。
(3)缓冲字符流的使用
java.io.BufferedWriter
类主要用于写入单个字符、字符数组以及字符串到输出流中,常用方法如下:
方法声明 | 功能介绍 |
---|---|
BufferedWriter(Writer out) | 根据参数指定的引用来构造对象 |
BufferedWriter(Writer out, int sz) | 根据参数指定的引用和缓冲区大小来构造对象 |
void write(int c) | 写入单个字符到输出流中 |
void write(char[] cbuf, int off, int len) | 将字符数组cbuf中从下标off开始的len个字符写入输出流中 |
void write(char[] cbuf) | 将字符串数组cbuf中所有内容写入输出流中 |
void write(String s, int off, int len) | 将参数s中下标从off开始的len个字符写入输出流中 |
void write(String str) | 将参数指定的字符串内容写入输出流中 |
void newLine() | 用于写入行分隔符到输出流中 |
void flush() | 刷新流 |
void close() | 关闭流对象并释放有关的资源 |
java.io.BufferedReader
类用于从输入流中读取单个字符、字符数组以及字符串,常用方法如下:
方法声明 | 功能介绍 |
---|---|
BufferedReader(Reader in) | 根据参数指定的引用来构造对象 |
BufferedReader(Reader in, int sz) | 根据参数指定的引用和缓冲区大小来构造对象 |
int read() | 从输入流读取单个字符,读取到末尾则返回-1,否则返回实际读取到的字符内容 |
int read(char[] cbuf, int off, int len) | 从输入流中读取len个字符放入数组cbuf中下标从off开始的位置上,若读取到末尾则返回-1,否则返回实际读取到的字符个数 |
int read(char[] cbuf) | 从输入流中读满整个数组cbuf |
String readLine() | 读取一行字符串并返回,返回null表示读取到末尾 |
void close() | 关闭流对象并释放有关的资源 |
使用缓冲字符流实现文本拷贝,如下:
package com.stage1.module4;
import java.io.*;
/**
* @author Corley
* @date 2021/8/5 9:18
* @description javase-com.stage1.module4
*/
public class BufferedCharCopyTest {
public static void main(String[] args) {
BufferedReader reader = null;
BufferedWriter writer = null;
try {
// 1.创建BufferedReader类型的对象与E:/Test/a.txt文件关联
reader = new BufferedReader(new FileReader("E:/Test/a.txt"));
// 2.创建BufferedWriter类型的对象与E:/Test/b.txt文件关联
writer = new BufferedWriter(new FileWriter("E:/Test/b.txt"));
// 3.不断地从输入流中读取一行字符串并写入到输出流中
System.out.println("开始拷贝...");
String str = null;
while (null != (str = reader.readLine())) {
writer.write(str);
writer.newLine();
}
System.out.println("拷贝成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭流对象并释放有关的资源
if (null != writer) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != reader) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
开始拷贝...
拷贝成功!
查看本地文件可以发现,将a.txt中的内容逐行读取并写入到b.txt中;
同时因为b.txt中最后一行后面换行,即添加了换行符\n
、占2字节,所以会比a.txt中多一个字节。
3.其他流
(1)打印流和转换流模拟聊天的功能实现
java.io.PrintStream
类主要用于更加方便地打印各种数据内容,常用方法如下:
方法声明 | 功能介绍 |
---|---|
PrintStream(OutputStream out) | 根据参数指定的引用来构造对象 |
void print(String s) | 用于将参数指定的字符串内容打印出来 |
void println(String x) | 用于打印字符串后并终止该行 |
void flush() | 刷新流 |
void close() | 用于关闭输出流并释放有关的资源 |
可以看到,PrintStream类除了可以向之前经常使用到的控制台打印,还可以向文件写入。
java.io.PrintWriter
类主要用于将对象的格式化形式打印到文本输出流,常用方法如下:
方法声明 | 功能介绍 |
---|---|
PrintWriter(Writer out) | 根据参数指定的引用来构造对象 |
void print(String s) | 将参数指定的字符串内容打印出来 |
void println(String x) | 打印字符串后并终止该行 |
void flush() | 刷新流 |
void close() | 关闭流对象并释放有关的资源 |
java.io.OutputStreamWriter
类主要用于实现从字符流到字节流的转换,常用方法如下:
方法声明 | 功能介绍 |
---|---|
OutputStreamWriter(OutputStream out) | 根据参数指定的引用来构造对象 |
OutputStreamWriter(OutputStream out, String charsetName) | 根据参数指定的引用和编码构造对象 |
void write(String str) | 将参数指定的字符串写入 |
void flush() | 刷新流 |
void close() | 用于关闭输出流并释放有关的资源 |
java.io.InputStreamReader
类主要用于实现从字节流到字符流的转换,如下:
方法声明 | 功能介绍 |
---|---|
InputStreamReader(InputStream in) | 根据参数指定的引用构造对象 |
InputStreamReader(InputStream in, String charsetName) | 根据参数指定的引用和编码构造对象 |
int read(char[] cbuf) | 读取字符数据到参数指定的数组 |
void close() | 用于关闭输出流并释放有关的资源 |
案例题目:
不断地提示用户输入要发送的内容,若发送的内容是"bye"则聊天结束,否则将用户输入的内容写入到文件E:/Test/chat.txt中。
要求使用BufferedReader类来读取键盘的输入 System.in代表键盘输入;
要求使用PrintStream类负责将数据写入文件。
先实现基本功能如下:
package com.stage1.module4;
import java.io.*;
/**
* @author Corley
* @date 2021/8/5 9:59
* @description javase-com.stage1.module4
*/
public class PrintStreamChatTest {
public static void main(String[] args) {
BufferedReader reader = null;
PrintStream printStream = null;
try {
reader = new BufferedReader(new InputStreamReader(System.in));
printStream = new PrintStream(new FileOutputStream("E:/Test/chat.txt"));
while (true) {
// 1.提示用户输入要发送的聊天内容
System.out.println("请输入要发送的聊天内容:");
String str = reader.readLine();
// 2.判断用户输入的内容是否是bye,如果是则结束
if ("bye".equals(str)) {
System.out.println("聊天结束!");
break;
}
// 3.若不是则将用户输入的内容保存到E:/Test/chat.txt中
printStream.println(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭流对象并释放所有的资源
if (null != printStream) {
printStream.close();
}
if (null != reader) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
请输入要发送的聊天内容:
123
请输入要发送的聊天内容:
abc
请输入要发送的聊天内容:
bye
聊天结束!
程序执行结束后,查看本地可以看到新建的文本文件,内容为输入的聊天内容;
因为BufferedReader类的构造方法需要传入Reader类型的引用,但Reader类是个抽象类,实参只能传递子类的对象,属于字符流,而System.in代表健盘输入,是InputStream类型,属于字节流,类型不同,需要将字节流类型转换为字符流类型,此时就需要用到转换流中的InputStreamReader类。
现在进一步实现聊天双方切换聊天的功能,如下:
package com.stage1.module4;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* @author Corley
* @date 2021/8/5 9:59
* @description javase-com.stage1.module4
*/
public class PrintStreamChatTest {
public static void main(String[] args) {
BufferedReader reader = null;
PrintStream printStream = null;
String dateTime = null;
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
try {
reader = new BufferedReader(new InputStreamReader(System.in));
printStream = new PrintStream(new FileOutputStream("E:/Test/chat.txt", true));
// 声明boolean类型的遍历作为发送方切换的标志
boolean flag = true;
while (true) {
// 1.提示用户输入要发送的聊天内容
System.out.println("请" + (flag ? "Corley" : " Jack") + "输入要发送的聊天内容:");
String str = reader.readLine();
// 2.判断用户输入的内容是否是bye,如果是则结束
if ("bye".equals(str)) {
System.out.println("聊天结束!");
break;
}
// 3.若不是则将用户输入的内容保存到E:/Test/chat.txt中
// 获取当前系统时间并调整格式
dateTime = LocalDateTime.now().format(timeFormatter);
printStream.println(dateTime + (flag ? " Corley" : " Jack") + "说:" + str);
flag = !flag;
}
printStream.println();
printStream.println();
printStream.println();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭流对象并释放所有的资源
if (null != printStream) {
printStream.close();
}
if (null != reader) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
请Corley输入要发送的聊天内容:
1
请 Jack输入要发送的聊天内容:
one
请Corley输入要发送的聊天内容:
2
请 Jack输入要发送的聊天内容:
two
请Corley输入要发送的聊天内容:
13
请 Jack输入要发送的聊天内容:
thirteen
请Corley输入要发送的聊天内容:
bye
聊天结束!
查看本地文件,如下:
2021-08-05 10:56:52 Corley说:1
2021-08-05 10:56:54 Jack说:one
2021-08-05 10:56:55 Corley说:2
2021-08-05 10:56:57 Jack说:two
2021-08-05 10:56:59 Corley说:13
2021-08-05 10:57:29 Jack说:thirteen
(2)字符编码
计算机只能识别二进制数据,早期就是电信号。为了方便计算机可以识别各个国家的文字,就需要将各个国家的文字采用数字编号的方式进行描述并建立对应的关系表,该表就叫做编码表。
常用的编码表如下:
编码表 | 说明 |
---|---|
ASCII | 美国标准信息交换码, 使用一个字节的低7位二位进制进行表示 |
ISO8859-1 | 拉丁码表,欧洲码表,使用一个字节的8位二进制进行表示 |
GB2312 | 中国的中文编码表,最多使用两个字节16位二进制进行表示 |
GBK | 中国的中文编码表升级,融合了更多的中文字符,最多使用两个字节16位二进制位表示 |
Unicode | 国际标准码,融合了目前人类使用的所有字符,为每个字符分配唯一的字符码。所有的文字都用两个字节16位二进制位来表示 |
其中,GB2312和GBK编码最多使用2个字节来表示1个中文字符,也有使用1个字节来表示,为了便于区分,最高位为0表示用1个字节来表示、最高位为1表示用2个字节来表示。
Unicode只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,具体存储成什么样的字节流,取决于字符编码方案。但是因为在网络上传输的问题,Unicode在很长一段时间内无法推广,面向传输的众多 UTF(UCS Transfer Format)标准的出现很好地解决了这个问题,推荐的Unicode编码是UTF-8和UTF-16,UTF-8每次8个位传输数据、UTF-16每次16个位。
UTF-8是为传输而设计的编码,并使编码无国界,包含了全世界上所有文化的字符。作为一种变长的编码方式,UTF-8可用1-4个字节来表示一个字符,其中最高位是0表示用1个字节来表示、最高位是110表示用2个字节来表示、最高位是1110表示用3个字节来表示。
(3)数据流的概念和使用
java.io.DataOutputStream
类主要用于以适当的方式将基本数据类型写入输出流中,常用方法如下:
方法声明 | 功能介绍 |
---|---|
DataOutputStream(OutputStream out) | 根据参数指定的引用构造对象, OutputStream类是抽象类,实参需要传递子类对象 |
void writeInt(int v) | 用于将参数指定的整数一次性写入输出流,优先写入高字节 |
void close() | 用于关闭文件输出流并释放有关的资源 |
java.io.DataInputStream
类主要用于从输入流中读取基本数据类型的数据,常用方法如下:
方法声明 | 功能介绍 |
---|---|
DataInputStream(InputStream in) | 根据参数指定的引用来构造对象, InputStream类是抽象类,实参需要传递子类对象 |
int readInt() | 用于从输入流中一次性读取一个整数数据并返回 |
void close() | 用于关闭文件输出流并释放有关的资源 |
先测试数据输出流,如下:
package com.stage1.module4;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author Corley
* @date 2021/8/5 11:25
* @description javase-com.stage1.module4
*/
public class DataOutputStreamTest {
public static void main(String[] args) {
DataOutputStream outputStream = null;
try {
// 1.创建DataOutputStream类型的对象与E:/Test/tmp.txt文件关联
outputStream = new DataOutputStream(new FileOutputStream("E:/Test/tmp.txt"));
// 2.将基本类型数据写入输出流
int num = 111;
outputStream.writeInt(num);
boolean b = false;
outputStream.writeBoolean(b);
System.out.println("写入数据成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3.关闭流对象并释放相关的资源
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
写入数据成功!
查看文件,可以看到,文本中为o
,即前面有3个空格,这是因为int类型为4个字节,111对应的高位(前面3个字节)均为0,ASCII码0对应的就是空字符Null,ASCII码111对应的字符为o;
数据输出流DataOutputStream用1个字节byte的0或1表示false和true,所以false对应的ASCII码为0,对应的字符也是空字符Null;
outputStream.writeInt()
是写入4个字节,outputStream.write()
是写入1个字节,两者有所区别。
再测试数据输入流,如下:
package com.stage1.module4;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @author Corley
* @date 2021/8/5 11:41
* @description javase-com.stage1.module4
*/
public class DataInputStreamTest {
public static void main(String[] args) {
DataInputStream inputStream = null;
try {
// 1.创建DataInputStream类型的对象与E:/Test/tmp.txt文件关联
inputStream = new DataInputStream(new FileInputStream("E:/Test/tmp.txt"));
// 2.从输入流中读取数据
int i = inputStream.readInt();
System.out.println("i = " + i);
boolean b = inputStream.readBoolean();
System.out.println("b = " + b);
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3.关闭流对象并释放所有的资源
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
i = 111
b = false
可以看到,读出了之前写入的数据;
需要注意,如果DataOutputStream调用的是writeInt()
方法,则DataInputStream也要调用对应的readInt()
方法;如果DataOutputStream调用的是write()
方法,则DataInputStream也要调用对应的read()
方法,否则会读到错误的数据。
(4)对象流的概念和使用
java.io.ObjectOutputStream
类主要用于将一个对象的所有内容整体写入到输出流中。
只能将支持 java.io.Serializable
接口的对象写入流中,类通过实现 Serializable 接口来启用其序列化功能 。
其中,序列化主要指将一个对象需要存储的相关信息有效组织成字节序列的转化过程。
ObjectOutputStream类常用方法如下:
方法声明 | 功能介绍 |
---|---|
ObjectOutputStream(OutputStream out) | 根据参数指定的引用来构造对象 |
void writeObject(Object obj) | 用于将参数指定的对象整体写入到输出流中 |
void close() | 用于关闭输出流并释放有关的资源 |
java.io.ObjectInputStream
类主要用于从输入流中一次性将对象整体读取出来,即反序列化。
其中,反序列化主要指将有效组织的字节序列恢复为一个对象及相关信息的转化过程。
ObjectInputStream类常用方法如下:
方法声明 | 功能介绍 |
---|---|
ObjectInputStream(InputStream in) | 根据参数指定的引用来构造对象 |
Object readObject() | 主要用于从输入流中读取一个对象并返回,无法通过返回值来判断是否读取到文件的末尾 |
void close() | 用于关闭输入流并释放有关的资源 |
序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的:
在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会发生序列化版本不一致的异常(InvalidCastException)。
transient是Java语言的关键字,用来表示一个成员不是该对象串行化的一部分,当一个对象被串行化的时候,transient型变量的值不包括在串行化的范围之内,非transient型的变量是被包含在串行化的范围之内的。
先测试ObjectOutputStream,User类如下:
package com.stage1.module4;
/**
* @author Corley
* @date 2021/8/5 12:02
* @description javase-com.stage1.module4
*/
public class User implements java.io.Serializable {
private static final long serialVersionUID = 6439331742069744321L;
private String userName; // 用户名
private String password; // 密码
private transient String phoneNum; // 手机号,不被序列化
public User() {
}
public User(String userName, String password, String phoneNum) {
this.userName = userName;
this.password = password;
this.phoneNum = phoneNum;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", password='" + password + '\'' +
", phoneNum='" + phoneNum + '\'' +
'}';
}
}
测试类如下:
package com.stage1.module4;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/**
* @author Corley
* @date 2021/8/5 12:04
* @description javase-com.stage1.module4
*/
public class ObjectOutputStreamTest {
public static void main(String[] args) {
ObjectOutputStream outputStream = null;
try {
// 1.创建ObjectOutputStream类型的对象与E:/Test/tmp.txt文件关联
outputStream = new ObjectOutputStream(new FileOutputStream("E:/Test/tmp.txt"));
// 2.创建一个User类型的对象并初始化
User user = new User("Corley", "123456", "13122223333");
// 3.将User对象写入输出流
outputStream.writeObject(user);
System.out.println("写入对象成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭流对象并释放有关的资源
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
写入对象成功!
查看文件可以看到,对象的信息已经保存到文件中,虽然是乱码,但是还是可以看到一些关键的信息;
如果User类没有实现Serializable接口开启序列化,会发生java.io.NotSerializableException
异常;
实现Serializable接口时建议显式声明serialVersionUID值,用于序列化时将每个可序列化类与版本号相关联,在反序列化时使用该版本号来验证序列化对象的发送方和接收方是否已加载与该序列化兼容的该对象的类。
再测试ObjectInputStream类,如下:
package com.stage1.module4;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
/**
* @author Corley
* @date 2021/8/5 12:19
* @description javase-com.stage1.module4
*/
public class ObjectInputStreamTest {
public static void main(String[] args) {
ObjectInputStream inputStream = null;
try {
// 1.创建ObjectInputStream类型的对象与E:/Test/tmp.txt文件关联
inputStream = new ObjectInputStream(new FileInputStream("E:/Test/tmp.txt"));
// 2.从输入流中读取对象
Object object = inputStream.readObject();
System.out.println("获取到的对象是:" + object);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
// 3.关闭流对象并释放有关的资源
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
获取到的对象是:User{userName='Corley', password='123456', phoneNum='null'}
可以看懂, 获取到了之前保存的对象信息;
因为User类中phoneNum变量被transient关键字修饰,所以不被序列化、未写入到文件中,所以反序列化时的值是null。
需要注意:
ObjectInputStream类的readObject()
方法无法通过返回值判断是否读到文件末尾。所以当希望将多个对象写入文件时,通常建议将多个对象放入一个集合中,然后将集合这个整体看做一个对象写入输出流中,此时只需要调用一次readObject方法就可以将整个集合的数据读取出来,从而避免了通过返回值进行是否达到文件末尾的判断。
(5)RandomAccessFile类的概念和使用
java.io.RandomAccessFile
类主要支持对随机访问文件的读写操作。
前面的IO流都是从文件开头开始进行读写,再向后移动读写位置;
但是有时候需要从文件中间的某个位置开始读写,此时就需要用到RandomAccessFile类。
RandomAccessFile类常用的方法如下:
方法声明 | 功能介绍 |
---|---|
RandomAccessFile(String name, String mode) | 根据参数指定的名称和模式构造对象: (1)r:以只读方式打开 (2)rw:打开以便读取和写入 (3)rwd:打开以便读取和写入,同步文件内容的更新 (4)rws:打开以便读取和写入,同步文件内容和元数据的更新 |
int read() | 读取单个字节的数据 |
void seek(long pos) | 用于设置从此文件的开头开始计算的文件指针偏移量 |
void write(int b) | 将参数指定的单个字节写入 |
void close() | 用于关闭流并释放有关的资源 |
使用如下:
package com.stage1.module4;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* @author Corley
* @date 2021/8/5 12:44
* @description javase-com.stage1.module4
*/
public class RandomAccessFileTest {
public static void main(String[] args) {
RandomAccessFile accessFile = null;
try {
// 1.创建RandomAccessFile类型的对象与E:/Test/tmp.txt文件关联
accessFile = new RandomAccessFile("E:/Test/tmp.txt", "rw"); // In love folly is always sweet.
// 2.对文件内容进行随机读写操作
// 设置距离文件开头位置的偏移量,从文件开头向后偏移5个字节
accessFile.seek(5);
int res = accessFile.read();
System.out.println("获取到的单个字符:" + (char) res);
res = accessFile.read();
System.out.println("获取到的单个字符:" + (char) res);
accessFile.write('X'); // In loveXfolly is always sweet.
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3.关闭流对象并释放有关的资源
if (null != accessFile) {
try {
accessFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出:
获取到的单个字符:v
获取到的单个字符:e
文件在执行的内容为In love folly is always sweet.
,执行后为In loveXfolly is always sweet.
;
可以看到,通过seek(long pos)
方法设置了偏移量后,是从偏移后的位置开始读写;
write(int b)
方法写入字节时,不是将当前位置之后的字符后移,而是覆盖替换掉当前位置的字符,可以用于数据的修改。
总结
IO流用于Java中处理文件,包含很多基础六,包括文件流、缓冲路、对象流和数据流等,可以根据需要选择进行文件处理。