【Java从入门到大牛】IO流下篇

本文由 程序喵正在路上 原创,CSDN首发!
系列专栏:Java从入门到大牛
首发时间:2023年8月31日
欢迎关注点赞收藏留言
一以贯之的努力 不得懈怠的人生

目录

  • IO流-字符流
    • 文件字符输入流-读字符数据进来
    • 文件字符输出流-写字符数据出去
  • IO流-缓冲流
    • 字节缓冲流
    • 字符缓冲流
    • 原始流、缓冲流的性能分析 [重点]
  • IO流-转换流
    • 引出问题:不同编码读取时会乱码
    • 字符输入转换流
    • 字符输出转换流
  • IO流-打印流
  • IO流-数据流
  • IO流-序列化流
  • IO框架

IO流-字符流

【Java从入门到大牛】IO流下篇_第1张图片

文件字符输入流-读字符数据进来

FileReader(文件字符输入流)

作用:以内存为基准,可以把文件中的数据以字符的形式读入到内存中去
【Java从入门到大牛】IO流下篇_第2张图片

具体应用

测试文本文件

【Java从入门到大牛】IO流下篇_第3张图片

代码实现

import java.io.FileReader;
import java.io.Reader;

/**
 * 目标:掌握文件字符输入流
 */
public class FileReaderTest1 {
    public static void main(String[] args)  {
        try (
                // 1、创建一个文件字符输入流管道与源文件接通
                Reader fr = new FileReader("io-app2/src/test01.txt");
                ){
            // 2、读取文本文件的内容,每次读取一个字符,性能较差
//            int c; // 记住每次读取的字符编号
//            while ((c = fr.read()) != -1){
//                System.out.print((char) c);
//            }


            // 3、每次读取多个字符,性能还行
            char[] buffer = new char[3];
            int len; // 记住每次读取了多少个字符
            while ((len = fr.read(buffer)) != -1){
                System.out.print(new String(buffer, 0, len));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果

【Java从入门到大牛】IO流下篇_第4张图片

文件字符输出流-写字符数据出去

FileWriter(文件字符输出流)

作用:以内存为基准,把内存中的数据以字符的形式写出到文件中去

【Java从入门到大牛】IO流下篇_第5张图片

字符输出流使用时的注意事项

字符输出流写出数据后,必须刷新流,或者关闭流,写出去的数据才能生效,具体方法如下
【Java从入门到大牛】IO流下篇_第6张图片

字节流、字符流的使用场景小结:

  • 字节流适合做一切文件数据的拷贝(音视频或文本);字节流不适合读取中文内容输出
  • 字符流适合做文本文件的操作(读或写)

具体应用

import java.io.FileWriter;
import java.io.Writer;

/**
 * 目标:掌握文件字符输出流:写字符数据出去
 */
public class FileWriterTest2 {
    public static void main(String[] args) {
        try (
                // 创建一个文件字符输出流管道与目标文件接通
                // 覆盖管道
                // Writer fw = new FileWriter("io-app2/src/itheima02out.txt");

                // 追加管道
                Writer fw = new FileWriter("io-app2/src/test02out.txt", true);
                ){
            // 1、public void write(int c):写一个字符出去
            fw.write('a');
            fw.write(97);
            fw.write('好'); // 写一个字符出去
            fw.write("\r\n"); // 换行

            // 2、public void write(String c)写一个字符串出去
            fw.write("我爱你中国abc");
            fw.write("\r\n");

            // 3、public void write(String c ,int pos ,int len):写字符串的一部分出去
            fw.write("我爱你中国abc", 0, 5);
            fw.write("\r\n");

            // 4、public void write(char[] buffer):写一个字符数组出去
            char[] buffer = {'你', '好', '1', '2', '3'};
            fw.write(buffer);
            fw.write("\r\n");

            // 5、public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
            fw.write(buffer, 0, 2);
            fw.write("\r\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

【Java从入门到大牛】IO流下篇_第7张图片

IO流-缓冲流

【Java从入门到大牛】IO流下篇_第8张图片

对原始流进行包装,以提高原始流读写数据的性能

字节缓冲流

字节缓冲流的作用

提高字节流读写数据的性能

字节缓冲流的原理

字节缓冲输入流自带了 8KB 缓冲池;字节缓冲输出流也自带了 8KB 缓冲池

【Java从入门到大牛】IO流下篇_第9张图片

具体应用

import java.io.*;
/**
 * 目标:掌握字节缓冲流的作用
 */
public class BufferedInputStreamTest1 {
    public static void main(String[] args) {
        try (
                InputStream is = new FileInputStream("io-app2/src/test01.txt");
                // 1、定义一个字节缓冲输入流包装原始的字节输入流
                InputStream bis = new BufferedInputStream(is);

                OutputStream os = new FileOutputStream("io-app2/src/test01copy.txt");
                // 2、定义一个字节缓冲输出流包装原始的字节输出流
                OutputStream bos = new BufferedOutputStream(os);
        ){
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1){
                bos.write(buffer, 0, len);
            }
            System.out.println("复制完成!!");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

【Java从入门到大牛】IO流下篇_第10张图片

字符缓冲流

BufferedReader(字符缓冲输入流)

作用:自带 8K(8192)的字符缓冲池,可以提高字符输入流读取字符数据的性能
【Java从入门到大牛】IO流下篇_第11张图片

字符缓冲输入流新增的功能:按照行读取字符
【Java从入门到大牛】IO流下篇_第12张图片

字符缓冲输入流具体应用

import java.io.*;

/**
 * 目标:掌握字符缓冲输入流的用法
 */
public class BufferedReaderTest2 {
    public static void main(String[] args)  {
        try (
                Reader fr = new FileReader("io-app2\\src\\test02.txt");
                // 创建一个字符缓冲输入流包装原始的字符输入流
                BufferedReader br = new BufferedReader(fr);
        ){
            String line; // 记住每次读取的一行数据
            while ((line = br.readLine()) != null){
                System.out.println(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

【Java从入门到大牛】IO流下篇_第13张图片

BufferedWriter(字符缓冲输出流)

作用:自带 8K(8192)的字符缓冲池,可以提高字符输出流写字符数据的性能
【Java从入门到大牛】IO流下篇_第14张图片

字符缓冲输出流新增的功能:换行
【Java从入门到大牛】IO流下篇_第15张图片

字符缓冲输出流具体应用

import java.io.*;

/**
 * 目标:掌握字符缓冲输出流的用法
 */
public class BufferedWriterTest3 {
    public static void main(String[] args) {
        try (
                Writer fw = new FileWriter("io-app2/src/test03out.txt", true);
                // 创建一个字符缓冲输出流管道包装原始的字符输出流
                BufferedWriter bw = new BufferedWriter(fw);
        ){
            bw.write('a');
            bw.write(97);
            bw.write('好');
            bw.newLine();

            bw.write("我爱你中国abc");
            bw.newLine();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

【Java从入门到大牛】IO流下篇_第16张图片

原始流、缓冲流的性能分析 [重点]

测试用例:

分别使用原始的字节流以及字节缓冲流复制一个很大的视频

测试步骤:

  1. 使用低级的字节流按照一个一个字节的形式复制文件
  2. 使用低级的字节流按照字节数组的形式复制文件
  3. 使用高级的缓冲字节流按照一个一个字节的形式复制文件
  4. 使用高级的缓冲字节流按照字节数组的形式复制文件

代码实现

import java.io.*;

/**
    目标:观察原始流和缓冲流的性能
 */
public class TimeTest4 {
    // 复制的视频路径
    private static final String SRC_FILE = "D:\\resource\\test.mp4";
    // 复制到哪个目的地
    private static final String DEST_FILE = "D:\\";

    public static void main(String[] args) {
        copy01();   // 低级字节流一个一个字节的赋值,慢的简直让人无法忍受,直接淘汰!
        copy02();   // 低级的字节流流按照一个一个字节数组的形式复制,速度较慢!
        copy03();   // 缓冲流按照一个一个字节的形式复制,速度较慢,直接淘汰!
        copy04();   // 缓冲流按照一个一个字节数组的形式复制,速度极快,推荐使用!
    }

    private static void copy01() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream(SRC_FILE);
                OutputStream os = new FileOutputStream(DEST_FILE + "1.mp4");
        ){
            int b;
            while ((b = is.read()) != -1){
                os.write(b);
            }

        }catch (Exception e){
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("低级字节流一个一个字节复制耗时:" + (endTime - startTime) / 1000.0 + "s");
    }

    private static void copy02() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream(SRC_FILE);
                OutputStream os = new FileOutputStream(DEST_FILE + "2.mp4");
        ){
            byte[] buffer = new byte[1024*64];
            int len;
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0, len);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("低级字节流使用字节数组复制耗时:" + (endTime - startTime) / 1000.0 + "s");
    }

    private static void copy03() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream(SRC_FILE);
                BufferedInputStream bis = new BufferedInputStream(is);
                OutputStream os = new FileOutputStream(DEST_FILE + "3.mp4");
                BufferedOutputStream bos = new BufferedOutputStream(os);
        ){

            int b;
            while ((b = bis.read()) != -1){
                bos.write(b);
            }

        }catch (Exception e){
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("缓冲流一个一个字节复制耗时:" + (endTime - startTime) / 1000.0 + "s");
    }

    private static void copy04() {
        long startTime = System.currentTimeMillis();
        try (
                InputStream is = new FileInputStream(SRC_FILE);
                BufferedInputStream bis = new BufferedInputStream(is, 64 * 1024);
                OutputStream os = new FileOutputStream(DEST_FILE + "4.mp4");
                BufferedOutputStream bos = new BufferedOutputStream(os, 64 * 1024);
        ){
            byte[] buffer = new byte[1024 * 64]; // 32KB
            int len;
            while ((len = bis.read(buffer)) != -1){
                bos.write(buffer, 0, len);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("缓冲流使用字节数组复制耗时:" + (endTime - startTime) / 1000.0 + "s");
    }
}

执行结果

【Java从入门到大牛】IO流下篇_第17张图片

结论

建议使用字节缓冲输入流和字节缓冲输出流,结合字节数组的方式,目前看来是性能最优的组合

IO流-转换流

引出问题:不同编码读取时会乱码

  • 如果代码编码和被读取的文本文件的编码是一致的,使用字符流读取文本文件时不会出现乱码
  • 如果代码编码和被读取的文本文件的编码是不一致的,使用字符流读取文本文件就会出现乱码

字符输入转换流

【Java从入门到大牛】IO流下篇_第18张图片

InputStreamReader(字符输入转换流)

  • 解决不同编码时,字符流读取文本内容乱码的问题
  • 解决思路:先获取文件的原始字节流,再将其按真实的字符集编码转成字符输入流,这样字符输入流中的字符就不会乱码了

【Java从入门到大牛】IO流下篇_第19张图片

具体应用

import java.io.*;

/**
 * 目标:掌握字符输入转换流的作用
 */
public class InputStreamReaderTest2 {
    public static void main(String[] args) {
        try (
                // 1、得到文件的原始字节流(GBK的字节流形式)
                InputStream is = new FileInputStream("io-app2/src/inputStreamReaderTest.txt");
                // 2、把原始的字节输入流按照指定的字符集编码转换成字符输入流
                Reader isr = new InputStreamReader(is, "GBK");
                // 3、把字符输入流包装成缓冲字符输入流
                BufferedReader br = new BufferedReader(isr);
                ){
            String line;
            while ((line = br.readLine()) != null){
                System.out.println(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

字符输出转换流

需要控制写出去的字符使用什么字符集编码,该咋整 ?

  1. 调用 String 提供的 getBytes 方法解决

    String data = "我爱你中国abc";
    byte[] bytes = data.getBytes("GBK");
    
  2. 使用 “字符输出转换流” 实现

OutputStreamWriter字符输出转换流

  • 作用:可以控制写出去的字符使用什么字符集编码
  • 解决思路:获取字节输出流,再按照指定的字符集编码将其转换成字符输出流,以后写出去的字符就会用该字符集编码了

【Java从入门到大牛】IO流下篇_第20张图片

具体应用

import java.io.*;

/**
 * 目标:掌握字符输出转换流的使用
 */
public class OutputStreamWriterTest3 {
    public static void main(String[] args) {
        // 指定写出去的字符编
        try (
                // 1、创建一个文件字节输出流
                OutputStream os = new FileOutputStream("io-app2/src/outputStreamWriterTest.txt");
                // 2、把原始的字节输出流,按照指定的字符集编码转换成字符输出转换流
                Writer osw = new OutputStreamWriter(os, "GBK");
                // 3、把字符输出流包装成缓冲字符输出流
                BufferedWriter bw = new BufferedWriter(osw);
                ){
            bw.write("我是中国人abc");
            bw.write("我爱你中国123");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

IO流-打印流

【Java从入门到大牛】IO流下篇_第21张图片

PrintStream/PrintWriter(打印流)

作用:打印流可以实现更方便、更高效地打印数据出去,能实现打印啥出去就是啥出去

PrintStream提供的打印数据的方案

【Java从入门到大牛】IO流下篇_第22张图片

PrintStream和PrintWriter的区别

  • 打印数据的功能上是一模一样的,都是使用方便、性能高效
  • PrintStream 继承自字节输出流 OutputStream,因此支持写字节数据的方法
  • PrintWriter 继承自字符输出流 Writer,因此支持写字符数据出去

具体应用

import java.io.*;

/**
 * 目标:掌握打印流:PrintStream/PrintWriter的用法
 */
public class PrintTest1 {
    public static void main(String[] args) {
        try (
                // 创建一个打印流管道
//                PrintStream ps =
//                        new PrintStream("io-app2/src/testPrint.txt", Charset.forName("GBK"));
//                PrintStream ps =
//                        new PrintStream("io-app2/src/testPrint.txt");
                PrintWriter ps =
                        new PrintWriter(new FileOutputStream("io-app2/src/testPrint.txt", true));
                ){
                ps.println(97);
                ps.println('a');
                ps.println("我爱你中国abc");
                ps.println(true);
                ps.println(99.5);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

打印流的一种应用:输出语句的重定向

可以把输出语句的打印位置改到某个文件中去

PrintStream ps = new PrintStream("文件地址");
System.setOut(ps);
import java.io.PrintStream;

/**
 * 目标:了解下输出语句的重定向
 */
public class PrintTest2 {
    public static void main(String[] args) {
        try ( PrintStream ps = new PrintStream("io-app2/src/testPrint.txt"); ){

            // 把系统默认的打印流对象改成自己设置的打印流
            System.setOut(ps);

            System.out.println("烈士暮年");
            System.out.println("壮心不已");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

IO流-数据流

【Java从入门到大牛】IO流下篇_第23张图片

DataOutputStream(数据输出流)

允许把数据和其类型一并写出去

【Java从入门到大牛】IO流下篇_第24张图片

import java.io.*;

/**
 * 目标:数据输出流
 */
public class DataOutputStreamTest1 {
    public static void main(String[] args) {
        try (
                // 1、创建一个数据输出流包装低级的字节输出流
                DataOutputStream dos =
                        new DataOutputStream(new FileOutputStream("io-app2/src/testDataOutputStream.txt"));
                ){
            dos.writeInt(97);
            dos.writeDouble(99.5);
            dos.writeBoolean(true);
            dos.writeUTF("一二三456");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

DataInputStream(数据输入流)

用于读取数据输出流写出去的数据

【Java从入门到大牛】IO流下篇_第25张图片

import java.io.*;

/**
 * 目标:使用数据输入流读取特定类型的数据
 */
public class DataInputStreamTest2 {
    public static void main(String[] args) {
        try (
                DataInputStream dis =
                        new DataInputStream(new FileInputStream("io-app2/src/testDataOutputStream.txt"));
                ){
            int i = dis.readInt();
            System.out.println(i);

            double d = dis.readDouble();
            System.out.println(d);

            boolean b = dis.readBoolean();
            System.out.println(b);

            String rs = dis.readUTF();
            System.out.println(rs);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

IO流-序列化流

【Java从入门到大牛】IO流下篇_第26张图片

ObjectOutputStream(对象字节输出流)

可以把 Java 对象进行序列化:把 Java 对象存入到文件中去

【Java从入门到大牛】IO流下篇_第27张图片

注意:对象如果要参与序列化,必须实现序列化接口(java.io.Serializable

import java.io.Serializable;

// 注意:对象如果需要序列化,必须实现序列化接口
public class User implements Serializable {
    private String loginName;
    private String userName;
    private int age;
    // transient 表示这个成员变量将不参与序列化
    private transient String passWord;

    public User() {
    }

    public User(String loginName, String userName, int age, String passWord) {
        this.loginName = loginName;
        this.userName = userName;
        this.age = age;
        this.passWord = passWord;
    }

    public String getLoginName() {
        return loginName;
    }

    public void setLoginName(String loginName) {
        this.loginName = loginName;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getAge() {
        return age;
    }

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

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

    @Override
    public String toString() {
        return "User{" +
                "loginName='" + loginName + '\'' +
                ", userName='" + userName + '\'' +
                ", age=" + age +
                ", passWord='" + passWord + '\'' +
                '}';
    }
}
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

/**
 * 目标:掌握对象字节输出流的使用:序列化对象
 */
public class Test1ObjectOutputStream {
    public static void main(String[] args) {
        try (
                // 2、创建一个对象字节输出流包装原始的字节 输出流
                ObjectOutputStream oos =
                        new ObjectOutputStream(new FileOutputStream("io-app2/src/testObjectOutput.txt"));
                ){
            // 1、创建一个Java对象
            User u = new User("admin", "张三", 32, "666888xyz");

            // 3、序列化对象到文件中去
            oos.writeObject(u);
            System.out.println("序列化对象成功!!");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

ObjectInputStream(对象字节输入流)

可以把 Java 对象进行反序列化:把存储在文件中的 Java 对象读入到内存中来

【Java从入门到大牛】IO流下篇_第28张图片

如果要一次序列化多个对象,该怎么办呢 ?

用一个 ArrayList 集合存储多个对象,然后直接对集合进行序列化即可

注意:ArrayList 集合已经实现了序列化接口

import java.io.FileInputStream;
import java.io.ObjectInputStream;

/**
 * 目标:掌握对象字节输入流的使用:反序列化对象
 */
public class Test2ObjectInputStream {
    public static void main(String[] args) {
        try (
                // 创建一个对象字节输入流管道,包装低级的字节输入流与源文件接通
                ObjectInputStream ois =
                        new ObjectInputStream(new FileInputStream("io-app2/src/testObjectOutput.txt"));
                ){
            User u = (User) ois.readObject();
            System.out.println(u);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

IO框架

什么是框架 ?

  • 解决某类问题,编写的一套类、接口等,可以理解成一个半成品,大多框架都是第三方研发的
  • 好处:在框架的基础上开发,可以得到优秀的软件架构,并能提高开发效率
  • 框架的形式:一般是把类、接口等编译成 class 形式,再压缩成一个 .jar 结尾的文件发行出去

什么是IO框架 ?

封装了 Java 提供的对文件、数据进行操作的代码,对外提供了更简单的方式来对文件进行操作,对数据进行读写等

Commons-io

比较常用的有 Commons-io 框架,Commons-io 是 apache 开源基金组织提供的一组有关 IO 操作的小框架,目的是提高 IO 流的开发效率

【Java从入门到大牛】IO流下篇_第29张图片

Commons-io的下载

由于 Commons-io 框架是第三方开发的,所有我们需要进行下载才能使用

下载很简单,进入其官网下载即可,点击此处进入官网,下载图中这一个即可,然后解压缩

【Java从入门到大牛】IO流下篇_第30张图片

在解压后的文件夹中找到图中这一个,这个压缩包就是我们开发时会使用到的

【Java从入门到大牛】IO流下篇_第31张图片
下一步我们需要将这个压缩包导入到要使用的项目中去,步骤如下

  1. 在项目中创建一个文件夹 lib,用来放置压缩包
  2. 找到压缩包复制一下,然后粘贴到 lib 文件夹下
  3. 在 lib 文件夹下右键点击压缩包,选择 Add as Library,再点击 OK 即可
  4. 在类中导包使用

如下图这样即为成功

【Java从入门到大牛】IO流下篇_第32张图片

Commons-io的具体应用

import org.apache.commons.io.FileUtils;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;

/**
 * 目标:使用CommonsIO框架进行IO相关的操作
 */
public class CommonsIOTest1 {
    public static void main(String[] args) throws Exception {
        FileUtils.copyFile(new File("io-app2\\src\\test01.txt"), new File("io-app2/src/a.txt"));
//        FileUtils.copyDirectory(new File("D:\\resource\\test1"), new File("D:\\resource\\test2"));
//        FileUtils.deleteDirectory(new File("D:\\resource\\test2"));

        // Java官方提供的原生的一行代码搞定很多事情
//         Files.copy(Path.of("io-app2\\src\\test01.txt"), Path.of("io-app2\\src\\b.txt"));
//        System.out.println(Files.readString(Path.of("io-app2\\src\\test01.txt")));
    }
}

你可能感兴趣的:(Java从入门到大牛,java)