Java IO流专题

2年前 (2022) 程序员胖胖胖虎阿
154 0 0

Java IO流专题

  • 一、IO流是什么?
    • 1、IO流概念
    • 2、IO流原理及流的分类
  • 二、输入流 使用代码示例
    • 1、FileInputStream 字节流
    • 2、FileReader 字符流
    • 3、BufferedInputStream 字节处理流
    • 4、BufferedReader 字符处理流
  • 二、输出流 使用代码示例
    • 1、FileOutputStream 字节流
    • 2、FileWriter 字符流
    • 3、BufferedOutputStream 字符处理流
    • 4、BufferedWriter 字符处理流
  • 三、实现拷贝功能
  • 四、对象处理流
    • 1、ObjectOutputStream
    • 2、ObjectOutputStream
  • 五、转换处理流
    • 1、InputStreamReader
    • 2、InputStreamReader
  • 六、打印处理流
  • 七、节点流(低级流)和处理流(包装流)区别和联系

一、IO流是什么?

1、IO流概念

IO(Input Output)用于实现对数据的输入与输出操作,Java把不同的输入/输出源(键盘、文件、网络等)抽象表述为流(Stream),简单来说就是对于数据的输入/输出操作以流的方式进行。流是从起源到接收的有序数据,有了它程序就可以采用同一方式访问不同的输入/输出源。

Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。
Java IO流专题

2、IO流原理及流的分类

①、按照数据流向,可以将流分为输入流和输出流,其中输入流只能读取数据、不能写入数据,而输出流只能写入数据、不能读取数据。

    输入流(input):读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
  
    输出流(output):将程序(内存)数据输出到硬盘、光盘等存储设备中。

②、按照数据类型,可以将流分为字节流和字符流,其中字节流操作的数据单元是8位的字节,而字符流操作的数据单元是16位的字符

    字节流:可以用于读写二进制文件及任何类型文件。
  
    字符流:可以用于读写文本文件。

③、按照处理功能,可以将流分为节点流(低级流)和处理流(高级流),其中节点流可以直接从/向一个特定的IO设备(磁盘、网络等)读/写数据,也称为低级流,而处理流是对节点流的连接或封装,用于简化数据读/写功能或提高效率,也称为高级流。

 节点流:可以从或向一个特定的地方(节点)读写数据。如FileInputStream,FileReader。

 处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader、BufferedWriter。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

 节点流是直接作用在文件上的流,可以理解为一个管道,文件在管道中传输。

 处理流是作用在已有的节点流基础上,是包在节点流的外面的管道(可多层),其目的是让管道内的流可以更快的传输。

Java提供了大量的类来支持IO操作,下表给大家整理了其中比较常用的一些类。其中,黑色字体的是抽象基类,其他所有的类都继承自它们。红色字体的是节点流,蓝色字体的是处理流。

Java IO流专题
根据命名很容易理解各个流的作用:

  • 以File开头的文件流用于访问文件;

  • 以ByteArray/CharArray开头的流用于访问内存中的数组;

  • 以Piped开头的管道流用于访问管道,实现进程之间的通信;

  • 以String开头的流用于访问内存中的字符串;

  • 以Buffered开头的缓冲流,用于在读写数据时对数据进行缓存,以减少IO次数;

  • InputStreamReader、InputStreamWriter是转换流,用于将字节流转换为字符流;

  • 以Object开头的流是对象流,用于实现对象的序列化;

  • 以Print开头的流是打印流,用于简化打印操作;

  • 以Pushback开头的流是推回输入流,用于将已读入的数据推回到缓冲区,从而实现再次读取;

以Data开头的流是特殊流,用于读写Java基本类型的数据。

二、输入流 使用代码示例

Java IO流专题

1、FileInputStream 字节流

