Java IO流最全详解

Java IO流最全详解

目录

  • Java IO流最全详解
    • 前言
    • 一、IO流简介
    • 二、JAVA IO流的分类
      • 1. 按流的方向分类
      • 2. 按处理的数据单元分类
      • 3. 按处理对象分类
    • 三、Java中四大IO抽象类
    • 四、JAVA IO包中常用的流对象及使用方法
      • 1. 字节流
        • 1.1 文件字节读(输入)写(输出)流
        • 1.2 基于缓冲区的文件字节读写流(高效)
        • 1.3 字节数组流
        • 1.4 随机访问流
        • 1.5 数据流(实现基本类型数据序列化到文件)
        • 1.6 对象流(实现基本类型数据及自定义对象数据序列化到文件)
      • 2. 字符流
        • 2.1 文件字符读(输入)写(输出)流
        • 2.2 基于缓冲区的文件字符读写流(高效)
      • 3. 转换流
      • 4. File类
        • 4.1 操作文件常用方法
        • 4.2 操作目录常用方法
    • 五、Apache IO包扩展
    • 六、参考引用说明

前言

一个程序总是离不开数据的输入(Input)输出(IO),而Java IO流就是用于程序从外部读取数据,以及向外部写出数据的java处理包,本文详细讲解Java IO包中常用的IO流,通过本文的知识,可以使得你在开发中随心所欲的高效使用IO流。

一、IO流简介

以流的方式进行输入输出的操作就是IO(Input Output)流。流是一个抽象的概念,它是一串连续动态的数据集合,可以将流理解为水管,而数据就是水,水通过水管从一端流到另一端
输入流是指的操作,指从外部设备向程序内存中读取数据的过程。
在这里插入图片描述
输出流是指的操作,指从程序内存中向外部设备写入数据的过程。
在这里插入图片描述

二、JAVA IO流的分类

1. 按流的方向分类

输入流:侧重于,数据从数据源读入程序。
输出流:侧重于,数据从程序写出到目的地。

2. 按处理的数据单元分类

字节流:以 字节(1Byte=8bit) 为单位进行读取或者写出数据。
字符流:以 字符为单位进行读取或者写出数据。

比如图片本身就是以二进制存储的,因此采用字节流。
比如文本本身是以字符存储的,因此采用字符流。

3. 按处理对象分类

节点流:直接处理数据源或者目的地数据的读写。
处理流:不直接连接到数据源或目的地,而是为了提高节点流的效率,对节点流进行处理的流,主要指缓冲流

三、Java中四大IO抽象类

Java IO包中的类结构图如图所示。
在这里插入图片描述
上面说道IO主要用于数据的读写,因此从上图也可以看出,IO包主要包括四大抽象类,然后基于这四大抽象类继承从众多的子类,这四大抽象类分别包括Writer、OutputStream、InputStream、Reader,其中Writer和OutputStream都属于输出流,只是Writer是字符输出流,OutputStream是字节输出流;同理,InputStream和Reader都属于输入流,只是Reader是字符输入流,InputStream是字节输入流。Java IO包对于字节流和字符流的命名都很规范,类名中包含Stream的都是字节流,不包含Stream的都是字符流。

四、JAVA IO包中常用的流对象及使用方法

1. 字节流

1.1 文件字节读(输入)写(输出)流
  • Java IO包中以字节流进行读取文件的类是FileInputStream,它是InputStream(字节输入流)抽象类的子类。
    Java IO流最全详解_第1张图片
  • 如使用文件输入流读取文本文件的示例代码如代码1所示:
//代码1
public static void main(String[] args) throws IOException {
	//1. 连接数据:FileInputStream 直接与数据源连接,因此属于节点流
	FileInputStream fis = new FileInputStream("D:\\1.txt");
	//2. 循环逐个读取字节
	int temp=0;
	StringBuilder sb = new StringBuilder();
	while ((temp=fis.read())!=-1)//如果文件中的字节读取完毕,则返回-1,否则返回该字节数值(0-255)
       	sb.append((char)temp);
	System.out.println(sb.toString());
	//3. 关闭流
	fis.close();
}
  • Java IO包中以字节流进行写出数据的类是FileOutputStream,它是OutputStream(字节输出流)抽象类的子类。
    Java IO流最全详解_第2张图片
  • 如使首先使用文件输入流读取图片数据,然后使用文件输出流将读取的数据写出到图片文件中,代码示例如代码2所示:
//代码2
 public static void main(String[] args) throws IOException {
        //1. 连接数据源:直接与数据源连接,因此属于节点流
        FileInputStream fis_img = new FileInputStream("D:/1.jpg");
        //2. 连接目的地:直接与目的地连接,因此属于节点流
        FileOutputStream fos_img = new FileOutputStream("D:/2.jpg");
        //3. 逐个读取字节,并将字节逐个写出
        int temp=0;
        while ((temp=fis_img.read())!=-1)
        {
            fos_img.write(temp);
        }
        //4. 关闭流
        fos_img.flush();
        fis_img.close();
        fos_img.close();
    }
1.2 基于缓冲区的文件字节读写流(高效)

上面所介绍的FileInputStream及FileOutputStream都是一个字节一个字节的读取或者写出数据,那么如果我们每次读取多个字节,同时将多个字节写出,自然比一个一个字节读写的效率高。

这就好比从果园搬100斤的苹果回家,如果你每次拿一个回家,那肯定要很久,而如果你准备一个可以装20斤苹果的麻袋,每次都装满一麻袋带回家,拿5趟就拿完了。这里的麻袋就是缓冲区,每次将多个字节装入缓冲区,然后一次性从缓冲区中写出多个字节

  • 如对代码2,通过缓冲区进行改造,示例代码如代码3所示:
//代码3
 public static void main(String[] args) throws IOException {
        FileInputStream fis_img = new FileInputStream("D:/1.jpg");
        FileOutputStream fos_img = new FileOutputStream("D:/2.jpg");
        int temp=0;
        byte[] buffer = new byte[1024];//设置缓冲区,缓冲区大小去2^n合适
        while ((temp=fis_img.read(buffer))!=-1)//每次读取多个字节存入缓冲区
        {
            fos_img.write(buffer,0,temp);//每次将缓冲区中的多个字节写出
        }
        fos_img.flush();
        fis_img.close();
        fos_img.close();
    }

上面是手动设置的缓冲区大小,对于较小的文件而言,我们明明可以一次性搞定,就没必要分多次,因此将缓冲区的大小设置为文件内容的实际大小即可,为满足这一需求,FileInputStream对象提供了一个估算文件字节个数的方法available,通过该方法获取文件字节个数,然后将其设置为缓冲区大小,即可实现一次性读写文件内容,最大的提高文件读写效率。很明显,缓冲区的空间来自内存,缓冲区越大,那么占用的空间越大,因此,一次性读写数据只适用于较小的数据,如果一次读写较大的数据会占用过多的内存,导致计算机崩溃,或者性能降低。

  • 对代码3进行改造,通过available方法设置缓冲区大小,进而提高读写效率的示例代码,如代码4所示。
//代码4
 public static void main(String[] args) throws IOException {
        FileInputStream fis_img = new FileInputStream("D:/1.jpg");
        FileOutputStream fos_img = new FileOutputStream("D:/2.jpg");
        int temp=0;
        //fis_img.available()直接获取数据的长度,类似一次搬完,不用分批,
        //但这比较占内存,因此适用于小文件,比如一个人只能扛100斤苹果,你让他一次扛200斤,那就累趴了。
        byte[] buffer = new byte[fis_img.available()];
        fis_img.read(buffer);
        fos_img.write(buffer);
        fos_img.flush();

        fis_img.close();
        fos_img.close();
    }

根据通过缓冲区提高IO效率的思想,Java提供了BufferedInputStream和BufferedOutputStream两个处理流,来实现通过缓冲区读写文件。
Java IO流最全详解_第3张图片
Java IO流最全详解_第4张图片

  • 基于java提供的缓冲处理流对代码3进行改造,示例代码如代码5所示:
//代码5
public static void main(String[] args) {
        FileInputStream fis=null;//节点流
        FileOutputStream fos=null;//节点流

        BufferedInputStream bis=null;//处理流:缓冲流,实现原理就是通过缓冲区提高读效率
        BufferedOutputStream bos=null;//处理流:缓冲流,实现原理就是通过缓冲区提高写效率
        try {
            fis = new FileInputStream("D:/1.jpg");
            bis=new BufferedInputStream(fis);
            fos=new FileOutputStream("D:/2.jpg");
            bos=new BufferedOutputStream(fos);

            //默认缓冲区大小:DEFAULT_BUFFER_SIZE = 8192;
            int temp=0;
            while ((temp=bis.read())!=-1)
            {
                bos.write(temp);
            }
            bos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //流的关闭顺序:后开的先关
            try {
                if (bis!=null)
                    bis.close();
               if (fis!=null)
                   fis.close();
               if (bos!=null)
                   bos.close();
               if (fos!=null)
                   fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }
1.3 字节数组流

ByteArrayInputStream将内存中的字节数组对象作为数据源,示例代码如代码6所示。
Java IO流最全详解_第5张图片

//代码6
 public static void main(String[] args) throws IOException {
        byte[] source="mekeater".getBytes();
        //内存中的字节数组对象作为数据源
        ByteArrayInputStream bis = new ByteArrayInputStream(source);
        int temp=0;
        StringBuilder sb = new StringBuilder();
        while ((temp=bis.read())!=-1)
        {
            sb.append((char) temp);
        }
        System.out.println(sb.toString());

        bis.close();
    }

ByteArrayOutputStream将读取的数据写入到字节数组输出流中,示例代码如代码7所示。
Java IO流最全详解_第6张图片

//代码7
 public static void main(String[] args) throws IOException {
        //将流中的数据写入到字节数组中
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        bos.write('m');
        bos.write('e');
        bos.write('k');
        bos.write('e');
        bos.write('a');
        bos.write('t');
        bos.write('e');
        bos.write('r');
        byte[] bytes = bos.toByteArray();
        for (byte b : bytes) {
            System.out.print((char) b);
        }
        bos.close();
    }
1.4 随机访问流

上面几种流读取内容,都是从第一个字节开始逐个往后读取,直至结束,而RandomAccessFile可以指定开始查找的字节位置,如对一个文件采用随机访问流进行读取示例代码如代码8所示。
Java IO流最全详解_第7张图片

//代码8
public static void main(String[] args) throws IOException {
        RandomAccessFile raf = new RandomAccessFile("D:/2.txt", "rw");
        for (int i = 0; i < 10; i++) {
            raf.writeInt(i);
        }
        raf.seek(4);//seek指定开始查找的位置从第4个字节开始,一个int整数占4个字节,因此从第二个位置查找
        System.out.println(raf.readInt());
        //隔一个位置查找一个
        for (int i = 0; i < 10; i+=2) {
            raf.seek(i*4);
            System.out.printf(raf.readInt()+"\t");
        }
        System.out.println();
        //seek指定位置,重新写入该位置的数据,替换原有数据
        raf.seek(4);//指定第4个字节位置,即第二个整数
        raf.writeInt(66);

        raf.seek(0);//重新定位查找的起始位置
        for (int i = 0; i < 10; i++) {
            System.out.print(raf.readInt()+"\t");
        }
    }
1.5 数据流(实现基本类型数据序列化到文件)
  • DataOutputStream将基本数据类型的数据以字节流形式输出到本地文件(类似序列化,但是它只能对基本数据类型进行操作,不能对java自定义对象进行操作),基本操作示例代码如代码9所示。
    Java IO流最全详解_第8张图片
//代码9
public static void main(String[] args) throws IOException {
        DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("D:/data")));
        //写出基本数据类型数据文件中
        dos.writeChar('m');
        dos.writeBoolean(false);
        dos.writeDouble(Math.random());
        dos.writeInt(66);
        dos.writeUTF("Mekeater");
        dos.flush();

        dos.close();
    }
  • DataInputStream从文件中读取存储的基本数据类型的数据,示例代码如代码10所示。
//代码10
public static void main(String[] args) throws IOException {
        DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream("D:/data")));
        //注意读取数据顺序要去写入数据一致
        System.out.println(dis.readChar());
        System.out.println(dis.readBoolean());
        System.out.println(dis.readDouble());
        System.out.println(dis.readInt());
        System.out.println(dis.readUTF());

        dis.close();
    }
1.6 对象流(实现基本类型数据及自定义对象数据序列化到文件)

