IO流
本文最后更新于 223天前,其中的信息可能已经有所发展或是发生改变。若对文章内容存疑,或是本文的转载部分侵犯了您的权益,请联系博主 3240572824@qq.com

存储与读取数据的解决方案。

I:input,O:output。用于读取数据,包括本地文件,网络文件等。

使用原则

  • 随用随创
  • 何时不用,何时关流
  • (关流,即释放资源,若不关流,被操作的文件会被Java一直占用,会出现如无法删除等状况)

分类

  • 按流的方向划分
    • 输入流
    • 输出流
  • 按操作文件的类型划分
    • 字节流 所有类型的文件(图片 文本 视频 音频等)
      • 使用场景:拷贝任意类型的文件
    • 字符流 纯文本文件(Windows自带记事本打开可看懂的文件) txt md xml lrc等
      • 使用场景:读取/写入纯文本文件中的数据,尤其是中文文本
  • 按流的角色不同划分
    • 节点流 又叫基本流,向磁盘或网络进行基本的读写操作
    • 处理流 又叫高级流,对一个已存在的流进行连接和封装,通过封装后的流来实现数据的读写功能
图片截自b站阿玮老师的Java课程视频,文章末尾已标明出处

如上所示,因为四者都是抽象类,因此我们在使用IO流时,都是创建他们的子类对象。

下面,我从基本流与高级流的角度展开IO流的常用子类。

基本流

字节流 ByteStream

FileInputStream

操作本地文件的字节输入流 可将本地文件中的数据读取进程序。

使用步骤
  1. 创建字节输入流对象
    • 细节:若文件不存在,直接报错 (而非输出流中的自行创建此文件)(因为创建一个无数据的文件没有意义)
  2. 读取数据
    • 细节1 一次读一个字节,读出的数据是字符在ASCII码上对应的数字 并向后移动指针
    • 细节2 若读不到数据 (读到了文件末尾) read方法返回-1
  3. 释放资源
FileInputStream fis=new FileInputStream("S:\\TestFolder\\ccc\\test.txt");
        int b1 = fis.read();
        System.out.println(b1);// 100 (d)
        System.out.println((char) b1);// d

        fis.close();

FileOutputStream

将程序的数据写入本地文件。

使用步骤
  1. 创建字节输出流对象
    • 细节1 参数是字符串表示的路径/File对象均可
    • 细节2 若文件不存在则创建一新的文件 但是要确保其父级路径存在
    • 细节3 若文件存在 先删除原先的文件数据 向其中写入的数据会覆盖原先的内容
  2. 写出数据
    • write方法的参数是整数 但写入本地文件中的是整数对应ASCII码的对应字符
  3. 释放资源
FileOutputStream fos=new FileOutputStream("S:\\TestFolder\\ccc\\test.txt");
fos.write(77);// M
fos.close();

字符流 CharStream

FileReader

其方法read()既可以空参一次读取一个字节,也可以带参一次读取多个字节,读取过后,方法底层还会解码并转为十进制且返回。

// 1 创建对象并关联本地文件
        FileReader fr=new FileReader("S:\\TestFolder\\ash.txt");

        // 2 读取数据 read()
        // 字符流底层也是字节流 默认每次读取一个字节
        // 若遇到中文一次读取多个,GBK一次读两个字节 UTF-8一次读三个字节
        int ch;
        while ((ch= fr.read())!=-1){
            System.out.print((char)ch);// ch是read方法将文件中的文本解码为二进制并转为十进制的数字 // 1-2-4-7-8-9
                                        
        }

        // 3 释放资源(关流)
        fr.close();

FileWriter

字节输出流FileOutputStream输出一个中文字符时,因为一次只能操作一个字节而汉字长于一个字节的缘故,会输出产生乱码
而字符输出流FileWriter会根据字符集编码方式进行编码,并将编码后的数据写入文件,从而避免上述情况

FileWriter fw=new FileWriter("S:\\TestFolder\\ash.txt",true); // true是打开续写开关
// 1 一次写一个字符
fw.write(25105);// "我"
// 2 一次写一个字符串
fw.write("灰烬、Alex");
// 3 一次写一个字符数组
        char[]chars={'a','B','我'};
        fw.write(chars);
// 我灰烬、AlexaB我
        fw.close();

字符集

计算机当中,任意数据都是以二进制形式来存储的,最小的存储单元是一个字节,而ASCII字符集中,一个英文占一个字节。

简体中文版Windows默认使用GBK字符集,GBK字符集完全兼容ASCII字符集

  • 一个英文占一个字节,二进制第一位是0
  • 一个中文占两个字节,二进制高位字节第一位是1

Unicode字符集的UTF-8编码格式

  • 一个英文占一个字节,二进制第一位是0,转成十进制是正数
  • 一个中文占三个字节,二进制第一位是1,第一个字节转成十进制是负数

因此,为了避免乱码的产生,不要用字节流读取文本文件,且在编码解码时要使用相同字符编码

