Java 基础进阶篇(十五):IO 流总结(全网最全面)

文章目录

  • 前置内容:字符集
  • 一、IO 流概述
  • 二、字节流
    • 2.1 文件字节输入流 FileInputStream
      • 2.1.1 案例:每次读取一个字节
      • 2.1.2 案例:每次读取一个字节数组
      • 2.1.3 案例:读取文件的全部字节
    • 2.2 文件字节输出流 FileOutputStream
    • 2.3 文件拷贝
    • 2.4 资源释放的方式
      • 2.4.1 try-catch-finally
      • 2.4.2 try-with-resource
  • 三、字符流
    • 3.1 文件字符输入流 FileReader
    • 3.2 文件字符输出流 FileWriter
  • 四、缓冲流
    • 4.1 字节缓冲流
      • 4.1.1 字节缓冲输入流 BufferedInputStream
      • 4.1.2 字节缓冲输出流 BufferedOutputStream
    • 4.2 字符缓冲流
      • 4.2.1 字符缓冲输入流 BufferedReader
      • 4.2.2 字符缓冲输出流 BufferedWritter
  • 五、转换流
    • 5.1 字符输入转换流 InputStreamReader
    • 5.2 字符输出转换流 OutputStreamWrite
  • 六、序列化流
    • 6.1 对象序列化与反序列化
    • 6.2 对象字节输出流 ObjectOutputStream
    • 6.3 对象字节输入流 ObjectInputStream
  • 七、打印流
    • 7.1 PrintStream
    • 7.2 PrintWriter
    • 7.3 PrintStream 和 PrintWriter 的区别
  • 补充:Commons-IO 框架


前置内容:字符集

字符集(Character Set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同。

常见的字符集有:ASCII、GBK、Unicode(UTF-8)字符集等。

  • ASCII 字符集:包括了数字、英文、符号。
    ASCII 字符集中一个字符存储 1 个字节,一个字节是 8 位,总共可以表示 128 个字符信息,对于表示英文、数字来说是够用的。

  • GBK 字符集:是中国的码表,包含了几万个汉字等字符
    GBK 字符中一个中文字符一般以 2 个字节的形式存储,同时兼容 ASCII 编码表

  • Unicode 字符集 :又叫统一码、万国码,其容纳了世界上大多数国家的常见文字和字符,是计算机科学领域里的一项业界标准。
    UTF-8 是 Unicode 字符集的一种常见编码方式,用于将数字转换成二进制。
    UTF-8 编码后一个中文一般以 3 个字节的形式存储,同时兼容 ASCII 编码表

中文存储和展示流程解析:

Java 基础进阶篇(十五):IO 流总结(全网最全面)_第1张图片

注意:

① 英文和数字不会乱码;
② 英文和数字等在任何国家的字符集中都占 1 个字节;
③ 字符解码时使用的字符集和编码时使用的字符集必须一致,否则会出现中文乱码;


字符集的编码、解码操作:

String 编码:

Java 基础进阶篇(十五):IO 流总结(全网最全面)_第2张图片

String 解码:

在这里插入图片描述

举例:

public class Test {
    public static void main(String[] args) throws Exception {
        // 1、编码:把文字转换成字节(使用指定的编码)
        String name = "abc我爱你中国";
        byte[] bytes = name.getBytes(); // 以当前代码默认字符集进行编码 (UTF-8)
        //byte[] bytes = name.getBytes("GBK"); // 指定编码
        System.out.println(bytes.length); // 18 abc占三个字节,5个汉字各占三个字节,一共 18
        System.out.println(Arrays.toString(bytes)); 
        // [97, 98, 99, -26, -120, -111, -25, -120, -79, -28, -67, -96, -28, -72, -83, -27, -101, -67]

        // 2、解码:把字节转换成对应的中文形式(编码前 和 编码后的字符集必须一致,否则乱码 )
        String rs = new String(bytes); // 默认的UTF-8
        // String rs = new String(bytes, "GBK"); // 指定GBK解码
        System.out.println(rs); // abc我爱你中国
    }
}

一、IO 流概述

I 表示 intput,负责读,把硬盘文件中的数据读入到内存的过程,称之输入。
O 表示 output,负责写,把内存中的数据写出到硬盘文件的过程,称之输出。

IO流的分类:按照流的方向分为输入流与输出流,若按照流中数据的最小单位可分为字节流与字符流。

Java 基础进阶篇(十五):IO 流总结(全网最全面)_第3张图片
IO 流的四大类:

字节输入流: 以内存为基准,来自磁盘文件/网络中的数据 以字节的形式读入到内存中去
字节输出流: 以内存为基准,把内存中的数据 以字节写出到磁盘文件或者网络中去
字符输入流: 以内存为基准,来自磁盘文件/网络中的数据 以字符的形式读入到内存中去
字符输出流: 以内存为基准,把内存中的数据 以字符写出到磁盘文件或者网络介质中去

IO 流的体系如下:

Java 基础进阶篇(十五):IO 流总结(全网最全面)_第4张图片


二、字节流

2.1 文件字节输入流 FileInputStream

以内存为基准,来自磁盘文件/网络中的数据 以字节的形式读入到内存 中去。

Java 基础进阶篇(十五):IO 流总结(全网最全面)_第5张图片

构造方法:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第6张图片

常用方法:

在这里插入图片描述


2.1.1 案例:每次读取一个字节

示例:

public class FileInputStreamDemo01 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输入流管道与源文件接通。
        // InputStream is = new FileInputStream(new File("day09-oop/src/data.txt"));// ab3
        // 简化写法
        InputStream is = new FileInputStream("day09-oop/src/data.txt");

        // 2、读取一个字节返回 (每次读取一滴水)
		//int b1 = is.read();
        System.out.println((char)is.read()); // a

        int b2 = is.read();
        System.out.println((char)b2); // b

        int b3 = is.read();
        System.out.println((char)b3); // 3

        int b4 = is.read(); // 读取完毕返回-1
        System.out.println(b4); // -1
    }
}

