IO流的基本操作

我们都知道现如今绝大部分计算机的组成方式都是著名的冯诺依曼体系,其中有两个部分叫作输入设备和输出设备,输入设备就是键盘,鼠标等能将数据输入到内存中;输出设备就是显示器,音箱等能让数据以特殊的方式让我们感知到。

那么输入设备和输出设备之间具体是如何打交道的呢?其实我们的这些硬件设备都被抽象成了一些文件保存在磁盘上,操作硬件的时候本质上就是在对这些文件进行读写。例如一个进程通过网卡发送以及接收数据,本质上就是在这个进程中对网卡设备抽象出来的文件进行读写操作,把内存中的数据写到网卡文件中就相当于是在发送数据;从网卡文件中读取数据到内存就相当于是在接收数据。

操作系统为我们提供了一组API来针对文件进行读写操作,这组API在Java中就被叫作IO流。
在这里插入图片描述
以读写文件的时候的数据传输方式来划分,IO流分为两类

  1. 字符流,也就是以字符为基本单位进行文件的读写操作。处理文本文档的时候一般就是用到字符流。
  2. 字节流,也就是以字节为基本单位进行文件的读写操作。处理二进制文件的时候一般就是用到字节流。

我们的记事本就是以字符流的形式来将文本文档读取到内存中,再从内存中写到显示器文件中的。所以我们可以用记事本来区分到底是二进制文件还是文本文件,如果我们能看懂,就是文本文件;如果看不懂,就是二进制文件。

由于字符流和字节流对文件的操作方式基本都是一样的,所以我们就以字节流为例子用FileInputStream和FileOutputStream这两个类来演示一下如何操作二进制文件。

public class IODemo3 {//通过流来进行二进制文件的读写操作。也就是字节流的输入输出

    public static void main(String[] args) throws IOException {
        copyFiles("C:\\mkdir\\rose.jpeg","C:\\mkdir\\rosee.jpeg");
    }
    private static void copyFiles(String srcPath,String destPath) throws IOException {
        //1.先打开文件,才能进行读写,也就是创建InputStream/OutputStream的过程,类似于创建File对象的过程。就是打开文件
        //很多编程语言在操作文件的时候都要先打开。Java中的打开是直接new对象就打开了
        FileInputStream f = new FileInputStream(srcPath);
        FileOutputStream ff = new FileOutputStream(destPath);
        //注意:实例化这个对象的时候,构造方法里面可以i使用一个字符串路径来实例化,也可以使用使用一个File对象
        //此处有一个异常需要处理。当传入的异常不存在时候,就会抛出异常
        //1.需要读取文件的内容
        byte[] buffer = new  byte[1024];
        //单次读取的内容是存在上限的,也就是缓冲区(即临时数组的长度)。要想把文件整个读取完,需要搭配一个循环。
  
        //返回值的含义代表当前这次读取实际读到多少个字节,当是-1的时候代表已经读取完了
        int len = -1;
        while((len = f.read(buffer))!=-1){
            //读取成功,就把读到的内容写入到另外一个文件中
            ff.write(buffer,0,len);//此处的写要用第三个版本是因为len不一定和缓冲区一样长
            System.out.println((len));
        }
        //2.把读取到的内容写入到Path对应的文件中
        f.close();
        ff.close();
    }
}

注:
1.为什么代码中在进行读取文件的时候需要搭配一个数组来作为缓冲区?
因为如果读取了一个字节到内存,就立刻又写到磁盘上的另外一个文件中去的话,代码和磁盘打交道的次数将会非常多,而访问内存的速率比访问磁盘的速率远远高出三四个数量级,所以我们可以开辟一个数组空间来作为缓冲区,先将内存中的缓冲区填满,再一次性地写到磁盘中去。可以提高效率。

2.为什么代码末尾需要调用close方法?
涉及到文件资源泄露问题。当在一个进程中打开了一个文件的时候,内核中描述这个进程的PCB中有一个数组结构的文件描述符表,此时这个表里面就会增加一个file_struct,用户描述打开的这个文件。而这个文件描述符表能装的file_struct是有上限的,如果在同一个进程中只是打开不关闭文件的话,当达到上限之后,再想打开文件就会失败。

但是以上代码是有Bug的,代码中有多处地方都会触发异常,一旦触发异常,程序就会立刻终止掉,可能就不会调用到末尾的close方法了。所以我们需要对这个代码进行改进:

public class IOdemo7 {
    public static void main(String[] args) throws IOException {
        copyFiles("C:\\mkdir\\rose.jpeg","C:\\mkdir\\rosee.jpeg");
    }

    private static void copyFiles(String srt, String dest) {
        try(FileInputStream fileInputStream =new FileInputStream(srt);
            FileOutputStream fileOutputStream = new FileOutputStream(dest)) {
            byte[] buffer = new byte[1024];
            int len = -1;
            while((len = fileInputStream.read(buffer))!=-1){
                //读取成功,就把读到的内容写入到另外一个文件中
                fileOutputStream.write(buffer,0,len);//此处的写要用第三个版本是因为len不一定和缓冲区一样长
                System.out.println((len));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

由于所有的流对象都实现了closable接口,所以当触发异常之后,打开的文件都会被自动关闭。

其他的流对象和FileInputStream和FileOutputStream的用法基本类似,这里就不一一介绍了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值