// Java的编码/解码方法
        // 编码
        String str="灰烬、Alex";

        // 默认方式编码 UTF-8
        byte[]bytes1=str.getBytes();
        System.out.println(Arrays.toString(bytes1)); // [-25, -127, -80, -25, -125, -84, -29, -128, -127, 65, 108, 101, 120]

        // 指定方式编码
        byte[] bytes2 = str.getBytes("GBK");
        System.out.println(Arrays.toString(bytes2)); // [-69, -46, -67, -3, -95, -94, 65, 108, 101, 120]


        // 解码
        // 默认方式 UTF-8
        String str1=new String(bytes1);
        System.out.println(str1); // 灰烬、Alex
        String str2=new String(bytes2);
        System.out.println(str2); // �ҽ���Alex  由于编码与解码的方式不一致而导致的乱码

        // 指定方式解码
        String str3=new String(bytes2,"GBK");
        System.out.println(str3); // 灰烬、Alex

高级流

缓冲流

缓冲流自带长度为8192的缓冲区,所以一般适用于大量读写操作的场景。

缓冲流共四种:

  • 字节缓冲输入流 BufferedInputStream
  • 字节缓冲输出流 BufferedOutputStream
  • 字符缓冲输入流 BufferedReader
  • 字符缓冲输出流 BufferedWriter

其中,BufferedReader独有的方法readLine()与BufferedWriter独有的方法newLine()可以一次进行整行的读/写操作。

BufferedWriter bw=new BufferedWriter(new FileWriter("S:\\TestFolder\\ccc\\test2.txt"));
        bw.write("卧槽!魂批!");
        bw.newLine();// 多个平台统一的换行方式
        bw.write("魂批怎么你了?");
        bw.newLine();
// 卧槽!魂批!
// 魂批怎么你了?
        bw.close();

转换流

是字符流与字节流之间的桥梁,一般仅应用于字节流想要使用字符流中方法的情况。

  • 字符转换输入流 InputStreamReader
  • 字符转换输出流 OutputStreamWriter
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("S:\\TestFolder\\ccc\\test2.txt")));
        String line;
        while ((line=br.readLine())!=null) System.out.println(line);
// 卧槽!魂批!
// 魂批怎么你了?
        br.close();

序列化流

又叫对象操作输出流,可将Java对象写入本地文件,反之,将文件的对象读到程序中就叫反序列化流/对象操作输入流。

作用

public class Student implements Serializable {
    @Serial
    private static final long serialVersionUID = -7320567096138221572L;

    // Serializable 可序列化的 一旦实现这个接口 表示此类可以被序列化
    // Serializable 接口内无抽象方法,此般接口称作 标记型接口



    private String name;
    private int age;
    private transient String address;// transient 瞬间的 该关键字标记的成员变量不参与序列化过程


    public Student() {
    }

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

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return address
     */
    public String getAddress() {
        return address;
    }

    /**
     * 设置
     * @param address
     */
    public void setAddress(String address) {
        this.address = address;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + ", address = " + address + "}";
    }

Student s1=new Student("zhangsan",23,"London");

        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("S:\\TestFolder\\ccc\\test1.txt"));
        oos.writeObject(s1);

        oos.close();

ObjectInputStream ois=new ObjectInputStream(new FileInputStream("S:\\TestFolder\\ccc\\test1.txt"));
        Student o =(Student) ois.readObject();
        System.out.println(o);
// Student{name = zhangsan, age = 23, address = null}
        ois.close();

打印流

一般指字节打印流 PrintStream与字符打印流PrintWriter两个类。

系统标准输出流 System.out.println()

通过System.out即可获取系统打印流对象,此打印流在虚拟机启动时由虚拟机创建,默认指向控制台。

PrintStream out = System.out;
out.println("有点东西"); // 有点东西
out.println("啊对对对"); // 啊对对对

压缩流

将一个文件夹打包成一个压缩包(ZipOutputStream)的操作,反之,将压缩包解压即可使用解压缩流(ZipInputStream)。

其中,解压的本质就是将压缩包内每一个文件或文件夹读取出来,按层级拷贝在目的地中。

核心

  • 运用方法getNextEntry()获取压缩包里的每一个zipEntry对象
  • 每一个zipEntry对象即表示每一个压缩包中的获取到的文件或文件夹
// 1 创建数据源文件对象
        File src = new File("S:\\TestFolder\\ddd");
// 2 创建src的父级目录对象 表示压缩包将要存储的位置
        File parent = src.getParentFile();
        // 3 创建压缩包对象
        File dest = new File(parent, src.getName() + ".zip");
        // 4 创建压缩流
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest));

        // 5 获取src中每一个文件或文件夹 (ZipEntry)放入压缩包
        toZip(src,zos,src.getName());
        // 6 关流
        zos.close();
最后,可以在文件夹ddd的父目录也就是TestFolder中找到此压缩包

相关链接

作者:Gaspard
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0协议
若是幸得赏识、转载,还请注明文章地址及作者哦~
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