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() 方法,只是传入的参数不同

  1. write(byte[] buff) -> writeBytes(buff, 0, buff.length):偏移量从 0(固定) 开始,写入的内容长度为 buff.length(固定)
  1. 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();
                }
            }
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章