java基础【IO流】

86 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

1、四个基本的抽象基类

        InputStream             字节输入流

        OutputStream          字节输出流

        Reader                     字符输入流

        Writer                       字符输出流

        读写文件的步骤:

        (1)打开流,如果是读文本文件,用FileReader,写文本文件,用FileWriter,读二进制文件,用FileInputStream,写二进制文件,用FileOutputStream

        (2)通过流处理数据

        (3)关闭流

2、字符输入流 Reader 和字节输入流 inputStream

FileReader读取文件过程

简略版本:

    @Test
    public void test1() throws IOException{
        FileReader fileReader = new FileReader("一个文本文件");
        int ch = fileReader.read();
        while(ch != -1){
            System.out.print((char)ch);
            ch = fileReader.read();
        }
        fileReader.close();
    }

标准版本:

    @Test
    public void test(){
        //1)声明引用,初始化为null
        FileReader fileReader = null;
        //2)try catch finally
        try{
            //5)创建流对象,建立通道
            fileReader = new FileReader("一个文本文件"); //在当前目录读取此文件,项目目录是当前目录
            //6)处理数据
            int ch = fileReader.read();     //读到的是字符的码值
            while(ch != -1){
                System.out.print((char)ch);     //将码值强转成字符
                ch = fileReader.read();
            }
        } catch (IOException e){
            //4)处理异常
            e.printStackTrace();
        }finally {  //一定可以关闭流
            //3)关闭流
            if(fileReader != null){
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

问题: 一个一个读,效率太低!

使用缓冲区读取文本文件

    @Test
    public void test3(){
        FileReader fileReader = null;
        try{
            fileReader = new FileReader("一个文本文件");
            char[] buff = new char[100];
            int realCount = fileReader.read(buff);
            while(realCount != -1){
                //处理已经读到的数据
                for(int i = 0; i < realCount; i++){     //处理实际读到的字符数
                    System.out.print(buff[i]);
                }
                //继续读后面的数据
                realCount = fileReader.read(buff);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(fileReader != null){
                try {
                    fileReader.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

练习: 使用缓冲区读文件,并且加上每行的行号

    @Test
    public void test4(){
        FileReader fileReader= null;
        int line = 1;
        System.out.print(line + " ");
        try {
            fileReader = new FileReader("CollectionTest.java");
            char[] buff = new char[100];
            int realCount = fileReader.read(buff);
            while(realCount != -1){
                for(int i = 0; i < realCount; i++){
                    System.out.print(buff[i]);
                    if(buff[i] == '\n') {
                        System.out.print(++line + " ");
                    }
                }
                realCount = fileReader.read(buff);
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            if(fileReader != null){
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

缓冲区大小为8192时,读取文件的效率最好。

3、字符输出流 Writer 和字节输出流 OutputStream

FileWriter写文本文件操作

    @Test
    public void test2(){
        FileWriter fileWriter = null;
        try {
            fileWriter = new FileWriter("写一个文本文件");
            fileWriter.write('a');
            fileWriter.write('b');
            fileWriter.write('c');
            fileWriter.write('\r');
            fileWriter.write('\n');

            fileWriter.write('1');
            fileWriter.write('2');
            fileWriter.write('3');
            fileWriter.write(13);
            fileWriter.write(10);

            fileWriter.write('我');
            fileWriter.write('是');
            fileWriter.write('汉');
            fileWriter.write(13);
            fileWriter.write(10);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(fileWriter != null){
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

使用缓冲区批量写入文本数据

    @Test
    public void test5(){
        FileWriter fileWriter = null;
        try {
            fileWriter = new FileWriter("写一个文本文件");
            String[] contents = {"随便写上一些内容1",
                    "随便写上一些内容2",
                    "随便写上一些内容3",
                    "jfkdsdlnmnc",
                    "积分卡死了讲课费街坊邻居斯柯达",
                    "随便写上一些内容1",
                    "随便写上一些内容1",};
            for(int i = 0; i < contents.length; i++){
                char[] charArray = contents[i].toCharArray();
                //fileWriter.write(charArray);        //直接把一个数组的内容写入到输出流中
                //fileWriter.write(charArray,4,5);   //第二个参数是offset偏移量,第三个参数是lenth长度,表示从下标4开始,每次写5个字符
                fileWriter.write(charArray,4,charArray.length - 4);
                fileWriter.write(13);       //写回车
                fileWriter.write(10);       //写换行
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            if(fileWriter != null){
                try {
                    fileWriter.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

练习: 编写程序FileCopy.java,在测试方法中,将FileCopy.java复制为FileCopy.java.bak文件。

    @Test
    public void test(){

        FileReader fileReader = null;
        FileWriter fileWriter = null;

        try {
            fileReader = new FileReader("FileCopy.java");
            fileWriter = new FileWriter("FileCopy.java.bak");
            char[] buff = new char[100];
            //以读为主导
            int realCount = fileReader.read(buff);
            while(realCount != -1){
                //处理已经读到的数据
                //在处理已经读到的数据时,写文件
                fileWriter.write(buff,0,realCount);
                //继续读
                realCount = fileReader.read(buff);
            }

        } catch(Exception e){
            e.printStackTrace();
        } finally {
            if(fileReader != null){
                try {
                    fileReader.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if(fileWriter != null){
                try {
                    fileWriter.close();
                }catch (Exception e1){
                    e1.printStackTrace();
                }
            }
        }
    }
}

缓冲流 FileInputStream FileOutputStream

使用缓冲流复制二进制文件,非文本文件。缓冲流可以处理任意文件。

    @Test
    public void test1(){
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        try {
            fileInputStream = new FileInputStream("1.mp3");
            fileOutputStream = new FileOutputStream("2.mp3");
            byte[] buff = new byte[8192];
            int realCount = fileInputStream.read(buff);
            while(realCount != -1){
                fileOutputStream.write(buff,0,realCount);
                realCount = fileInputStream.read(buff);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(fileInputStream != null){
                try {
                    fileInputStream.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if(fileOutputStream != null){
                try {
                    fileOutputStream.close();
                }catch (Exception e1){
                    e1.printStackTrace();
                }
            }
        }
    }

4、缓冲流BufferedReader和BufferedWriter(处理流)

使用包装流BufferedReader读取文件内容

    @Test
    public void test6(){
        FileReader fileReader = null;
        BufferedReader bufferedReader = null;
        try {
            fileReader = new FileReader("FileCopy.java");
            bufferedReader = new BufferedReader(fileReader);
            //最有价值的方法
            String line = bufferedReader.readLine();
            int count = 1;
            System.out.print(count + " ");
            while(line != null){
                System.out.println(line);
                line = bufferedReader.readLine();
                System.out.print(++count + " ");
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(bufferedReader != null){
                try {
                    //只需要关闭高级流
                    bufferedReader.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

使用包装流BufferedWriter写文件

    @Test
    public void test7(){
        FileWriter fileWriter = null;
        BufferedWriter bufferedWriter = null;
        try {
            fileWriter = new FileWriter("使用缓冲流写文本");
            bufferedWriter = new BufferedWriter(fileWriter);
            String[] contents = {"随便写上一些内容1",
                                "随便写上一些内容2",
                                "随便写上一些内容3",
                                "jfkdsdlnmnc",
                                "积分卡死了讲课费街坊邻居斯柯达",
                                "随便写上一些内容1",
                                "随便写上一些内容1",};
            for(String string : contents){
                bufferedWriter.write(string);
                //最有价值的方法,可以写入跨平台的换行
                bufferedWriter.newLine();
            }

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

5、终极处理流:对象流

ObjectInputStream和ObjectOutputStream字节流

使用对象输出流ObjectOutputStream写二进制文件

    @Test
    public void test8(){
        FileOutputStream fileOutputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        ObjectOutputStream objectOutputStream = null;

        try {
            fileOutputStream = new FileOutputStream("对象输出流二进制文件");
            bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
            objectOutputStream = new ObjectOutputStream(bufferedOutputStream);

            objectOutputStream.writeInt(10);    //数据在内存中如何保存,它就如何写入文件
            objectOutputStream.writeBoolean(false);
            objectOutputStream.writeDouble(3.33);

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

使用对象输入流ObjectInputStream读二进制文件

    @Test
    public void test9(){
        FileInputStream fileInputStream = null;
        BufferedInputStream bufferedInputStream = null;
        ObjectInputStream objectInputStream = null;

        try {
            fileInputStream = new FileInputStream("对象输出流二进制文件");
            bufferedInputStream = new BufferedInputStream(fileInputStream);
            objectInputStream = new ObjectInputStream(bufferedInputStream);

            int readInt = objectInputStream.readInt();
            boolean readBoolean = objectInputStream.readBoolean();
            double readDouble = objectInputStream.readDouble();
            System.out.println(readInt + " " + readBoolean + " " + readDouble);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(objectInputStream != null){
                try {
                    objectInputStream.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

练习:使用对象流写入50个100以内的随机整数,并使用对象输入流读取出来

    @Test
    public void test10(){
        FileInputStream fileInputStream = null;
        BufferedInputStream bufferedInputStream = null;
        ObjectInputStream objectInputStream = null;

        try {
            fileInputStream = new FileInputStream("写入50个100以内随机整数");
            bufferedInputStream = new BufferedInputStream(fileInputStream);
            objectInputStream = new ObjectInputStream(bufferedInputStream);
            for(int i = 0; i < 50; i++){
                System.out.println(objectInputStream.readInt());
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(objectInputStream != null){
                try {
                    objectInputStream.close();
                }catch (Exception e1){
                    e1.printStackTrace();
                }
            }
        }
    }

    @Test
    public void exer(){
        FileOutputStream fileOutputStream = null;
        BufferedOutputStream bufferedoutputStream = null;
        ObjectOutputStream objectOutputStream = null;

        try {
            fileOutputStream = new FileOutputStream("写入50个100以内随机整数");
            bufferedoutputStream = new BufferedOutputStream(fileOutputStream);
            objectOutputStream = new ObjectOutputStream(bufferedoutputStream);
            for(int i = 0; i < 50; i++){
                objectOutputStream.writeInt((int)(Math.random() * 100));
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (objectOutputStream != null){
                try {
                    objectOutputStream.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

使用ObjectOutputStream序列化java对象

序列化:将java对象输出到流中,以便方法的远程调用(网络调用)

class Student implements Serializable{
    private int id;
    private String name;
    private int age;
    private int grade;

    public Student() {
    }

    public Student(int id, String name, int age, int grade) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.grade = grade;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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 int getGrade() {
        return grade;
    }

    public void setGrade(int grade) {
        this.grade = grade;
    }

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

    @Test
    public void serialize() throws IOException {
        FileOutputStream fileOutputStream = null;
        BufferedOutputStream bufferedoutputStream = null;
        ObjectOutputStream objectOutputStream = null;

        try {
            fileOutputStream = new FileOutputStream("对象序列化");
            bufferedoutputStream = new BufferedOutputStream(fileOutputStream);
            objectOutputStream = new ObjectOutputStream(bufferedoutputStream);

            Student s1 = new Student(1, "张三", 14, 2);
            Student s2 = new Student(1, "张三1", 14, 2);
            Student s3 = new Student(1, "张三2", 14, 2);
            
            objectOutputStream.writeObject(s1);
            objectOutputStream.writeObject(s2);
            objectOutputStream.writeObject(s3);

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

注意: 静态属性不可以被序列化,只序列化对象在GC区中的数据,被transient修饰的属性不能被序列化(private transient int age)

使用ObjectInputStream实现对象的反序列化

    @Test
    public void unserialize(){
        FileInputStream fileInputStream = null;
        BufferedInputStream bufferedInputStream = null;
        ObjectInputStream objectInputStream = null;

        try {
            fileInputStream = new FileInputStream("对象序列化");
            bufferedInputStream = new BufferedInputStream(fileInputStream);
            objectInputStream = new ObjectInputStream(bufferedInputStream);

            Object object1 = objectInputStream.readObject();
            Object object2 = objectInputStream.readObject();
            Object object3 = objectInputStream.readObject();

            System.out.println(object1);
            System.out.println(object2);
            System.out.println(object3);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(objectInputStream != null){
                try {
                    objectInputStream.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

练习:写一个person类,包含属性name,age,gender,要求性别不序列化,创建两个对象,序列化到文件中,查看文件,并反序列化,拿到对象。

class Person implements Serializable{
    private String name;
    private int age;
    private transient String gender;

    public Person() {
    }

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

    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 String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

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

    @Test
    public void exer2(){
        FileInputStream fileInputStream = null;
        BufferedInputStream bufferedInputStream = null;
        ObjectInputStream objectInputStream = null;

        try {
            fileInputStream = new FileInputStream("序列化对象练习");
            bufferedInputStream = new BufferedInputStream(fileInputStream);
            objectInputStream = new ObjectInputStream(bufferedInputStream);

            System.out.println(objectInputStream.readObject());
            System.out.println(objectInputStream.readObject());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(objectInputStream != null){
                try {
                    objectInputStream.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

    @Test
    public void exer1(){
        FileOutputStream fileOutputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        ObjectOutputStream objectOutputStream = null;

        try {
            fileOutputStream = new FileOutputStream("序列化对象练习");
            bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
            objectOutputStream = new ObjectOutputStream(bufferedOutputStream);

            Person p1 = new Person("张三", 45, "男");
            Person p2 = new Person("小红", 16, "女");

            objectOutputStream.writeObject(p1);
            objectOutputStream.writeObject(p2);

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

一个一个对象序列化太麻烦,可以使用对象数组批量进行对象的序列化

    @Test
    public void exer2(){
        FileInputStream fileInputStream = null;
        BufferedInputStream bufferedInputStream = null;
        ObjectInputStream objectInputStream = null;

        try {
            fileInputStream = new FileInputStream("序列化对象练习");
            bufferedInputStream = new BufferedInputStream(fileInputStream);
            objectInputStream = new ObjectInputStream(bufferedInputStream);
            /*
            System.out.println(objectInputStream.readObject());
            System.out.println(objectInputStream.readObject());
             */
            Person[] arr = (Person[]) objectInputStream.readObject();
            for(int i = 0; i < arr.length; i++){
                System.out.println(arr[i]);
            }

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

    @Test
    public void exer1(){
        FileOutputStream fileOutputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        ObjectOutputStream objectOutputStream = null;

        try {
            fileOutputStream = new FileOutputStream("序列化对象练习");
            bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
            objectOutputStream = new ObjectOutputStream(bufferedOutputStream);


            Person p1 = new Person("张三", 45, "男");
            Person p2 = new Person("小红", 16, "女");
            /*
            objectOutputStream.writeObject(p1);
            objectOutputStream.writeObject(p2);
            */
            Person[] arr = {p1,p2};
            objectOutputStream.writeObject(arr);

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

数组没有集合方便,使用集合序列化对象

     @Test
    public void exer2(){
        FileInputStream fileInputStream = null;
        BufferedInputStream bufferedInputStream = null;
        ObjectInputStream objectInputStream = null;

        try {
            fileInputStream = new FileInputStream("序列化对象练习");
            bufferedInputStream = new BufferedInputStream(fileInputStream);
            objectInputStream = new ObjectInputStream(bufferedInputStream);
            /*
            System.out.println(objectInputStream.readObject());
            System.out.println(objectInputStream.readObject());

            Person[] arr = (Person[]) objectInputStream.readObject();
            for(int i = 0; i < arr.length; i++){
                System.out.println(arr[i]);
            }*/
            List<Person> list= (List<Person>)objectInputStream.readObject();
            Iterator<Person> iterator = list.iterator();
            while(iterator.hasNext()){
                System.out.println(iterator.next());
            }

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

    @Test
    public void exer1(){
        FileOutputStream fileOutputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        ObjectOutputStream objectOutputStream = null;

        try {
            fileOutputStream = new FileOutputStream("序列化对象练习");
            bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
            objectOutputStream = new ObjectOutputStream(bufferedOutputStream);


            Person p1 = new Person("张三", 45, "男");
            Person p2 = new Person("小红", 16, "女");
            /*
            objectOutputStream.writeObject(p1);
            objectOutputStream.writeObject(p2);

            Person[] arr = {p1,p2};
            objectOutputStream.writeObject(arr);
*/
            List<Person> list = new ArrayList<>();
            list.add(p1);
            list.add(p2);
            objectOutputStream.writeObject(list);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(objectOutputStream != null){
                try {
                    objectOutputStream.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

6、转换流 InputStreamReader 和 OutputStreamReader

        在处理字符文件时,比较麻烦,因为在Unicode和GBK编码下,一个汉字所占的字节数不同,所以转换流的作用是将难处理的字节文本文件转换成字符流。

         FileReader只能处理和项目一致的编码的文本文件。

使用InputStreamReader读文件(标准做法)

    @Test
    public void test11(){
        //FileReader fileReader = null;     不好用
        FileInputStream fileInputStream = null;
        InputStreamReader inputStreamReader = null; //转换流
        BufferedReader bufferedReader = null;
        try {
            //fileReader = new FileReader("FileCopy.java");   //只能处理和项目一致的编码的文本文件
            fileInputStream = new FileInputStream("FileCopy.java");
            //inputStreamReader = new InputStreamReader(fileInputStream); //使用的是默认的编码方式
            inputStreamReader = new InputStreamReader(fileInputStream,"GBK");  //指明转换流在处理字节数据时按照某种编码方式处理字符串
            bufferedReader = new BufferedReader(inputStreamReader);
            String line = bufferedReader.readLine();
            int count = 1;
            System.out.print(count + " ");
            while(line != null){
                System.out.println(line);
                line = bufferedReader.readLine();
                System.out.print(++count + " ");
            }

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(bufferedReader != null){
                try {
                    //只需要关闭高级流
                    bufferedReader.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

使用OutputStreamReader写文件(标准做法)

    @Test
    public void test12(){
        FileOutputStream fileOutputStream = null;
        OutputStreamWriter outputStreamWriter =null;
        BufferedWriter bufferedWriter = null;
        try {
            fileOutputStream = new FileOutputStream("写一个文本文件");
            outputStreamWriter = new OutputStreamWriter(fileOutputStream,"utf8");   //写文件时,把字符串用某种编码方式写入
            bufferedWriter = new BufferedWriter(outputStreamWriter);
            bufferedWriter.write("abc我和你");

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

7、IO流体系总览