FileInputStream文件输入流。它通常用于对文件进行读取操作。
注意:这里读取中文会发生乱码,所以有用到读取中文(字符),可以使用字符流来操作。

        //1、读取文件的路径
        String filePath = "D:\\hwx.txt";
        FileInputStream fileInputStream = null;
        byte[] bytes = new byte[8];
        int read = 0;
        try {
            //2、创建对象FileInputStream用来读取文件内容
            fileInputStream = new FileInputStream(filePath);
            //3、如果返回-1,表示文件读取完毕
            while ((read = fileInputStream.read(bytes))!=-1){
                System.out.print(new String(bytes,0,read));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //4、关闭流
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

2、FileReader 字符流

        //1、读取文件的路径
        String filePath = "D:\\hwxs.txt";
        FileReader fileReader = null;
        int read = 0;
        char[] bytes =new char[1024];
        try {
            //2、创建对象FileReader用来读取文件内容
            fileReader = new FileReader(filePath);
            //3、如果返回-1,表示文件读取完毕
            while ((read = fileReader.read(bytes))!=-1){
                System.out.print(new String(bytes,0,read));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //4、关闭流 这样用if判断,是因为如果没有创建FileReader成功,去关闭会报错
                if(fileReader!=null) {
                    fileReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

3、BufferedInputStream 字节处理流

        //1、读取文件的路径
        String filePath = "D:\\hwxs.txt";
        BufferedInputStream bufferedInputStream = null;
        byte[] bytes = new byte[8];
        int read = 0;
        try {
            //2、创建对象BufferedInputStream用来读取文件
            bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
            //3、如果返回-1,表示文件读取完毕
            while ((read = bufferedInputStream.read(bytes)) != -1) {
                System.out.print(new String(bytes, 0, read));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //4、关闭流,注意只需要关闭bufferedInputStream处理流,底层会自动关闭节点流。
                if(bufferedInputStream!=null){
                    bufferedInputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

4、BufferedReader 字符处理流

  		//1、读取文件的路径
        String filePath = "D:\\hwxs.txt";
        BufferedReader bufferedReader = null;
        char[] bytes = new char[8];
        int read = 0;
        String line = null;//按行读取
        try {
            //2、创建对象FileInputStream用来读取文件内容
            bufferedReader = new BufferedReader(new FileReader(filePath));
            //3、如果返回-1,表示文件读取完毕
            while ((read = bufferedReader.read(bytes)) != -1) {
                System.out.print(new String(bytes, 0, read));
            }
            //3、如果返回null,表示文件读取完毕,按行读取(效率高)
            while ((line = bufferedReader.readLine())!=null){
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //4、关闭流,注意只需要关闭bufferedReader处理流,底层会自动关闭节点流。
                if(bufferedReader!=null){
                    bufferedReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

提示:关闭流,注意只需要关闭bufferedReader处理流,因为底层会自动关闭节点流。
Java IO流专题

二、输出流 使用代码示例

1、FileOutputStream 字节流

 		//1、写入文件的路径
        String filePath = "D:\\hwxs.txt";
        FileOutputStream fileOutputStream = null;
        try {
             //根据项目需求,二选一
            //2、创建对象FileOutputStream用来写入文件内容
            fileOutputStream = new FileOutputStream(filePath);//会覆盖原来内容
            fileOutputStream = new FileOutputStream(filePath,true);//在原来的内容后面进行追加
            //3
            //3.1、写入一个字节
            fileOutputStream.write('A');
            //3.2、写入字符串
            fileOutputStream.write("JavaIo".getBytes());
            //3.3、将len字节从位于偏移量off的指定字节数组写入此文件输出流
            //write(byte b[], int off, int len)
            fileOutputStream.write("JavaIo".getBytes(),0,"JavaIo".length());
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //4、关闭流
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

2、FileWriter 字符流

		//1、写入文件的路径
        String filePath = "D:\\hwxs.txt";
        FileWriter fileWriter = null;
        try {
            //2、创建对象FileOutputStream用来写入文件内容(根据需求2选其一)
            fileWriter = new FileWriter(filePath);//会覆盖原来内容
            fileWriter = new FileWriter(filePath,true);//在原来的内容后面进行追加
            //3
            //3.1、写入一个字节
            fileWriter.write('A');
            //3.2、写入字符串
            fileWriter.write("JavaIo流我最爱");
            //3.3、将len字节从位于偏移量off的指定字节数组写入此文件输出流
            //write(byte b[], int off, int len) 写入字符串指定部位
            fileWriter.write("JavaIo流我最爱".toCharArray(),0,"JavaIo流我最爱".length());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //4、关闭流
                if (fileWriter != null) {
                    fileWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

3、BufferedOutputStream 字符处理流

 		//1、写入文件的路径
        String filePath = "D:\\hwxs.txt";
        //2、创建对象BufferedOutputStream用来写入文件
        BufferedOutputStream bufferedOutputStream = null;

        try {
            //根据项目需求,二选一
            bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(filePath));//会覆盖原来内容
           // bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(filePath,true));//在原来的内容后面进行追加

            //3.1、写入一个字节
            bufferedOutputStream.write('A');
            //3.2、写入字符串
            bufferedOutputStream.write("JavaIo".getBytes());
            //3.3、将len字节从位于偏移量off的指定字节数组写入此文件输出流
            //write(byte b[], int off, int len) 写入字符串指定部位
            bufferedOutputStream.write("JavaIo".getBytes(),0,"JavaIo".length());

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(bufferedOutputStream!=null){
                    bufferedOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

4、BufferedWriter 字符处理流

        //1、写入文件的路径
        String filePath = "D:\\hwxs.txt";
        //2、创建对象BufferedWriter用来写入文件内容
        BufferedWriter bufferedWriter = null;
        try {
              //根据项目需求,二选一
              //会覆盖原来内容
            bufferedWriter = new BufferedWriter(new FileWriter(filePath));
            //在原来的内容后面进行追加
            bufferedWriter = new BufferedWriter(new FileWriter(filePath,true));
            bufferedWriter.write("我是第一行");
            
            //插入一个换行符
            bufferedWriter.newLine();
            
            bufferedWriter.write("我是第二行");

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(bufferedWriter!=null){
                    bufferedWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

三、实现拷贝功能

提示:根据上面学到的输入、输出流的使用,我们接下来结合一下输入和输出流,做一个拷贝的功能,比如我需要把C盘的一张照片拷贝到D盘去,因为照片是二进制文件,所以我们需要使用到字节流。我将使用BufferedInputStream以及BufferedOutputStream来完成。

参考代码如下:

  		//1、读取文件的路径
        String filePath = "C:\\io.png";
        //1、输出文件的路径
        String filePathOut = "D:\\io.png";
        BufferedInputStream bufferedInputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        byte[] bytes = new byte[8];
        int read = 0;
        try {
            //2、创建对象BufferedInputStream用来读取文件
            bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
            //2、创建对象BufferedInputStream用来写入文件
            bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(filePathOut));
            //3、如果返回-1,表示文件读取完毕
            while ((read = bufferedInputStream.read(bytes)) != -1) {
                bufferedOutputStream.write(bytes,0,read);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //4、关闭流,注意只需要关闭bufferedInputStream和bufferedOutputStream处理流,底层会自动关闭节点流。
                if(bufferedInputStream!=null){
                    bufferedInputStream.close();
                }
                if(bufferedOutputStream!=null){
                    bufferedOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

四、对象处理流

序列化机制可以将对象转换成字节序列,这些字节序列可以保存在磁盘上,也可以在网络中传输,并允许程序将这些字节序列再次恢复成原来的对象。其中,对象的序列化(Serialize),是指将一个Java对象写入IO流中,对象的反序列化(Deserialize),则是指从IO流中恢复该Java对象。

若对象要支持序列化机制,则它的类需要实现Serializable接口,该接口是一个标记接口,它没有提供任何方法,只是标明该类是可以序列化的,Java的很多类已经实现了Serializable接口,如包装类、String、Date等。

若要实现序列化,则需要使用对象流ObjectInputStream和ObjectOutputStream。其中,在序列化时需要调用ObjectOutputStream对象的writeObject()方法,以输出对象序列。在反序列化时需要调用ObjectInputStream对象的readObject()方法,将对象序列恢复为对象。

1、ObjectOutputStream

序列化对象代码如下:

     	//1、序列号文件的路径
        String filePath = "D:\\data";
        //2、创建对象BufferedOutputStream用来写入文件
        ObjectOutputStream objectOutputStream = null;
        try {
            objectOutputStream =  new ObjectOutputStream(new FileOutputStream(filePath));
            objectOutputStream.writeInt(66);// int ->Integer(实现了Serializable)
            objectOutputStream.writeObject(new Person("大雄有哆啦梦",21));
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(objectOutputStream!=null){
                try {
                    objectOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


//记得序列号
class Person implements Serializable {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


2、ObjectOutputStream

反序列化代码如下:

        //1、反序列号文件的路径
        String filePath = "D:\\data";
        //2、创建对象BufferedOutputStream用来写入文件
        ObjectInputStream objectInputStream = null;
        //3、注意:取出的数据需要与保存的数据位置一致
        try {
            objectInputStream =  new ObjectInputStream(new FileInputStream(filePath));
            // 66
            System.out.println(objectInputStream.readInt());
            try {
                //Person{name='大雄有哆啦梦', age=21}
                System.out.println(objectInputStream.readObject());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(objectInputStream!=null){
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

注意事项:

  1. 读写顺序要一致(上述代码示例中有提醒)
  2. 若对象要支持序列化机制,则它的类需要实现Serializable接口
  3. 序列化的类建议添加SerialVersionUID,那为什么要添加呢?

serialVersionUID代表序列化的版本,通过定义类的序列化版本,在反序列化时,只要对象中所存的版本和当前类的版本一致,就允许做恢复数据的操作,否则将会抛出序列化版本不一致的错误。

如果不定义序列化版本,在反序列化时可能出现冲突的情况,例如:

  • 创建该类的实例,并将这个实例序列化,保存在磁盘上;

  • 升级这个类,例如增加、删除、修改这个类的成员变量;

  • 反序列化该类的实例,即从磁盘上恢复修改之前保存的数据。

在第3步恢复数据的时候,当前的类已经和序列化的数据的格式产生了冲突,可能会发生各种意想不到的问题。增加了序列化版本之后,在这种情况下则可以抛出异常,以提示这种矛盾的存在,提高数据的安全性

  1. 序列化对象时,默认将里面所有属性一起序列化了,除了static或者transient修饰的成员
  2. 序列化对象时,要求里面的属性的类型也需要实现序列号接口
  3. 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也默认已经实现了序列化

五、转换处理流

简单介绍:

  1. InputStreamReader与OutputStream:可以将字节流转换成字符流
  2. 当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决乱码问题,所有建议将字节流转换成字符流(支持指导格式编码:比如utf-8、gbk等等)

1、InputStreamReader

代码示例如下:

  		//1、读取文件的路径
        String filePath = "D:\\hwxs.txt";
        InputStreamReader inputStreamReader = null;
        BufferedReader bufferedReader = null;
        String line = null;
        try {
            //2、把 FileInputStream(filePath) 转换成InputStreamReader,指定了编码
            inputStreamReader = new InputStreamReader(new FileInputStream(filePath),"gbk");
            //3、把inputStreamReader 传入 BufferedReader(读取字符效率高)
            bufferedReader = new BufferedReader(inputStreamReader);
            //4、如果返回null,表示文件读取完毕
            while ((line = bufferedReader.readLine()) != null) {
                System.out.print(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //5、关闭流bufferedReader 关闭最外层流就可以了
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

2、InputStreamReader

代码示例如下:

		//1、写入文件的路径
        String filePath = "D:\\hwxs.txt";
        OutputStreamWriter outputStreamWriter = null;
        String line = null;
        try {
            //2、把 FileInputStream(filePath) 转换成InputStreamReader,指定了编码
            outputStreamWriter = new OutputStreamWriter(new FileOutputStream(filePath),"utf-8");
            //3、写入操作
            outputStreamWriter.write("Java IO从入门到精通");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //4、关闭流outputStreamWriter 关闭最外层流就可以了
                if (outputStreamWriter != null) {
                    outputStreamWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

六、打印处理流

System.out.println();是不是对这题语句特别熟悉,看看下面的代码你就豁然开朗了!

       //从源码中得出 public final static PrintStream out = null;
        PrintStream out = System.out;

        //在默认情况下,PrintStream 输出的位置是 标准输出 即显示器
        out.print("标准输出");

        //System.out.println(); 底层用的是BufferedWriter的write(字节流)进行输出
        out.write("标准输出".getBytes());
        out.close();

        //可以指定输出位置
        System.setOut(new PrintStream("D:\\f1.txt"));
        System.out.println("指定输出位置D:\\f1.txt");

七、节点流(低级流)和处理流(包装流)区别和联系

1、节点流是底层流,直接和数据源相接。
2、处理流包装节点流,即可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入和输出。
3、处理流对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连。
处理流的功能主要体现在以下两个方面:
1、性能提高:主要以增加缓冲的方式来提高输入输出的效率。
2、操作的便捷:处理流可以提供一系列便捷的方法来一次输入输出大批量的数据,使用更加的灵活。

Java IO流的内容结束啦!如果对你有帮助的话还请给个三连~~感谢!

Java IO流专题

版权声明:程序员胖胖胖虎阿 发表于 2022年10月13日 上午6:16。
转载请注明:Java IO流专题 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...