上文案例的循环改进版本:

public class FileInputStreamDemo01 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输入流管道与源文件接通。
        InputStream is = new FileInputStream("day09-oop/src/data.txt");// ab3

        // 2、使用循环改进
        int b;
        while (( b = is.read() ) != -1){
            System.out.print((char) b);
        }
    }
}

问题分析:由于中文字符占3个字节,每次读取一个字符会出现乱码问题。


2.1.2 案例:每次读取一个字节数组

示例2: 每次读取一个字节数组(可以理解为一桶水)

public class FileInputStreamDemo03 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输入流管道与源文件接通
        InputStream is = new FileInputStream("day09-oop/src/data02.txt"); // abcdefgh

        // 2、定义一个字节数组,用于读取字节数组
        byte[] buffer = new byte[3]; // 3B
        int len1 = is.read(buffer);
        System.out.println("读取了几个字节:" + len1); // 3
        String rs1 = new String(buffer);
        System.out.println(rs1); // abc

        int len2 = is.read(buffer);
        System.out.println("读取了几个字节:" + len2); // 3
        String rs2 = new String(buffer);
        System.out.println(rs2); // def

        int len3 = is.read(buffer);
        System.out.println("读取了几个字节:" + len3); // 2
        // 读取多少倒出多少
        String rs3 = new String(buffer,0 ,len3);// gh
        String rs4 = new String(buffer); // ghf
        System.out.println(rs3);

        int len4 = is.read(buffer);
        System.out.println(len4); // 读取完毕返回-1
	}
}

问题分析1:为什么上文案例当读到 “第三桶水” 的时候,桶里面的水不是 gh,而是 ghf

int len3 = is.read(buffer);
System.out.println("读取了几个字节:" + len3); // 2
// 读取多少倒出多少
String rs3 = new String(buffer,0 ,len2);// gh
String rs4 = new String(buffer); // ghf

