Java中的IO基础操作

492 阅读10分钟

大家好我是wave,这篇文章给大家介绍一下Java中基本IO操作,希望大家看完这篇文章可以对Java中的IO有一个了解,并能自己完成一个文件的拷贝操作

IO

  • BIO:本次给大家介绍的IO都是Java中的BIO,就是同步阻塞的IO方式。这种方式的IO后序任务会等待当前的调用返回,并且当前线程会阻塞,文章后面所讲的都是BIO的内容
  • NIO:同步非阻塞IO,这种IO方式相比BIO线程不会阻塞,所以在很多场景下NIO的效率高于BIO。Netty框架的底层就是NIO,Zookeeper源码中也用到了NIO。
  • AIO:非同步非阻塞IO,这种方式目前还使用的比较少,Netty5的设计就是希望把NIO换成AIO,但是设计者们发现这样不仅让操作变的更加复杂了,但是效率并没有明显的提升,所以Netty5整个都被移除了。

File

  • IO操作就是对文件的操作,在Java万物皆对象的思想下,我们不难想到肯定会有一个类是用来表示文件的。
  • Java中用来表示一个文件的类就是File类,使用这个类可以对文件进行一系列的操作,比如打开文件、获取文件路径、获取文件名字等等操作。
  • 我们要对文件进行操作,就必须依靠这个对象来打开文件
File对象的部分方法
File对象的部分方法

大致扫一眼这张图,其实会发现这些方法都是用于操作一个文件,但是并不能操作文件里面的值,那Java中是怎么操作文件的呢?这就要说到Java中的IO流了

Stream

什么是流?

  • 当不同的介质之间有数据交互的时候,JAVA就使用流来实现。数据源可以是文件,还可以是数据库,网络甚至是其他的程序
  • 也就是说如果需要进行数据的交互,比如说把文件的内容取出来、网络传输这类操作都需要依靠流。
  • 在Java中流根据传输的不同方向分为输入流和输出流。
  • 输入流:程序从输入流读取数据源。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道。
  • 输出流: 程序向输出流写入数据。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道。
  • 流根据传输的时候根据数据的不同又可以分为字符流和字节流,字符流和字节流就比较好区分了,字符流用于处理存文本信息,字节流可以处理所有信息,因为所有文件都可以转化成二进制的形式。
  • 综上,Java就有了四个最基本的抽象类:InputStream、OutputStream,Reader、Writer。Java中所有对流进行操作的类,都直接或间接继承了这四个抽象类的其中一个。
Stream类图
Stream类图

字符流演示

FileReader

 public static  void main(String arg[])throws Exception {
        //打开一个文件
        FileReader fileReader = new FileReader("F:/xxx.txt");
        //新建一个char数组用来存放数据
        char buf[] = new char[1024];
        //把内容读进buf数组里面
        fileReader.read(buf);
        //打印文件内容
        System.out.println(new String(buf));
    }
  • 我们之前说过,InputStream、OutputStream,Reader、Writer都是一个抽象类,那么我们研究的肯定是他们对应的实现类,文章的开头也给大家介绍了一下File这个类,想要打开文件要借助File这个类,所以大胆猜测FileReader就是一个可以打开文件的字符输入流。
  • 同样的,InputStream、OutputStream、Writer也都有对应的FileInputStream、FileOutStream、FileWriter。
  • 这里所使用的思想就是装饰器设计模式,这里不对这个设计模式进行展开,这里只要能理解我们可以使用一个方法更多的Reader的实现类来加强字符输入流的功能

体验装饰器设计模式的感觉

public static  void main(String arg[])throws Exception { 
        //File类,只能对文件进行操作,不能对文件内容进行操作
        File f = new File("F:/xxx.txt");
        //使用FileReader对File类进行增强,这样就可以对文件内容进行操作了
        FileReader fr = new FileReader(f);
        //继续对FileReader进行增强,br相比于fr会有更多方法,功能更强大
        BufferedReader br = new BufferedReader(fr);
    }
  • 装饰器模式通俗的来说可以理解为套娃模式,内部使用了继承和组合的方式,可以增强某个流的功能。

FileWriter

 public static  void main(String arg[])throws Exception {
        //新建一个字符输出流
       FileWriter fw = new FileWriter("F:/xxx.txt");
       //向文件写入一个字符串
       fw.write("HelloWorld");
       //关闭流
       fw.close();
    }

