Java 文件拷贝

188 阅读3分钟

文件拷贝

基本代码

FileInputStream fis = new FileInputStream("D:\\itheima\\movie.mp4");
FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");

int b;
while ((b = fis.read()) != -1) {
    fos.write(b);
}

//先开的流最后关闭
fos.close();
fis.close();

弊端与解决方案

  • 弊端
    • 一次只读写一个字节,速度太慢

Snipaste_2022-12-06_14-40-20.png

  • 解决方案

    • 一次读取多个字节
  • FileInputStream一次读取多个字节

方法名称说明
public int read()一次读一个字节数据
public int read(byte [] buffer)一次读取一个字节数组数据
  • 注意
    • 一次读一个字节数组的数据,每次读取会尽可能把数组装满
    • 返回值:本次读取到了多少个字节数据
    • 一般创建的字节数组大小为1024的整数倍
    • 1024 * 1024 * 5 或 1024 * 1024 * 10
    • 使用同一个字节数组存储读取的数据时,读取到的新数据会覆盖数组中的老数据,若新数据的长度小于老数据的长度,多出来的部分不会被清空
      • 解决方案1:在将字节数组转换为字符串的时候,使用重载的构造方法

        String str = new String(bytes, 0, len);
        
        • 解决方案2:使用write方法的重载形式,指定写入数组的开始和结束索引
        fos.write(bytes[], 0, len);
        

改写代码

FileInputStream fis = new FileInputStream("D:\\itheima.mp4");
FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");

byte[] bytes = new byte[1024 * 1024 * 5];
int len;
while ((len = fis.read(bytes)) != -1) {
    fos.write(bytes, 0, len);
}

fos.close();
fis.close();

捕获异常

  • try...catch...finally语句
    • 特点:finally里面的代码一定会被执行,除非虚拟机停止

基本做法

//先创建对象,但不初始化,因为初始化也会有编译时期异常
FileInputStream fis = null;
fileOutputStream fos = null;

try {
    //如果在语句块中创建对象,下面的close方法会找不到对象
    fis = new FileInputStream("D:\\itheima.mp4");
    fos = new FileOutputStream("myio\\copy.mp4");
    
    byte[] bytes = new byte[1024 * 1024 * 5];
	int len;
	while ((len = fis.read(bytes)) != -1) {
    	fos.write(bytes, 0, len);
	}
} catch (IOException e) {
    e.printStackTrace();
} finally {
    //close方法也会报编译时期异常,也要捕获
    //但是如果在初始化对象时文件不存在(这个空指针异常被上面的catch捕获),但是对象中存储的还是null,此时调用close方法还会报空指针异常,所以还要加非空判断
    if (fos != null) {
        try {
            fos.close();
        } catch (IOException e){
            e.printStackTrace();
        }
    }
    
    if (fis != null) {
        try {
            fis.close();
        } catch (IOException e){
            e.printStackTrace();
        }
    }
    
}

简化方案

  • 在JDK7时,Java推出了新的接口AutoCloseable

  • 特点:实现这个接口后,在特定的情况下,可以自动释放资源

基本做法

try {
    可能出现异常的代码;
} catch(异常类名 变量名) {
    异常的处理代码;
} finally {
    执行所有资源释放操作;
}

JDK7方案

  • 资源用完最终自动释放
  • 只有实现了AutoCloseAble接口的类,才能在小括号中创建对象
  • FileInputStream继承自InputStream,InputStream实现了Closeable接口,Closeable继承自AutoCloseable
  • FileOutputStream同理
try(创建流对象1; 创建流对象2) {
    可能出现异常的代码;
} catch(异常类名 变量名) {
    异常的处理代码;
}
try(FileInputStream fis = new FileInputStream("D:\\itheima.mp4");
    FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");) {
    byte[] bytes = new byte[1024 * 1024 * 5];
	int len;
	while ((len = fis.read(bytes)) != -1) {
    	fos.write(bytes, 0, len);
	}
} catch (IOException e) {
    e.printStackTrace();
}

JDK9方案

  • 资源用完最终自动释放
创建流对象1;
创建流对象2;
try(流1; 流2) {
    可能出现异常的代码;
} catch(异常类名 变量名) {
    异常的处理代码;
}
//外面的编译时期异常还是
FileInputStream fis = new FileInputStream("D:\\itheima.mp4");
    FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");
try(fis; fos) {
    byte[] bytes = new byte[1024 * 1024 * 5];
	int len;
	while ((len = fis.read(bytes)) != -1) {
    	fos.write(bytes, 0, len);
	}
} catch (IOException e) {
    e.printStackTrace();
}