原因在于:当读完 “第二桶水” 的时候,字节数组 buffer 中装有的水是 def,当继续从读取的时候,由于只有前两滴水可被读到桶中,因此最终桶里的水只有 ghf

Java 基础进阶篇(十五):IO 流总结(全网最全面)_第7张图片
上文案例的循环改进版本:

public class FileInputStreamDemo04 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输入流管道与源文件接通
        InputStream is = new FileInputStream("day09-oop/src/data02.txt"); // abcdefgh
		
		// 2、循环改进
		byte[] buffer = new byte[3];
        int len; // 记录每次读取的字节数。
        while ((len = is.read(buffer)) != -1) {
            // 读取多少倒出多少
            System.out.print(new String(buffer, 0 , len));
        }
	}
}

问题分析2:读取中文字符输出无法避免乱码问题。

例:读取内容为 ab巴s啦啦c 时,每次读取三个字节,会出现乱码问题。


2.1.3 案例:读取文件的全部字节

方式一: 定义一个字节数组与文件的大小一样大,然后使用读取字节数组的方法,一次性读取完成。

方式二: 字节输入流 InputStream 提供了如下 API 可以直接把文件的全部数据读取到一个字节数组中。

在这里插入图片描述

public class FileInputStreamDemo05 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输入流管道与源文件接通
        File f = new File("day09-oop/src/data3.txt");
        InputStream is = new FileInputStream("day09-oop/src/data3.txt");

        // 2、定义一个字节数组与文件的大小刚刚一样大。
        // byte[] buffer = new byte[(int) f.length()];
        // int len = is.read(buffer);
        // System.out.println("读取了多少个字节:" + len);
        // System.out.println("文件大小:" + f.length());
        // System.out.println(new String(buffer));

        // 读取全部字节数组
        byte[] buffer = is.readAllBytes();
        System.out.println(new String(buffer));
    }
}

问题分析:不会有中文乱码,但如果文件过大,字节数组可能引起内存溢出


2.2 文件字节输出流 FileOutputStream

以内存为基准,把内存中的数据 以字节写出到磁盘文件或者网络中去

Java 基础进阶篇(十五):IO 流总结(全网最全面)_第8张图片

构造方法:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第9张图片

常用方法:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第10张图片

流的刷新与关闭:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第11张图片

示例:

public class OutputStreamDemo01 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输出流管道与目标文件接通
        // OutputStream os = new FileOutputStream("day09-oop/src/out04.txt" , true); // 追加数据管道
        OutputStream os = new FileOutputStream("day09-oop/src/out04.txt"); // 先清空之前的数据,写新数据进入

        // 2、写数据出去
        // a.public void write(int a):写一个字节出去
        os.write('a'); // 自动转换成整型
        os.write(98);
        os.write("\r\n".getBytes()); // 添加换行,win和其他系统都支持换行
        // os.write('徐'); // [ooo]

        // b.public void write(byte[] buffer):写一个字节数组出去。
        byte[] buffer = {'a' , 97, 98, 99};
        os.write(buffer);
        os.write("\r\n".getBytes()); // 添加换行

        byte[] buffer2 = "我是中国人".getBytes();
        // byte[] buffer2 = "我是中国人".getBytes("GBK");
        os.write(buffer2);
        os.write("\r\n".getBytes());

        // c. public void write(byte[] buffer , int pos , int len):写一个字节数组的一部分出去。
        byte[] buffer3 = {'a',97, 98, 99};
        os.write(buffer3, 0 , 3);// 左闭右开,写前三个字符
        os.write("\r\n".getBytes());

        // os.flush(); // 写数据必须,刷新数据 可以继续使用流
        os.close(); // 释放资源,包含了刷新的!关闭后流不可以使用流对象
    }
}

2.3 文件拷贝

Java 基础进阶篇(十五):IO 流总结(全网最全面)_第12张图片

思路:

① 根据数据源创建字节输入流对象。
② 根据目的地创建字节输出流对象。
③ 定义一个字节数组进行数据的转移。
④ 释放资源。