对象本身也是数据,如果能将内存中对象数据存储到文件,然后通过网络传输该对象,那么对于程序中数据的保存及共享具有重要意义,而序列化和反序列化就是实现这一思想的。
序列化:将程序内存中的任何数据以二进制文件的形式进行保存,并通过网络传输。Java IO中提供了ObjectOutputStream类实现对象的序列化。
Java IO流最全详解_第9张图片

反序列化:将二进制文件恢复为对象数据的过程。Java IO中提供了ObjecInputStream类实现对象的序列化。
Java IO流最全详解_第10张图片
通过对象序列化可以实现对象的持久化及网络通信功能

  • 对象序列化示例代码如代码11所示
//代码11
 public static void main(String[] args) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("D:/data1")));
        //系列化基本数据类型
        oos.writeChar('m');
        oos.writeBoolean(false);
        oos.writeDouble(Math.random());
        oos.writeInt(66);
        oos.writeUTF("Mekeater");
        序列化自定义对象(对象能够序列化,必须实现Serializable标记接口)
        User user = new User(18, "mekeater", true);
        oos.writeObject(user);
        
        oos.flush();
        oos.close();
    }
 
 //实现序列化接口的对象
 public class User implements Serializable {
    private int age;
    private String name;
    private boolean isMan;

    public User() {
    }

    public User(int age, String name, boolean isMan) {
        this.age = age;
        this.name = name;
        this.isMan = isMan;
    }
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isMan() {
        return isMan;
    }

    public void setMan(boolean man) {
        isMan = man;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", isMan=" + isMan +
                '}';
    }
}
  • 对代码11中所序列化的二进制文件进行反序列化,恢复对象数据的示例代码如代码12所示
//代码12
 public static void main(String[] args) throws IOException {
        ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream("D:/data1")));
        //反序列化数据,读取必须和写入顺序一致
        System.out.println(ois.readChar());
        System.out.println(ois.readBoolean());
        System.out.println(ois.readDouble());
        System.out.println(ois.readInt());
        System.out.println(ois.readUTF());
        
        User user = (User)ois.readObject();
        System.out.println(user.getAge());
        System.out.println(user.getName());
        System.out.println(user.isMan());
        ois.close();
    }

2. 字符流

2.1 文件字符读(输入)写(输出)流
  • FileWriter类是Writer抽象类的子类,它实现==将数据以字符为单位写入文件。==示例代码如代码13所示。
    Java IO流最全详解_第11张图片
//代码13
 public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("D:/2.txt");//如果文件存在,则默认覆盖
        //如需追加需要设置参数
        //FileWriter fw = new FileWriter("D:/2.txt",true);
        fw.write("hello IO Stream\r\n");//换行
        fw.write("Mekeater come on!");
        fw.flush();
        fw.close();
    }
  • FileReader类是Reader抽象类的子类,它实现将以字符为单位读取文件内容示例代码如代码14所示。
    Java IO流最全详解_第12张图片
//代码14
public static void main(String[] args) throws IOException {
        FileReader frd = new FileReader("D:/2.txt");
        int temp=0;
        while ((temp=frd.read())!=-1)
        {
            System.out.println((char) temp);
        }
        frd.close();
    }
2.2 基于缓冲区的文件字符读写流(高效)

1.2 基于缓冲区的文件字节读写流思想一致,文件字符流通常是逐个字符读写内容,效率较低,我们可以通过开辟一块空间(数组)每次将读写的数据装满该空间,达到一次读取或者写出多个字符的效果,进而提高读写效率。

  • 逐个字符读写内容,实现文件拷贝功能示例代码如代码15所示:
//代码15
 //通过字符流实现(但这是一个字符一个字符的读写,效率低)
    public static void CopyFile(String source, String target) throws IOException {
        FileReader fr = new FileReader(source);
        FileWriter fw = new FileWriter(target);
        int temp=0;
        while ((temp=fr.read())!=-1)
        {
            fw.write(temp);
        }
        fw.flush();

        fr.close();
        fw.close();
    }
  • 通过缓冲机制对代码15进行改造,示例代码如代码16所示:
//代码16
 //通过字符流实现(通过缓冲区提高读写效率)
    public static void CopyFileBuffer(String source, String target) throws IOException {
        FileReader fr = new FileReader(source);
        FileWriter fw = new FileWriter(target);
        int temp=0;
        char[] buffer = new char[1024];//这个长度只能自己指定,不能像字节流可以获取大概的总长度
        while ((temp=fr.read(buffer))!=-1)
        {
            fw.write(buffer,0,temp);
        }
        fw.flush();

        fr.close();
        fw.close();
    }
  • Java IO包提供了BufferedReader类及BufferedWriter类两个处理流,实现缓冲区的思想,具体使用的示例代码如代码17所示:
    Java IO流最全详解_第13张图片
    Java IO流最全详解_第14张图片
//代码17
public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("D:/2.txt");//节点流
        BufferedWriter bw= new BufferedWriter(fw);//缓冲处理流
        bw.write("BufferedWrite context");
        bw.newLine();
        bw.write("I am Mekeater");
        bw.flush();

        bw.close();
        fw.close();
    }
    
public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("D:/1.txt");//节点流
        BufferedReader br = new BufferedReader(fr);//处理流
        String temp="";
        while ((temp=br.readLine())!=null)
            System.out.println(temp);
        br.close();
        fr.close();
    }

3. 转换流

有时候我们能够方便的获取字节流对象,但是我们需要将字节流转为字符流,这就需要Java IO提供的转换流了,它能将字节流转换为字符流。
如我们可以通过System.in获取用户输入的键盘数据,System.out向控制台输出用户输入的数据,但是System.in和System.out返回的都是字节流对象,而我们需要将字节流转为字符流进行操作更为方便,Java IO提供了InputStreamReader类实现输入字节流转为读取字符流,OutputStreamWriter类实现输出字节流转为写出字符流,具体转换代码如代码18所示。
Java IO流最全详解_第15张图片
Java IO流最全详解_第16张图片

//代码18
 public static void main(String[] args) throws IOException {
        //InputStreamReader将字节输入流转为字符输入流
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        //OutputStreamWriter将字节输出流转为字符输出流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        String line="";
        while (true)
        {
            bw.write("请输入:");
            bw.flush();
            line=br.readLine();
            if (line.equals("exit"))
                break;
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
    }

4. File类

4.1 操作文件常用方法
    public static void main(String[] args) throws IOException {
        File file = new File("D:/2.txt");//连接文件
        System.out.println(file.createNewFile());//创建文件(若存在,则默认覆盖,不存在,则创建)
        System.out.println(file.exists());//判断文件是否存在
        System.out.println(file.getName());//获取文件名
        System.out.println(file.getAbsolutePath());//获取文件的绝对路径
        System.out.println(file.isHidden());//判断文件是否为隐藏文件
        System.out.println(file.delete());//删除文件
    }
4.2 操作目录常用方法
public static void main(String[] args) throws IOException {
        File file = new File("D:/a");//连接目录
        System.out.println(file.mkdir());//创建单级目录
        File file1 = new File("D:/a/b/c");
        System.out.println(file1.mkdirs());//创建多级目录
        System.out.println(file.isFile());//判断是否为文件
        System.out.println(file.isDirectory());//判断是否为目录
        System.out.println(file1.getParent());//获取目录的上一级目录路径

        File file2 = new File("D:/");
        String[] list = file2.list();//获取目录下的所有文件名
        for (String s : list) {
            System.out.println(s);
        }
        System.out.println("=====================");
        File[] files = file2.listFiles();//获取目录下的所有文件对应的File类
        for (File file3 : files) {
            System.out.println(file3.getAbsolutePath());//获取目录下的所有文件的绝对路径
        }
    }

五、Apache IO包扩展

JAVA IO包都是非常基础的IO操作,使用复杂,Apache提供了更高层次的,使用更加方便的IO包Commons IO,通过该包可以更方便对IO进行操作。
Commons IO下载过程:

  1. 访问官网:https://www.apache.org/
  2. 拉到官网最下方,找到Commons
    Java IO流最全详解_第17张图片
  3. 点击Commons,在组件中找到IO
    Java IO流最全详解_第18张图片
  4. 点击IO,选择指定版本下载
    Java IO流最全详解_第19张图片
  5. 解压下载的压缩包,查看docs文件夹下的index.html,查看该包的相关方法
    Java IO流最全详解_第20张图片

六、参考引用说明

尚学堂JAVA IO流视频教程

你可能感兴趣的:(Java,java,IO,节点流,处理流,输入输出流)