IO-输出流及wirte()方法的理解误区
简介
在 Java 的 IO 输出流中,有三个重载的
write()方法,它们用于向输出流所对应的文件中写入内容,但是如果没有真正理解输出流及write()方法的原理,很容易混淆文件写入时到底是 覆盖 还是 追加
构造器
这里以
FileOutputStream,即 文件字节输出流 为例
1、FileOutputStream(File file)
通过一个
File对象来构建一个FileOutputStream对象,默认新写入的内容会 覆盖 文件中旧的内容,但是这里的 覆盖 只针对于 输出流关闭 之后
2、FileOutputStream(File file, boolean append)
通过一个
File对象来构建一个FileOutputStream对象,第二个参数指定为true,表示新写入的内容会 追加到 文件中旧的内容之后;第二个参数指定为false,表示新写入的内容会 覆盖 文件中旧的内容,这里的 追加 或者 覆盖 都只针对于 输出流关闭之后
FileOutputStream(String path)、FileOutputStream(String path, boolean append) 与 FileOutputStream(File file)、FileOutputStream(File file, boolean append) 的情况是类似的,只是构建 FileOutputStream 对象的方式不同,一种是用 File 对象构建,另一种是用 完整路径(字符串) 构建
write() 方法
write() 写入时是覆盖还是追加?
这里以
write(byte[] buff),即带一个参数的方法来举例说明,另外两个重载方法在写入时是覆盖还是追加的情况都是一样的
/**
* 写入字节数组到输出流对应的文件中
*/
@Test
public void writeFile02(){
String filePath = "d:/java-io/a.txt";
FileOutputStream fileOutputStream = null;
byte[] buff = null;
try {
// 根据完整路径构建一个 FileOutputStream 对象
// 若 文件a.txt 不存在,则会自动创建
// 若 目录java-io 不存在,抛出 FileNotFoundException
fileOutputStream = new FileOutputStream(filePath);
buff = "hello,world".getBytes(); // str.getBytes(() 可以将 字符串 -> 字节数组
// 将当前字节数组 buff 写入到 输出流对应的文件中
// 注意:这里调用了两次 write(buff) 方法,而构造 fileOutputStream 时并没有指定第二个参数为 true,即默认 写入覆盖
// 但是结果却是 a.txt 中有 两个 hello,world
// 按常理来理解:a.txt 中应该只有一个 hello,world 因为写入了两次,第二次应该会覆盖第一次
// 这是因为 调用两次 write(buff) 方法时,此时的 fileOutputStream 都还没有 关闭,所以会写入两次 hello,world
// 第二次写入会 追加,而不是 覆盖
// 那么 构造 fileOutputStream 时 指定不指定第二个参数为 true 还有没有意义呢?
// 有意义:如果构造 fileOutputStream 时指定了第二个参数为 true
// 那么即使流关闭了,新写入的内容会 追加到 旧的内容之后
// 比如:如果这里构造 fileOutputStream 时 没有指定 第二个参数为 true
// 那么 第一次 执行该方法时,会在 a.txt 中写入 两个 hello,world
// 后面 每次执行 都会写入两个 hello,world,但每次都会覆盖,最后 a.txt 中只有两个 hello,world
// 如果这里构造 fileOutputStream 时 指定了 第二个参数为 true
// 那么 第一次 执行该方法时,会在 a.txt 中写入 两个 hello,world
// 后面 每次执行 都会写入两个 hello,world,这时每次都会追加,最后 a.txt 中有 执行次数*2 个 hello,world
fileOutputStream.write(buff);
fileOutputStream.write(buff);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
总结:若在构造 FileOutputStream 对象的时候,指定了 追加,那么 输出流关闭之后,每次的写入都会 追加;反之如果没有指定 追加,即默认 覆盖,那么 输出流关闭之后,每次的写入都会 覆盖。如果输出流 没有关闭(即在一次写入时调用多次 write() 方法),则每次调用 wirte() 都是 追加 而不是 覆盖
write(byte[] buff) vs write(byte[] buff, int off, int len)
write(byte[] buff)与write(byte[] buff, int off, int len)底层都是调用的writeBytes()方法,只是传入的参数不同
write(byte[] buff)->writeBytes(buff, 0, buff.length):偏移量从0(固定)开始,写入的内容长度为buff.length(固定)
write(byte[] buff, int off, int len)->writeBytes(buff, off, len):偏移量从off(指定)开始,写入的内容长度为len(指定)
文件拷贝案例
/**
* 利用 FileInputStream-FileOutprtStream拷贝二进制文件
*/
public class FileCopyMain {
public static void main(String[] args) {
// 将 "d:/waifu.png" 拷贝到 "d:/java-io/waifu.png"
// 1. 创建文件输入流,将文件读入到内存
// 2. 创建文件输出流,将内存中读取到的文件数据,写入到指定的文件中
// 3. 在拷贝过程中,应该是读取部分数据,就写入到指定文件(利用循环)
// 而不是一次性读完再写入(防止文件内容过大而内存不足)
// 边读边写同时保证了在读写过程中,输出流没有关闭,因此循环每次写入时都会追加而不是覆盖
String srcPath = "d:/waifu.png"; // 文件输入流路径
String destPath = "d:/java-io/waifu.png"; // 文件输出流路径
FileInputStream fileInputStream = null; // 文件输入流
FileOutputStream fileOutputStream = null; // 文件输出流
try {
fileInputStream = new FileInputStream(srcPath);
fileOutputStream = new FileOutputStream(destPath);
byte[] buff = new byte[1024];
int readLen = 0;
while ((readLen = fileInputStream.read(buff)) != -1){
// 注意:这里要使用 write(buff, off, len)
// 并且指定的写入的长度为 readLen,readLen 即为真正读取到的字节数
// 假如 waifu.png 的大小为 1025 个字节
// 第一次写入 读取到的 [0-1024],write(buff)、write(buff, off, len) 都没问题
// 但是第二次写入时,读取到的是 [0-1] -> 1个字节
// write(buff) -> writeBytes(buff, 0, buff.length) -> writeBytes(buff, 0, 1024) -> error
// write(buff, 0, 1) -> writeBytes(buff, off, len) -> writeBytes(buff, 0, 1) -> ok
fileOutputStream.write(buff, 0, readLen);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fileInputStream != null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fileOutputStream != null){
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

浙公网安备 33010602011771号