实现:

public class CopyDemo {
    public static void main(String[] args) {
        try {
            // 1、创建一个字节输入流管道与原视频接通
            InputStream is = new FileInputStream("day09-oop/src/data.txt");
            // 2、创建一个字节输出流管道与目标文件接通
            OutputStream os = new FileOutputStream("day09-oop/src/copy.txt");
            
            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            
            // 4、关闭流。
            os.close();
            is.close();
        } catch (Exception e){
            e.printStackTrace();
        }
   }
}

任何文件的底层都是字节,只要前后文件格式、编码一致没有任何问题,字节流可以完成一切文件数据的拷贝。

总结:字节流适合做文件数据的拷贝,但不适合做中文数据的输出


2.4 资源释放的方式

2.4.1 try-catch-finally

finally: 放在 try-catch 后面的,无论是正常执行还是异常执行代码,最后一定要执行,除非 JVM 退出。

作用:一般用于进行最后的资源释放操作(专业做法)

Java 基础进阶篇(十五):IO 流总结(全网最全面)_第13张图片


2.4.2 try-with-resource

上文的资源虽然可行,但是释放资源的代码过于繁琐,如下文:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第14张图片
finally{...} 中判断是否为空,是因为有可能在得到流对象之前出现了异常的情况;
finally{...} 中再次 try catch 是因为系统认为可能在上文 try 未结束时已经关闭了流。

JDK 7 和 JDK 9 中都简化了资源释放操作:

Java 基础进阶篇(十五):IO 流总结(全网最全面)_第15张图片
JDK7 以及 JDK9 的改进方案中只能放置资源对象,否则报错。(注:资源都是实现了Closeable/AutoCloseable 接口的类对象)

如:

public abstract class InputStream implements Closeable {...}
public abstract class OutputStream implements Closeable, Flushable{...} 

示例1: JDK7 做法