输出流向文件写入数据的时候使用write方法,但是这个方法会先把数据写入一个缓存,并不会马上写入文件,所以需要调用flush方法才可以成功的把数据写入到文件中。

字符流的操作非常简单,只需记住read方法可以把文件内容读取到输入流,write方法可以将输出流中的数据写入到一个文件。如果需要功能更强大的字符输入流和字符输出流,就去找找Reader与Wirter的实现类,再使用装饰器设计模式进行一个功能的加强就可以了,是不是非常简单呢~

字节流演示

FileInputStream

public static  void main(String arg[])throws Exception {
        //创建一个FileInputStream对象并打开一个mp3文件
       FileInputStream fs = new FileInputStream(new File("E:/wudi.mp3"));
       //创建一个buf用来读取输入流中的字节数据
       byte[] buf = new byte[1024];
       //定义一个变量判断是否文件已经读完了,如果这个变量为-1就说明已经读完了
       int n = -1;
       //循环读,因为一个文件的大小肯定会有可能大于1204字节,所以需要循环读
       while ((n = fs.read(buf)) != -1){
           // do something
       }
       //文件操作结束需要进行关闭,以免浪费资源
       fs.close();
    }
  • 可以看到字节流与字符流还是很相似的,只不过char数组变成了byte数组。

FileOutputStream

public static  void main(String arg[])throws Exception {
        //创建一个输出流并打开一个文件,如果文件不存在会自动创建
        FileOutputStream fo = new FileOutputStream(new File("E:/wudi1.mp3"));
        //写入数据,一般需要配合输入流进行循环写入
        fo.write(new byte[1024]);
        //把数据从缓冲区写入到文件
        fo.flush();
        //输出流用完也要及时关闭
        fo.close();
    }
  • 将两段代码组合起来完成mp3文件的拷贝
 public static  void main(String arg[])throws Exception {
        //创建一个FileInputStream对象并打开一个mp3文件
        FileInputStream fs = new FileInputStream(new File("E:/wudi.mp3"));
        //创建一个输出流并打开一个文件,如果文件不存在会自动创建
        FileOutputStream fo = new FileOutputStream(new File("E:/wudi1.mp3"));
        //创建一个buf用来读取输入流中的字节数据
        byte[] buf = new byte[1024];
        //定义一个变量判断是否文件已经读完了,如果这个变量为-1就说明已经读完了
        int n = -1;
        //循环读,因为一个文件的大小肯定会有可能大于1204字节,所以需要循环读
        while ((n = fs.read(buf)) != -1){
            // do something
        }
        //文件操作结束需要进行关闭,以免浪费资源
        fs.close();
        //把数据从缓冲区写入到文件
        fo.flush();
        //输出流用完也要及时关闭
        fo.close();
    }
  • 运行这段代码就可以拷贝文件啦~

commons-io

在Java中也有一个专门处理IO操作的jar包

改进一下拷贝mp3文件的操作

public static  void main(String arg[])throws Exception {
        //创建一个FileInputStream对象并打开一个mp3文件
        FileInputStream fs = new FileInputStream(new File("E:/wudi.mp3"));
        //创建一个输出流并打开一个文件,如果文件不存在会自动创建
        FileOutputStream fo = new FileOutputStream(new File("E:/wudi1.mp3"));
        //使用IOUtils包的方法进行拷贝
        IOUtils.copy(fs,fo);
        //文件操作结束需要进行关闭,以免浪费资源
        fs.close();
        //把数据从缓冲区写入到文件
        fo.flush();
        //输出流用完也要及时关闭
        fo.close();
    }
  • 这样是不是简洁了很多呢,而且这个包是apache开发的包,所以我们也可以避免写循环的时候出现的一些错误。
  • 这个包里面所带的方法也非常的丰富,大家可以自行探索一下,以后处理IO操作就可以用这个包来简化操作了。

BIO读取流程

BIO读取过程图
BIO读取过程图
  • 这里再给大家讲一下BIO的读取过程。BIO的读取需要进行两次次拷贝过程:
  • 从磁盘读取数据,把数据放入内核态,然后写到用户态

本次的分享就到这里结束了,我们下次见。

扫码关注一波吧~