Java——IO流(字符流,字节流)

JavaIO的整体框架图

img

IO流从方向上来说,可以分为输入流和输出流;
从传输内容上来说,可以分为字符流和字节流

防止记混的口诀

所谓的IO,说白了就是数据在内存和硬盘之间的传输

输入流 = %Reader = %InputStream,从硬盘写入内存;
输出流 = %Writer = %OutputStream,从内存写入硬盘;

站在内存的角度,输入流是读入,输出流是写出;
站在硬盘的角度,输入流是读出,输出流是写入

我们为了防止记忆错乱,我建议站在内存的角度记忆,因为In和Out能对应上入和出

流 = %Reader = %InputStream = 内存

流 = %Writer = %OutputStream =

字符流

文本,我们能读的懂的都可以认为是字符流。比如:文章,java文件等等

字符流的本质是字符数组char[]

字符输入流的超类:
Reader: 子类FileReader,BufferedReader
字符输出流的超类:
Writer:子类FileWriter,BufferedWriter

字符流可以分为字符输入流Reader和字符输出流Writer

文件字符输入流FileReader

import java.io.*;

public class ReaderTest {
    public static void main(String[] args) {
        File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\hello.txt");
        File file2 = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\charInput.txt");
        Reader reader = null;
        try {
            reader = new FileReader(file);
            // 1. 单个字符读取
            int c = reader.read();// 这样只能一个字符一个字符地读
            System.out.println((char)c);

            // 2. while循环读取
            while((c = reader.read()) != -1){
                System.out.print((char)c);
            }

            // 3. reader填充字符数组
            char[] cArr = new char[100];
            // 填充
            int len = reader.read(cArr);
            System.out.println("读取的长度:" + len + "    读取的内容:" + Arrays.toString(cArr));
            len = reader.read(cArr);
            System.out.println("第二次读取的长度:" + len + "    读取的内容:" + Arrays.toString(cArr));

            len = reader.read(cArr);
            System.out.println("第三次读取的长度:" + len + "    读取的内容:" + Arrays.toString(cArr));

            // 4. reader填充字符数组,并且拼接为字符串
            char[] cArr2 = new char[200];
            int len2 = -1;
            while ((len2 = reader.read(cArr2)) != -1) {
                String str = new String(cArr2, 0, len2 );
                System.out.println(str);
            }


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

文件字符输出流FileWriter

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;

public class WriterTest {
    public static void main(String[] args) {
        File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\hello.txt");
        Writer writer = null;
        try {
            // 1. 直接write会删掉之前全部的,然后放进去新的
             writer = new FileWriter(file);
           writer.write("Hello 111Writer!!!");
            // 2. 在源文件后拼接字符流
            writer = new FileWriter(file, true);
            writer.write("append text!!!");
            // 3. 循环分段拼接字符流
            writer = new FileWriter(file, true);
            for (int i = 0; i < 100; i++) {
                writer.write("HelloWorld\n");
                //每次写入10个helloworld的时候做一次flush
               if (i % 10 == 0) {
                    writer.flush();
                }
            }

            // 4. 字符数组(字符流)的直接输出(写入)
            File file2 = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\charInput.txt");
            writer = new FileWriter(file2);
            //定义一个数组
            char[] c = {'a', 'b', 'p', 'b', 'p'};
            writer.write(c);
            writer.write("\r\n");
            writer.write(c, 2, 2);
            writer.write("\r\n");
            writer.write(98);
            writer.write("\r\n");
            writer.write("我们今生注定是沧桑", 2, 6);


        } catch (IOException ioe) {
            ioe.printStackTrace();
        } finally {
            //判断writer不是空防止空指针异常
            if (writer != null) {
                try {
                    //关闭流
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }
}

注意:一般的writer会将原内容删除,再写新内容;如果想要在原有内容的基础上填充,就需要让append为true

Writer writer = new FileWriter(file, true);
flush方法是干什么的?

writer.flush()的作用在于将缓冲区的所有字符都发送给硬盘

只有Writer和OutputStream才有flush方法;但是不管读写都有自己的缓冲区,只不过读操作不需要我们手动清空缓冲区罢了

java在使用流时,都会有一个缓冲区,按一种它认为比较高效的方法来发数据:把要发的数据先放到缓冲区,缓冲区放满以后再一次性发过去,而不是分开一次一次地发.
而flush()表示强制将缓冲区中的数据发送出去,不必等到缓冲区满.

执行close()方法时,会强制执行一次flush()方法

但是如果忘了使用close(),也没有用flush()这个方法,很多情况下会出现流的另一边读不到数据的问题,特别是在数据特别小的情况下

我们测试用的数据都是很少的,FileWriter默认的缓冲区大小是8KB,显然到不了这个长度

为什么需要缓冲区(重点)

因为内存与硬盘的读写操作比较耗费时间,为了减少内存与磁盘的交互次数,降低时间消耗,我们使用缓冲区缓存一部分内容,只有缓冲区满了才会向硬盘发送写请求,这样做可以减少交互次数,降低耗时

使用FileReader和FileWriter完成文件拷贝
import java.io.*;

public class CopyFileTest {

    public static void main(String[] args) throws IOException {
        File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\hello.txt");

        Writer writer = new FileWriter("helloCopy.txt", true);
        Reader reader = new FileReader(file);

		// 清空之前的内容
        writer.write("");
        // 一字节一字节地读取
        char[] cArr = new char[1024];
        int len = -1;
        while ((len = reader.read(cArr)) != -1) {
            String str = new String(cArr, 0, len);
            // 读到的字符串写入文件中
            writer.write(cArr, 0, len);
            writer.flush();
        }

        if (reader != null) {
            reader.close();
        }
        if (writer != null) {
            writer.close();
        }
    }
}

缓冲字符输入流BufferedReader

public class BufferReaderTest {

    public static void main(String[] args) {
        File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\charInput.txt");
        BufferedReader bufferedReader = null;
        try {
            bufferedReader = new BufferedReader(new FileReader(file));
            String line = null;
            // BufferedReader是一行一行读取的
            while((line = bufferedReader.readLine()) != null){
                //打印一行
                System.out.println(line);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭外层的对象的时候,内存的资源会自动的被关闭
            if(bufferedReader != null){
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

BufferedReader读取数据是一行一行地读的

之前说过Reader和Writer都有自己的缓冲区,BufferedReader和FileWriter的缓冲区大小相同,区别在于BUfferedWriter只有缓冲区满了才会进行字符转码(将字符转化为字节)

缓冲字符输出流BufferedWriter

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class BufferWriterTest {

    public static void main(String[] args) {
        File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\charInput.txt");
        BufferedWriter bufferWriter = null;

        try {
            bufferWriter = new BufferedWriter(new FileWriter(file, true));
            bufferWriter.write("BufferedWriter写入的内容:11111");
            //换行
            bufferWriter.newLine();
            bufferWriter.write("BufferedWriter写入的内容:22222");
            bufferWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            //资源关闭
            if (bufferWriter != null) {
                try {
                    bufferWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
使用BufferedReader和BufferedWriter完成文件拷贝

原理同上,写法不同

import java.io.*;

public class BufferedCopyFile {

    public static void main(String[] args) {
        File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collection-test\\src\\FileTestPath\\charInput.txt");
        BufferedReader bufferedReader = null;
        BufferedWriter bufferWriter = null;

        try {
            bufferedReader = new BufferedReader(new FileReader(file));
            String line = null;
            // BufferedReader是一行一行读取的
            while ((line = bufferedReader.readLine()) != null) {
                bufferWriter = new BufferedWriter(new FileWriter("bufferedCopyFile.txt", true));
                bufferWriter.write(line);
                bufferWriter.flush();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭外层的对象的时候,内存的资源会自动的被关闭
            if(bufferedReader != null){
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //资源关闭
            if (bufferWriter != null) {
                try {
                    bufferWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

Buffered为什么高效?

在这里插入图片描述
在这里插入图片描述
所有的Writer字符流,都要先变成InputStreamReader/OutputStreamWriter,然后转化为InputStream/OutputStream变成字节数组,最终转化为二进制数据才能在计算机底层进行传输

所有的Reader字符流,过程相反
我们可以看到,字符流读写存在编解码的过程,而Buffered之所以高效,就是因为FileWriter每次调用write()方法,就会调用一次OutputStreamWriter中的write()方法,而BufferedWriter只有在缓冲区满了才会调用OutputStreamWriter中的write()方法。

打印字符输出流PrintWriter

PrintWriter和FileWriter之间的区别

区别在于 :

  1. PrintWriter设计之初就是为了写出,所以没有PrintReader
  2. PrintWriter 提供了一些额外的格式化方法,如 println ,print和 printf,而且可以将内容打印到控制台上
  3. 封装了字符输出流,还可以字符流和字节流的转换
  4. 可以打印各种数据类型
  5. PrintWriter 在传递流时所做的是以 缓冲(BufferedWriter) 方式打开它,FileWriter不是
  6. 与 PrintStream 类不同,如果启用了自动刷新,但只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作(PrintStream是任何写方法都可以自动刷新)
PrintWriter(Writer out, boolean autoFlush);// true开启,false关闭
public PrintWriter(File file) throws FileNotFoundException {
    this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))),
         false);
}

FileWriter没有缓冲

public FileWriter(File file) throws IOException {
    super(new FileOutputStream(file));
}
demo

打印控制台

        PrintWriter pw = null;
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader("c.txt"));
            pw = new PrintWriter("d.txt");
            String line = null;
            while((line = br.readLine()) != null){

                pw.println(line);
                pw.flush();
            }


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


        }

打印到文件中(其实就是向文件中写)

        PrintWriter pw = null;
        try {
            pw = new PrintWriter("c.txt");
            pw.println(1);
            pw.println(1.1);
            pw.println("liangge");
            pw.println('c');
            pw.println(false);
            pw.println(1.2f);
            pw.flush();


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(pw != null){
                pw.close();
            }


        }

字节流

二进制的数据,这种数据一般用文本打开我们读不懂。比如,图片文件,mp3文件。

字节输入流的超类:InputStream:
子类:FileInputStream
字节输出流的超类:
OutputStream:
子类FileOutputStream

字节流传输的是二进制的数据

字节流的本质是字节数组byte[]

字符流能传输的,字节流也可以传输;
字节流能传输的,字符流不一定能传输

文件字节输入流FIleInputStream

public static void main(String[] args) {

        //创建字符输入流的对象
        InputStream in = null;

        try {
            in = new FileInputStream("a.txt");
            int r = in.read();
            System.out.println((char)r);


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

        }


    }

文件字节输出流FIleOutputStream

public class IOByteDemo {

    public static void main(String[] args) {
        //创建字节输出流
        OutputStream out = null;

        try {
            out = new FileOutputStream(new File("a.txt"));
            //字节流不需要flush
            out.write(98);// Ascii码对应的是小写字母b
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(out != null){
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }
}

缓冲字节输入流BufferedInputStream

public static void main(String[] args) throws IOException {
        File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collectionAndIOStream-test\\helloCopy.txt");
        // 以下两种方式均可
        // BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(file.toPath()));
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));

        byte[] bytes = new byte[1024];
        int len = -1;
        while ((len = bis.read(bytes)) != -1) {
            String s = new String(bytes, 0, len);
            System.out.print(s);
        }

    }

比BufferedReader更快,因为不需要转化为InputStreamReader,但是仍然需要编码

缓冲字节输入流BufferedOutputStream

    public static void main(String[] args) throws IOException {
        File file = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collectionAndIOStream-test\\buf.txt");
        // 以下两种方式均可
        // BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(file.toPath()));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
        //创建一个字节的数组
        byte[] bs = {97, 99, 103, 111};
        bos.write(bs, 1, 2);
    }

比BufferedWriter更快,因为不需要转化为OutputStreamWriter,但是仍然需要解码

BufferedStream拷贝文件
public static void main(String[] args) throws IOException {
        File fileRead = new File("D:\\2_WorkSpace\\qcbyProject\\shixun\\collectionAndIOStream-test\\helloCopy.txt");
        File fileWrite = new File("bisAndBos.txt");
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileRead));

        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileWrite));

        int count = 0;
        while (count == 0) {
            count = bis.available();
        }
        int fileSize = bis.available();
        long written = 0;
        int beteSize = 1024;
        byte[] bytes = new byte[beteSize];
        while (written < fileSize) {
            if (written + beteSize > fileSize) {
                beteSize = (int) (fileSize - written);
                bytes = new byte[beteSize];
            }
            bis.read(bytes);
            bos.write(bytes);
            bos.flush();
            written += beteSize;
        }

    }

对象字节输出流ObjectOutputStream(序列化流)

对象转化为二进制数据,被称为序列化;
将二进制数据转化为对象,被称为反序列化

我们假设有这样一个Person

必须实现Serializable接口!!!

public class Person implements Serializable{
    
    //唯一的序列化接口的id值

    private String name;

    private int age;

    private Date birth;
    private static final long serialVersionUID = 3587752576949978144L;
    private String addr;



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

    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 Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

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

对象流进行序列化

public static void main(String[] args) {

        Person p = new Person("宇智波止水", 16, new Date());

        ObjectOutputStream out = null;

        try {
            out = new ObjectOutputStream(new FileOutputStream("person-data.txt"));
            out.writeObject(p);
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(out != null){
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }


    }

txt内容
在这里插入图片描述

对象字节输入流ObjectInputStream(反序列化流)

把我们刚才序列化的字节序列反序列化为java对象

public static void main(String[] args) {

        

        ObjectInputStream in = null;

        try {
            in = new ObjectInputStream(new FileInputStream("person-data.txt"));
            //读取一个对象, 叫反序列化
            Object o = in.readObject();
            System.out.println(o);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }


    }

在这里插入图片描述

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值