public class TryCatchResourceDemo2 {
    public static void main(String[] args) {
        try (
                // 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源
                // 即使出现异常也会做关闭操作
                // 1、创建一个字节输入流管道与原视频接通
                InputStream is = new FileInputStream("day09-oop/src/data.txt");
                // 2、创建一个字节输出流管道与目标文件接通
                OutputStream os = new FileOutputStream("day09-oop/src/copy02.txt");

                // int age = 23; //报错,这里只能放资源
                MyConnection connection = new MyConnection(); // 模拟资源对象,最终会自动调用资源的close方法
                ) {              
            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

class MyConnection implements AutoCloseable{
    @Override
    public void close() throws IOException {
        System.out.println("连接资源被成功释放了!");
    }
}

示例2: JDK9 做法

public class TryCatchResourceDemo3 {
    public static void main(String[] args) throws Exception {
        // 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
        // 1、创建一个字节输入流管道与原视频接通
        InputStream is = new FileInputStream("day09-oop/src/data.txt");
        // 2、创建一个字节输出流管道与目标文件接通
        OutputStream os = new FileOutputStream("day09-oop/src/copy03.txt");
        MyConn conn = new MyConn(); // 最终会自动调用资源的close方法

        try ( is; os; conn ) {
            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");

        } catch (Exception e){
            e.printStackTrace();
        }
    }
}
class MyConn implements AutoCloseable{
    @Override
    public void close() throws IOException {
        System.out.println("连接资源被成功释放了~~~");
    }
}

三、字符流

字节流适合做文件拷贝,但是读取中文会出现乱码或者内存溢出。

字符流则可以完全解决这个问题,其把中文和英文数字都当成一个字符来处理。

3.1 文件字符输入流 FileReader

以内存为基准,来自磁盘文件/网络中的数据 以字符的形式读入到内存中去

构造器:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第16张图片
常用方法:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第17张图片

示例1: 每次读取一个字符

public class FileReaderDemo01 {
    public static void main(String[] args) throws Exception {
        // 1. 创建一个字符输入流与源文件接通
        Reader fr = new FileReader("day09-oop/src/data3.txt");

        // 2. 读取一个字符,若没有可读字符返回-1
        /*int code = fr.read();
        System.out.println((char)code); // 单个中文一个字符

        int code1 = fr.read();
        System.out.println((char)code1);*/

        // 循环读取
        int code;
        while((code = fr.read()) != -1){
            System.out.print((char)code);
        }
    }
}

注:如果代码文件编码一致,读取中文字符不会出现乱码;读取性能较慢

示例2: 每次读取一个字符数组

public class FileReaderDemo02 {
    public static void main(String[] args) throws Exception {
        // 1. 创建一个字符输入流与源文件接通
        Reader fr = new FileReader("day09-oop/src/data3.txt");

        // 2. 用循环,每次读取一个字符数组的数据
        char[] buffer = new char[1024]; // 1K 字符
        int len;
        while((len = fr.read(buffer)) != -1){
            String rs = new String(buffer, 0, len); // 解码并输出
            System.out.print(rs);
        }
    }
}

注:读取的性能得到了提升;读取中文字符输出不会乱码


3.2 文件字符输出流 FileWriter

以内存为基准,把内存中的数据 以字符写出到磁盘文件或者网络介质中去

构造器:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第18张图片

常用方法:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第19张图片

流的关闭与刷新:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第20张图片

示例:

public class FileWriterDemo03 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个字符输出流管道与目标文件接通
        Writer fw = new FileWriter("day09-oop/src/out04.txt"); // 覆盖管道,每次启动都会清空文件之前的数据
        // Writer fw = new FileWriter("day09-oop/src/out05.txt", true); // 追加数据

        // a.public void write(int c):写一个字符出去
        fw.write('c');
        fw.write(98);
        fw.write('爱');

        // b.public void write(String c)写一个字符串出去
        fw.write("我是中国人");
        fw.write("\r\n");

        // c.public void write(char[] buffer):写一个字符数组出去
        char[] chars = "我是中国人".toCharArray();
        fw.write(chars);

        // d.public void write(String c ,int pos ,int len):写字符串的一部分出去
        fw.write("我是中国人", 0, 3);

        // e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
        fw.write("我是中国人".toCharArray(), 0, 3);

        // fw.flush(); // 刷新后流可以继续使用
        fw.close(); // 关闭包含刷线,关闭后流不能使用
    }
}

四、缓冲流

缓冲流也称为高效流、或者高级流。之前学习的字节流、字符流可以称为原始流。

作用:缓冲流自带 8KB 缓冲区、可以提高原始字节流、字符流读写数据的性能

4.1 字节缓冲流

字节缓冲流性能优化原理:
字节缓冲输入流自带了 8KB 缓冲池,以后我们直接从缓冲池读取数据,所以性能较好。
字节缓冲输出流自带了 8KB 缓冲池,数据就直接写入到缓冲池中去,写数据性能提高了。
在这里插入图片描述

4.1.1 字节缓冲输入流 BufferedInputStream

字节缓冲输入流:BufferedInputStream,提高字节输入流读取数据的性能。

4.1.2 字节缓冲输出流 BufferedOutputStream

字节缓冲输出流:BufferedOutputStream,提高字节输出流读取数据的性能。

构造方法:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第21张图片

示例:

public class ByteBufferDemo {
    public static void main(String[] args) {
        try (
                // 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
                // 1、创建一个字节输入流管道与原视频接通
                InputStream is = new FileInputStream("day10-oop/src/csb.txt");
                // a.把原始的字节输入流包装成高级的缓冲字节输入流
                InputStream bis = new BufferedInputStream(is); // 多态
                // 2、创建一个字节输出流管道与目标文件接通
                OutputStream os = new FileOutputStream("day10-oop/src/copy01.txt");
                // b.把字节输出流管道包装成高级的缓冲字节输出流管道
                OutputStream bos = new BufferedOutputStream(os);
        ) {
            // 3、定义一个字节数组转移数据
            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();
        }
    }
}

4.2 字符缓冲流

4.2.1 字符缓冲输入流 BufferedReader

字符缓冲输入流:BufferedReader,可提高字符输入流读取数据的性能,除此之外多了按照行读取数据的功能

构造器:
在这里插入图片描述
独有方法:
在这里插入图片描述

示例1:

public class BufferedReaderDemo1 {
    public static void main(String[] args) {
        try(
            // 1、创建一个文件字符输入流与源文件接通。
            Reader fr = new FileReader("day10-oop\\src\\csb.txt");
            // a、把低级的字符输入流包装成高级的缓冲字符输入流。
            BufferedReader br = new BufferedReader(fr); // 多态下调用子类方法,只能强转或不用多态
        ) {
            // 2、用循环,每次读取一个字符数组的数据。  1024 + 1024 + 8
            char[] buffer = new char[1024]; // 1K字符
            int len;
            while ((len = br.read(buffer)) != -1) {
                String rs = new String(buffer, 0, len);
                System.out.print(rs);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

示例2:

public class BufferedReaderDemo2 {
    public static void main(String[] args) {
        try(
            // 1、创建一个文件字符输入流与源文件接通。
            Reader fr = new FileReader("day10-oop\\src\\csb.txt");
            // a、把低级的字符输入流包装成高级的缓冲字符输入流。
            BufferedReader br = new BufferedReader(fr); // 多态下调用子类方法,只能强转或不用多态
        ) {
            // 2、用循环,每次读取一行数据。 
            String line;
            while ((line = br.readLine()) != null){
                System.out.println(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4.2.2 字符缓冲输出流 BufferedWritter

字符缓冲输出流:BufferedWritter,可提高字符输出流写取数据的性能,除此之外多了换行功能

构造器:
在这里插入图片描述
独有方法:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第22张图片

注:特性是由原始管道决定。(如:若想追加数据,在原始管道中使用追加管道数据的构造方法)

示例:

public class BufferedWriterDemo3 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个字符输出流管道与目标文件接通
        // Writer fw = new FileWriter("day10-oop\\src\\data02.txt"); // 覆盖管道,每次启动都会清空文件之前的数据
        Writer fw = new FileWriter("day10-oop\\src\\data02.txt", true); // 追加数据
        BufferedWriter bw = new BufferedWriter(fw); // 特性是由原始管道决定,因此若想追加数据,也是设置上一行代码

        // a.public void write(int c):写一个字符出去
        bw.write(98);
        bw.write('a');
        bw.write('徐');
        bw.newLine(); // bw.write("\r\n"); // 换行

        // b.public void write(String c)写一个字符串出去
        bw.write("abc我是中国人");
        bw.newLine(); // bw.write("\r\n"); // 换行


        // c.public void write(char[] buffer):写一个字符数组出去
        char[] chars = "abc我是中国人".toCharArray();
        bw.write(chars);
        bw.newLine(); // bw.write("\r\n"); // 换行


        // d.public void write(String c ,int pos ,int len):写字符串的一部分出去
        bw.write("abc我是中国人", 0, 5);
        bw.newLine(); // bw.write("\r\n"); // 换行

        // e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
        bw.write(chars, 3, 5);
        bw.newLine(); // bw.write("\r\n"); // 换行

        // fw.flush();// 刷新后流可以继续使用
        bw.close(); // 关闭包含刷新,关闭后流不能使用
    }
}

五、转换流

上文在使用字符流读取中文的时候,只有在文件和代码的编码一致才不会乱码,但编码不一致会出现乱码,而转换流可解决上述问题。

5.1 字符输入转换流 InputStreamReader

字符输入转换流:InputStreamReader,可以把原始的字节流按照指定编码转换成字符输入流

构造器:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第23张图片
示例:

public class InputStreamReaderDemo01 {
    public static void main(String[] args) throws Exception {
        // 1、提取GBK文件的原始字节流。
        InputStream is = new FileInputStream("day10-oop\\src\\dataGBK.txt");
        
        // 2、把原始字节流转换成字符输入流
        // Reader isr = new InputStreamReader(is); // 默认以UTF-8的方式转换成字符流。 还是会乱码的  跟直接使用FileReader是一样的
        Reader isr = new InputStreamReader(is , "GBK"); // 以指定的GBK编码转换成字符输入流  完美的解决了乱码问题

        // 3、把低级的字符输入流包装成高级的缓冲字符输入流。
        BufferedReader br = new BufferedReader(isr);

        String line;
        while ((line = br.readLine()) != null){
            System.out.println(line); // 正常打印
        }
    }
}

5.2 字符输出转换流 OutputStreamWrite

字符输出转换流:OutputStreamWriter,可以把字节输出流按照指定编码转换成字符输出流

构造器:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第24张图片

示例:

public class OutputStreamWriterDemo02 {
    public static void main(String[] args) throws Exception {
        // 1、定义一个字节输出流
        OutputStream os = new FileOutputStream("day10-oop\\src\\demoGBK.txt");

        // 2、把原始的字节输出流 转换成 字符输出流
        // Writer osw = new OutputStreamWriter(os); // 以默认的UTF-8写字符出去 跟直接写FileWriter一样
        Writer osw = new OutputStreamWriter(os , "GBK"); // 指定GBK的方式写字符出去

        // 3、把低级的字符输出流包装成高级的缓冲字符输出流。
        BufferedWriter bw = new BufferedWriter(osw);

        bw.write("我爱中国1~~");
        bw.write("我爱中国2~~");
        bw.write("我爱中国3~~");
        bw.close();
    }
}

六、序列化流

6.1 对象序列化与反序列化

对象序列化: 以内存为基准,把内存中的对象存储到磁盘文件中去。使用到的流是对象字节输出流:ObjectOutputStream。

对象反序列化: 以内存为基准,把存储到磁盘文件中去的对象数据恢复成内存中的对象。使用到的流是对象字节输入流:ObjectInputStream


6.2 对象字节输出流 ObjectOutputStream

构造器:
在这里插入图片描述

序列化方法:
在这里插入图片描述

注意:

① 序列化对象必须实现序列化接口。
② transient 修饰的成员变量不参与序列化。

示例:

public class Student implements Serializable {
    // 申明序列化的版本号码
    // 序列化的版本号与反序列化的版本号必须一致才不会出错!
    private static final long serialVersionUID = 1;
    private String name;
    private String loginName;
    // transient 修饰的成员变量不参与序列化了
    private transient String passWord;
    private int age ;
	...
}
public class ObjectOutputStreamDemo1 {
    public static void main(String[] args) throws Exception {
        // 1、创建学生对象
        Student s = new Student("张三", "zhangsan", "1314520", 21);

        // 2、对象序列化:使用对象字节输出流包装字节输出流管道
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day10-oop/src/obj.txt"));

        // 3、直接调用序列化方法
        oos.writeObject(s);

        // 4、释放资源
        oos.close();
        System.out.println("序列化完成了~~");
    }
}

6.3 对象字节输入流 ObjectInputStream

构造器:
在这里插入图片描述
序列化方法:
在这里插入图片描述

示例:

public class ObjectInputStreamDemo2 {
    public static void main(String[] args) throws Exception {
        // 1、创建对象字节输入流管道包装低级的字节输入流管道
        ObjectInputStream is = new ObjectInputStream(new FileInputStream("day10-oop/src/obj.txt"));

        // 2、调用对象字节输入流的反序列化方法
        Student stu = (Student) is.readObject();
        System.out.println(stu );
    }
}

七、打印流

打印流可以实现方便、高效的打印数据到文件中去。打印流一般是指:PrintStream、PrintWriter两个类。

可以实现打印什么数据就是什么数据,如:打印整数 97 写出去就是 97,打印 boolean 的 true,写出去就是 true。

7.1 PrintStream

构造器:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第25张图片
常用方法:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第26张图片


7.2 PrintWriter

构造器:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第27张图片

常用方法:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第28张图片

示例1:

public class PrintDemo1 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个打印流对象
        // PrintStream ps = new PrintStream(new FileOutputStream("day10-oop/src/print.txt"));
        PrintStream ps = new PrintStream(new FileOutputStream("day10-oop/src/print.txt" , true)); // 追加数据,在低级管道后面加True
        // PrintStream ps = new PrintStream("day10-oop/src/print.txt" );
        // PrintWriter pw = new PrintWriter("day10-oop/src/print.txt"); // 打印功能上与PrintStream的使用没有区别

        ps.println(97);
        ps.println('a');
        ps.println(23.3);
        ps.println(true);
        ps.println("我是打印流输出的,我是啥就打印啥");
        ps.close();
    }
}

示例2: 改变输出语句的位置

public class PrintDemo2 {
    public static void main(String[] args) throws Exception {
        System.out.println("锦瑟无端五十弦");
        System.out.println("一弦一柱思华年"); // out 是 PrintStream 对象

        // 改变输出语句的位置(重定向)
        PrintStream ps = new PrintStream("day10-oop/src/log.txt");
        System.setOut(ps); // 把系统打印流改成我们自己的打印流

        System.out.println("庄生晓梦迷蝴蝶");
        System.out.println("望帝春心托杜鹃");
    }
}

7.3 PrintStream 和 PrintWriter 的区别

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

补充:Commons-IO 框架

Commons-IO 是 apache 开源基金组织提供的一组有关 IO 操作的类库,可以提高 IO 功能开发的效率。
Commons-IO 工具包提供了很多有关 IO 操作的类。有两个主要的类 FileUtils, IOUtils。

FileUtils 主要有如下方法:
Java 基础进阶篇(十五):IO 流总结(全网最全面)_第29张图片

示例:

/**
    包的功能描述见下表:
         | 包                                  | 功能描述                                     |
         | ----------------------------------- | :------------------------------------------- |
         | org.apache.commons.io               | 有关Streams、Readers、Writers、Files的工具类 |
         | org.apache.commons.io.input         | 输入流相关的实现类,包含Reader和InputStream  |
         | org.apache.commons.io.output        | 输出流相关的实现类,包含Writer和OutputStream |
         | org.apache.commons.io.serialization | 序列化相关的类

    步骤:
         1. 下载commons-io相关jar包;http://commons.apache.org/proper/commons-io/
         2. 把commons-io-2.6.jar包复制到指定的Module的lib目录中
         3. 将commons-io-2.6.jar加入到classpath中

    小结:
         IOUtils和FileUtils可以方便的复制文件和文件夹!!
 */
public class CommonsIODemo {
    public static void main(String[] args) throws Exception {

        // 1.完成文件复制!
        IOUtils.copy(new FileInputStream("D:\\resources\\hushui.jpeg"),
                new FileOutputStream("D:\\resources\\hushui2.jpeg"));

        // 2.完成文件复制到某个文件夹下!
        FileUtils.copyFileToDirectory(new File("D:\\resources\\hushui.jpeg"), new File("D:/"));


        // 3.完成文件夹复制到某个文件夹下!
        FileUtils.copyDirectoryToDirectory(new File("D:\\resources") , new File("D:\\new"));
        FileUtils.deleteDirectory(new File("D:\\new"));

        // JDK1.7 也做了一些一行代码完成复制的操作:New IO的技术
        // Files.copy(Path.of("D:\\resources\\hushui.jpeg"), Path.of("D:\\resources\\hushui3.jpeg"));

        FileUtils.deleteDirectory(new File("D:\\new")); // 只能删除空文件夹
    }
}

文章参考:Java入门基础视频教程,java零基础自学就选黑马程序员Java入门教程(含Java项目和Java真题)

你可能感兴趣的:(JavaSE,java,jvm,开发语言,IO流,字符集)