Java IO流 学习笔记

1、IO概述

1.1 什么是IO?

Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做写出数据。

1.2 IO的分类

根据数据的流向分为:输入流和输出流。

  • 输入流:把数据从其他设备上读取到内存中的流
  • 输出流:把数据从内存中写出到其他设备上的流

根据数据的类型分为:字节流和字符流

  • 字节流:以字节为单位,读写数据的流
  • 字符流:以字符为单位,读写数据的流

1.3 IO的流向说明

1_io

1.4 顶级父类

输入流 输出流
字节流 字节输入流
InputStream
字节输出流
OutputStream
字符流 字符输入流
Reader
字符输出流
Writer

2、字节流

2.1 一切皆为字节

一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。

2.2 字节输出流[OutputStream]

java.io.OutputStream抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。

2_io

  • public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
  • public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
  • public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
  • public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
  • public abstract void write(int b) :将指定的字节输出流。

close方法,当完成流的操作的时候,必须调用此方法,释放系统资源

2.3 FileOutputStream类

OutputStream有很多子类,如ByteArrayOutputStreamFileOutputStreamFilterOutputStream ObjectOutputStreamPipedOutputStream我们从最简单的一个子类开始。

写入数据的原理(内存--->硬盘)

java程序-->JVM(java虚拟机)-->OS(操作系统)-->OS调用写数据的方法-->把数据写入到文件中

字节输出流的使用步骤

  1. 创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
  2. 调用FileOutputStream对象中的方法write,把数据写入到文件中
  3. 释放资源(流使用会占用一定的内存,使用完毕后要把内存清空,提高程序的效率)

构造方法

  • public FileOutputStream(String name):创建文件输出流以指定的名称写入文件。
  • public FileOutputStream(File file):创建文件输出流以写入由指定的 File对象表示的文件。

写出字节数据

文件存储的原理和记事本打开的原理

3_io

  1. 写出字节:write(int b) 方法,每次可以写出一个字节数据,代码使用演示
public static void main(String[] args) throws IOException {
        //1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
        FileOutputStream fos = new FileOutputStream("G:\\testIO\\t1.txt");
        //2.调用FileOutputStream对象中的方法write,把数据写入到文件中
        fos.write(97);//文件中写的是  a
        //3.释放资源(流使用会占用一定的内存,使用完毕后要把内存清空,提高程序的效率)
        fos.close();
    }
  1. 写出字节数组:write(byte[] b),每次可以写出数组中的数据。

  2. 写出指定长度字节数组:write(byte[] b, int off, int len) ,每次写出从off索引开始,len个字节。

/*
 * `public void write(byte[] b)`:
 * 将 b.length字节从指定的字节数组写入此输出流。
 * `public void write(byte[] b, int off, int len)` :
 * 从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
 */
public class TestIO02 {
    public static void main(String[] args) throws IOException {
        //1.创建一个FileOutputStream对象,构造方法中传递写入数据的目的地
        FileOutputStream fos = new FileOutputStream("G:\\testIO\\t2.txt");
        fos.write(49);
        fos.write(48);
        fos.write(48);
        //2.调用FileOutputStream对象中的方法write,把数据写入到文件中
        /*
         * `public void write(byte[] b)`:
         * 将 b.length字节从指定的字节数组写入此输出流。
         * 一次写多个字节
         *      如果写的第一个字节数正数(0-127) ,那么显示的时候会查询ASCII表
         *      如果写的第一个字节是负数,那第一个字节会和第二个字节,两个字节组成一个中文显示,
         *      查询系统默认码表(GBK);
         */
        byte[] bytes = {65,66,67,68,69};
       // byte[] bytes = {-65,-66,-67,68,69};
        /*
         * `public void write(byte[] b, int off, int len)` :
         * 从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
         * int off:数组的开始索引
         * int len:写几个字符
         */
        fos.write(bytes,1,2);//BC
        /*
        写入字符串的方法:可以使用String类中的方法吧字符串,转换为字节数组
         byte[] getBytes() 把字符串转换为字节数组
         */
        byte[] bytes1 = "你好".getBytes();
        System.out.println(Arrays.toString(bytes1));//[-28, -67, -96, -27, -91, -67] UFT-8三个字节一个中文
        fos.write(bytes1);
        // fos.write(bytes);
        //3.释放资源(流使用会占用一定的内存,使用完毕后要把内存清空,提高程序的效率)
        fos.close();
    }

数据追加续写

  • public FileOutputStream(File file, boolean append): 创建文件输出流以写入由指定的 File对象表示的文件。
  • public FileOutputStream(String name, boolean append): 创建文件输出流以指定的名称写入文件。

这两个构造方法,参数中都需要传入一个boolean类型的值,true 表示追加数据,false 表示清空原有数据。

写换行:写换行符号

  • windows:\r\n
  • linux:\n
  • mac:\r
    /*
- `public FileOutputStream(File file, boolean append)`: 创建文件输出流以写入由指定的 File对象表示的文件。
- `public FileOutputStream(String name, boolean append)`: 创建文件输出流以指定的名称写入文件。
        参数:
            String name,File file:写入数据的目的地
            boolean append:追加写开关
                true:创建对象不会覆盖源文件,继续在文件末尾追加写数据
                false:创建一个新文件

        写换行:写换行符号
            windows:\r\n
            linux:\n
            mac:\r
     */
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("a.txt", true);
        for (int i =1;i<=10;i++){
            fos.write("你好".getBytes());
            fos.write("\r\n".getBytes());
        }
        fos.close();

    }

2.4 字节输入流[InputStream]

java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。

  • public void close() :关闭此输入流并释放与此流相关联的任何系统资源。
  • public abstract int read(): 从输入流读取数据的下一个字节。
  • public int read(byte[] b): 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。

小贴士:

close方法,当完成流的操作时,必须调用此方法,释放系统资源。

2.5 FileInputStream类

java.io.FileInputStream 类是文件输入流,从文件中读取字节。

构造方法:

  1. FileInputStream(String name): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
  2. FileInputStream(File file):通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。

当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出FileNotFoundException

读取数据的原理(硬盘--->内存)

java程序-->JVM(java虚拟机)-->OS(操作系统)-->OS调用读取数据的方法-->读取文件

字节输入流的使用步骤

  1. 创建一个FileInputStream对象,构造方法中绑定要读取的数据源
  2. 调用FileInputStream对象中的方法read,读取文件
  3. 释放资源(流使用会占用一定的内存,使用完毕后要把内存清空,提高程序的效率)

读取字节数据

  1. 读取字节:read方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1
/*
    java.io.InputStream:字节输入流
    此抽象类是表示字节输入流的所有类的超类

    定义了所有子类共性的方法
        int read()  从该输入流读取一个字节的数据。
        int read(byte[] b) 从输入流读取一些字节数,并将它们存储到缓冲区 b 。
        void close()  关闭此输入流并释放与流相关联的任何系统资源。

        java.io.FileInputStream extends  InputStream
        FileInputStream 文件字节输入流
        作用:把硬盘文件中的数据,读取到内存中使用
        构造方法:
        FileInputStream(String name)
        FileInputStream(File file)
        参数:读取文件的数据源
            String name :文件的路径
            File file:文件
        构造方法的作用:
            1.会创建一个FileInputStream对象
            2.会把FileInputStream对象指定构造方法中要读取的文件
        **读取数据的原理(硬盘--->内存)**
            java程序-->JVM(java虚拟机)-->OS(操作系统)-->OS调用读取数据的方法-->读取文件

        **字节输入流的使用步骤**
            1. 创建一个FileInputStream对象,构造方法中绑定要读取的数据源
            2. 调用FileInputStream对象中的方法read,读取文件
            3. 释放资源(流使用会占用一定的内存,使用完毕后要把内存清空,提高程序的效率)

 */
public class TestIO04 {
    public static void main(String[] args) throws IOException {
        //1. 创建一个FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("G:\\testIO\\t3.txt");
        //2. 调用FileInputStream对象中的方法read,读取文件
        //int read()读取文件中的一个字节并返回,读取到文件的末尾并返回-1
        /*int len = fis.read();
        System.out.println(len);

        len = fis.read();
        System.out.println(len);

        len = fis.read();
        System.out.println(len);

        len = fis.read();
        System.out.println(len);

        len = fis.read();
        System.out.println(len);*/
        /*
            发现以上读取文件是一个重复的过程,所以可以使用循环优化
            不知道文件中有多少个字节,使用while循环
            while循环结束条件,读取到-1时结束

            布尔表达式:(len=fis.read())!=-1
                1.fis.read():读取一个字节
                2.len=fis.read():把读取到的字节赋值给变量len
                3.(len=fis.read())!=-1:判断变量len是否不等于-1
         */

        int len=0;//记录读取的字节
        while((len=fis.read())!=-1){
            System.out.print((char) len);
        }
        //3. 释放资源(流使用会占用一定的内存,使用完毕后要把内存清空,提高程序的效率)
        fis.close();
    }

原理

4_io

  1. 使用字节数组读取:read(byte[] b),每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1
/*
    字节输入流一次读取多个字节的方法
        int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓存区数组b中。
    明确两件事情:
        1.方法的参数byte[]的作用?
        答:起到缓冲作用,存储每次读取到的多个字节
        数组长度一般定义为1024或1024的整数倍
        2.方法的返回值是什么?
        答:每次读取到的有效字节个数

    String类的构造方法
        String(byte[] bytes):把字节数组转换为字符串
        String(byte[] bytes,int offset,int length) 把字节数组的一部分转换为字符串
        offset:数组的开始索引,length:转换的字符个数
 */
public class TestIO05 {
    public static void main(String[] args) throws IOException {
        //1. 创建一个FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("G:\\testIO\\t3.txt");
        //使用FileInputStream对象中的方法read读取文件
        //int read(byte[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中
       /* byte[] bytes = new byte[2];
        int len = fis.read(bytes);
        System.out.println(len);//2
        //System.out.println(Arrays.toString(bytes));//[65, 66]
        System.out.println(new String(bytes));//AB

        len = fis.read(bytes);
        System.out.println(len);//2
        System.out.println(new String(bytes));//CD

        len = fis.read(bytes);
        System.out.println(len);//1
        System.out.println(new String(bytes));//ED

        len = fis.read(bytes);
        System.out.println(len);//-1
        System.out.println(new String(bytes));//ED*/
       /*
       发现以上读取时是一个重复的过程,可以使用循环优化
       不知道文件中有多少字节,所以使用while循环
       while循环结束的条件,读取到-1结束
        */
        byte[] buffer = new byte[1024];//存储读取到的多个字节
        int len=0;//记录每次读取的有效字节个数
        while ((len=fis.read(buffer))!=-1){
            System.out.println(new String(buffer,0,len));
        }
        //释放资源
        fis.close();
    }

原理

5_io

2.6 图片复制

原理

6_io

实现代码

/*
    复制文件:一读一写
    明确:
        数据源:G:\testIO\p1.jpeg
        数据的目的地:当前文件夹
    文件复制的步骤:
        1.创建一个字节输入流的对象,构造方法中绑定要读取的数据源
        2.创建一个字节输出流的对象,构造方法中绑定要写如的目的地
        3.使用字节输入流对象中的read方法读取文佳
        4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
        5.释放资源
 */
public class CopyFile {
    public static void main(String[] args) throws Exception {
        //1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("G:\\testIO\\p1.jpeg");
        //2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
        FileOutputStream fos = new FileOutputStream("p1.jpeg");
        //一次读取一个字节写入一个字节的方式
        //3.使用字节输入流对象中的方法read读取文件
        /*int len=0;
        while ((len=fis.read())!=-1){
            //4.使用字节输出流中的write,把读取到的字节写入到目的地文件中
            fos.write(len);
        }*/

        //优化
        //使用数组缓冲读取多个字节,写入多个字节
        byte[] buffer = new byte[1024];
        int len=0;
        //3.使用字节输入流对象中的方法read读取文件
        while ((len=fis.read(buffer))!=-1){
            //4.使用字节输出流中的write,把读取到的字节写入到目的地文件中
            fos.write(buffer,0,len);

        }
        //5.释放资源(先关闭写的,后关闭读的,如果写完了,肯定读完了)
        fos.close();
        fis.close();
        //

    }

3、字符流

当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储。所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。

/*
    使用字节流读取中文文件
    1个中文
        GBK:占用两个字节
        UTF-8:占用三个字节
 */
public class TestIO06 {
    public static void main(String[] args) throws Exception {
        FileInputStream fis = new FileInputStream("G:\\testIO\\t4.txt");
        int len=0;
        //不乱码
//        byte[] buffer = new byte[3];
//        while((len=fis.read(buffer))!=-1){
//            System.out.println(new String(buffer,0,len));
//        }
        //乱码
        while ((len=fis.read())!=-1){
            System.out.println((char)len);
        }
        fis.close();
    }
}

3.1 字符输入流【Reader】

java.io.Reader:字符输入流,是字符输入流的最顶层的父类,定义了一些共性的成员方法,是一个抽象类.

共性的成员方法:

  • public int read():读一个字符
  • public int read(char cbuf[]):将字符读入数组
  • public abstract void close():关闭流并释放与之相关联的任何系统资源。

3.2 FileReader类

java.io.FileReader 类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

小贴士:

  1. 字符编码:字节与字符的对应规则。Windows系统的中文编码默认是

    GBK编码表。

    idea中UTF-8

  2. 字节缓冲区:一个字节数组,用来临时存储字节数据。

构造方法

  • FileReader(File file):创建一个新的FileReader,给定要读取的File对象
  • FileReader(String fileName):创建一个新的FileReader,给定要读取的文件到的名称。

读取字符数据

字符输入流的使用步骤

  1. 创建FileReader对象,构造方法中绑定要读取的数据源

  2. 使用FileReader对象中的方法read读取文件

  3. 释放资源

  4. 读取字符read方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回-1,循环读取

  5. 使用字符数组读取read(char[] cbuf),每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回-1

    /*
        java.io.Reader:字符输入流,是字符输入流的最顶层的父类,定义了一些共性的成员方法,是一个抽象类

        共性的成员方法:
            int read() :读一个字符
            int read(char[] cbuf) :将字符读入数组。
            abstract void close() :关闭流并释放与之相关联的任何系统资源。
            java.io.FileReader extends java.io.InputStreamReader extends java.io.Reader
            FileReader : 文件字符输入流
            作用:把硬盘文件中的数据以字符的方式读取到内存中。

            构造方法:
                FileReader(File file)
                FileReader(String fileName)
                参数:读取文件的数据源
                    String fileName:文件的路径
                    File File:一个文件
            FileReader构造方法的作用:
                1.创建一个FileReader对象
                2.会把FileReader对象指向我们要读取的文件。
            字符输入流的使用步骤
                1.创建FileReader对象,构造方法中绑定要读取的数据源
                2.使用FileReader对象中的方法read读取文件
                3.释放资源

     */
    public static void main(String[] args) throws Exception {
        //1.创建FileReader对象,构造方法中绑定要读取的数据源
        FileReader fr = new FileReader("G:\\testIO\\t4.txt");
        //2.使用FileReader对象中的方法read读取文件
        //int read():读取单个字符并返回
        /*int len=0;
        while ((len=fr.read())!=-1){
            System.out.print((char)len);
        }*/
        //int read(char[] cbuf) :将字符读入数组。
        char[] cs = new char[1024];//存储读取到的多个字符
        int len =0;//记录的是每次读取的有效字符个数
        while ((len=fr.read(cs))!=-1){
            /*
                String 类的构造方法
                    String(char[] value):把字符数组转换成字符串
                    String(char[] value, int offset, int count) :把字符数组的一部分转换成字符串
                    offset:数组的开始索引;count:转换的个数
             */
            System.out.println(new String(cs,0,len));
        }
        // 3.释放资源
        fr.close();
    }

3.3 字节输出流【Writer】

java.io.Writer 抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。

  • void write(int c) 写入单个字符。
  • void write(char[] cbuf) 写入字符数组。
  • abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
  • void write(String str) 写入字符串。
  • void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
  • void flush() 刷新该流的缓冲。
  • void close() 关闭此流,但要先刷新它。

3.4 FileWriter类

java.io.FileWriter 类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

构造方法

  • FileWriter(File file): 创建一个新的 FileWriter,给定要读取的File对象。
  • FileWriter(String fileName): 创建一个新的 FileWriter,给定要读取的文件的名称。

作用

  1. 会创建一个FileWriter对象
  2. 会根据构造方法中传递的文件/文件名的路径,创建文件
  3. 会把FileWriter对象指向创建好的文件

1.写出字符write(int b) 方法,每次可以写出一个字符数据。

 /*
        java.io.Writer :字符输出流,是所有字符输出流的最顶层的父类,是一个抽象类

        共性的成员方法:
        - `void write(int c)` 写入单个字符。
        - `void write(char[] cbuf) `写入字符数组。
        - `abstract  void write(char[] cbuf, int off, int len) `写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        - `void write(String str) `写入字符串。
        - `void write(String str, int off, int len)` 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
        - `void flush() `刷新该流的缓冲。
        - `void close()` 关闭此流,但要先刷新它。

        java.io.FileWriter extends OutputStreamWriter extends Writer
        FileWriter:文件字符输出流
        作用:把内存中的字符数据写入到文件中

        构造方法
        FileWriter(File file) 给一个File对象构造一个FileWriter对象。
        FileWriter(String fileName) 构造一个给定文件名的FileWriter对象。
        参数:写入数据的目的地
            String fileName:文件路径
            File file:是一个文件
        构造方法的作用:
            1.会创建一个FileWriter对象
            2.会根据构造方法中传递的文件/文件名的路径,创建文件
            3.会把FileWriter对象指向创建好的文件
        字符输出流的使用步骤:
            1.创建FileWriter对象。构造方法中绑定要写入数据的目的地
            2.使用FileWriter中的方法write,把数据写入到内存缓冲区(字符转换为字节的过程)
            3.使用FileWriter中的方法flush,把内存缓冲区的数据,刷新到文件中
            4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
     */
    public static void main(String[] args) throws IOException {
        //1.创建FileWriter对象。构造方法中绑定要写入数据的目的地
        FileWriter fw = new FileWriter("G:\\testIO\\t5.txt");
        //2.使用FileWriter中的方法write,把数据写入到内存缓冲区(字符转换为字节的过程)
        // - `void write(int c)` 写入单个字符
        fw.write(97);
        //3.使用FileWriter中的方法flush,把内存缓冲区的数据,刷新到文件中
        fw.flush();
        // 4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
        fw.close();

    }

2.关闭和刷新

因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。但是关闭的流对象,是无法继续写出数据的。如果我们既想写出数据,又想继续使用流,就需要flush 方法了。

  • flush :刷新缓冲区,流对象可以继续使用。
  • close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了
public static void main(String[] args) throws IOException {
        //1.创建FileWriter对象。构造方法中绑定要写入数据的目的地
        FileWriter fw = new FileWriter("G:\\testIO\\t5.txt");
        //2.使用FileWriter中的方法write,把数据写入到内存缓冲区(字符转换为字节的过程)
        // - `void write(int c)` 写入单个字符
        fw.write(97);
        //3.使用FileWriter中的方法flush,把内存缓冲区的数据,刷新到文件中
        fw.flush();
        fw.write(98);//刷新后,流可以继续使用
        // 4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
        fw.close();
        fw.write(98);//close方法使用后已经关闭了,已经从内存中消失了,流就不能再使用了
    }

3.写出字符数组write(char[] cbuf)write(char[] cbuf, int off, int len) ,每次可以写出字符数组中的数据,用法类似FileOutputStream。

4.写出字符串write(String str)write(String str, int off, int len) ,每次可以写出字符串中的数据,更为方便

    /*
    - `void write(char[] cbuf) `写入字符数组。
        - `abstract  void write(char[] cbuf, int off, int len) `写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        - `void write(String str) `写入字符串。
        - `void write(String str, int off, int len)` 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
     */
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("G:\\testIO\\t6.txt");
        char[] cs= {'a','b','c','d','e'};
        //`void write(char[] cbuf) `写入字符数组。
        fw.write(cs);
        //`abstract  void write(char[] cbuf, int off, int len) `写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        fw.write(cs,1,3);
        //- `void write(String str) `写入字符串。
        fw.write("你好世界");
        //- `void write(String str, int off, int len)` 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
        fw.write("你好世界",2,2);

        fw.close();
    }

5.续写和换行:操作类似于FileOutputStream

续写和换行
续写,追加写:使用两个参数的构造方法
 FileWriter(File file, boolean append) :
 FileWriter(String fileName, boolean append) 
 参数:String fileName、File file:写入数据的目的地
 boolean append:续写数据的开关 true:可以续写;false:不可续写
 换行:换行符号
 windows:\r\n
 linux:\n
 mac:\r
    /*
    续写和换行
    续写,追加写:使用两个参数的构造方法
        FileWriter(File file, boolean append) :
        FileWriter(String fileName, boolean append)
        参数:String fileName、File file:写入数据的目的地
        boolean append:续写数据的开关 true:可以续写;false:不可续写
        换行:换行符号
        windows:\r\n
        linux:\n
        mac:\r
     */
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("t1.txt",true);
        for (int i = 0; i < 10; i++) {
            fw.write("helloworld"+i);
            fw.write("\r\n");
        }
        fw.close();
    }

4、IO异常处理

JDK7之前处理

在jdk1.7之前使用try catch finally处理流中的异常

/*
        在jdk1.7之前使用try catch finally处理流中的异常
        格式:
        try{
            可能会产生异常的代码
        }catch(异常类的变量 变量名){
            异常的处理逻辑
        }finally{
            一定执行的代码
            资源释放
        }
     */
    public static void main(String[] args) {
        //提高变量fw的作用域,让finally可以使用
        //变量在定义的时候,可以没有值,但是使用的时候必须有值
        FileWriter fw=null;
        try {
            //可能会产生异常的代码
            fw = new FileWriter("t1.txt",true);
            for (int i = 0; i < 10; i++) {
                fw.write("helloworld"+i);
                fw.write("\r\n");
            }


        }catch (IOException e){
            //异常的处理逻辑
            System.out.println(e);
        }finally {
            //一定执行的代码
            //如果对象创建失败了,fw的默认值就是null,null是不能调用方法的,会抛出NullPointerException
            // ,需要增加一个判断,不是null在吧资源释放。
            if (fw!=null){
                try {
                    //f.close方法声明抛出来IOException异常对象,
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }

    }

JDK7新特性

在try后面可以增加一个(),在括号中可以定义流对象
那么这个流对象的作用域就在try中有效
try中的代码执行完毕后,会自动把流对象释放,不用写finally

格式:
try(定义流对象;定义流对象){
可能会产生异常的代码
}catch(异常类的变量 变量名){
异常的处理逻辑
}

/*
    JDK7新特性
    在try后面可以增加一个(),在括号中可以定义流对象
    那么这个流对象的作用域就在try中有效
    try中的代码执行完毕后,会自动把流对象释放,不用写finally
    格式:
        try(定义流对象;定义流对象){
            可能会产生异常的代码
        }catch(异常类的变量 变量名){
            异常的处理逻辑
        }
*/
public class TestIO13 {
    public static void main(String[] args) {
        try(
                FileInputStream fis = new FileInputStream("GS:\\testIO\\p1.jpeg");
                FileOutputStream fos = new FileOutputStream("p1.jpeg");
                ) {
            byte[] buffer = new byte[1024];
            int len=0;
            while ((len=fis.read(buffer))!=-1){
                fos.write(buffer,0,len);
            }
        }catch (IOException e){
            System.out.println(e);

        }

    }
}

JDK9新特性

try的前面可以定义流对象
在try后面的()中可以直接引入流对象的名称(变量名)
在try代码执行完毕之后,流对象也可以释放掉,不用写finally

格式:
A a = new A();
B b = new B();
try(a;b){
}catch{
}

/*
    JDK9新特性
    try的前面可以定义流对象
    在try后面的()中可以直接引入流对象的名称(变量名)
    在try代码执行完毕之后,流对象也可以释放掉,不用写finally
    格式:
        A a = new A();
        B b = new B();
        try(a;b){
        }catch{
        }
 */
public class TestIO14 {
    public static void main(String[] args) throws FileNotFoundException {
        FileInputStream fis = new FileInputStream("G:\\testIO\\p1.jpeg");
        FileOutputStream fos = new FileOutputStream("p1.jpeg");
        try(fis;fos) {
            byte[] buffer = new byte[1024];
            int len=0;
            while ((len=fis.read(buffer))!=-1){
                fos.write(buffer,0,len);
            }
        }catch (IOException e){
            System.out.println(e);

        }
    }
}

5、属性集

5.1概述

java.util.Properties 继承于 Hashtable ,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。该类也被许多Java类使用,比如获取系统属性时,System.getProperties 方法就是返回一个Properties对象。

Properties类表示一组持久的属性。 Properties可以保存到流中或从流中加载。 属性列表中的每个键及其对应的值都是一个字符串。

5.2 Properties类

构造方法

public Properties() :创建一个空的属性列表。

基本的存储方法

  • Object setProperty(String key, String value) 调用 Hashtable方法 put 。
  • String getProperty(String key) 通过key找到value值,此方法相当于Map集合中的get(key)方法
  • Set stringPropertyNames() 返回此属性列表中的键集,其中该键及其对应值是字符串,此方法相当于Map集合中的keySet方法
/*
    java.util.Properties结合 extends Hashtable<k,v> implements Map<k,v>
    Properties类表示一组持久的属性。 Properties可以保存到流中或从流中加载。
    Properties集合是一个唯一和IO流相结合的集合
        可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
        可以使用Properties集合中的方法load,吧硬盘中保存的文件(键值对),读取到集合中使用
    属性列表中的每个键及其对应的值都是一个字符串。
        Properties集合是一个双列集合,key和value默认都是字符串 
 */
public class Test01 {
    public static void main(String[] args) {
        show01();
    }
    /*
    使用Properties集合存储数据,遍历取出Properties集合中的数据
     Properties集合是一个双列集合,key和value默认都是字符串
    Properties集合有一些操作字符串的特有方法:
        Object setProperty(String key, String value) 调用 Hashtable方法 put 。
        String getProperty(String key) 通过key找到value值,此方法相当于Map集合中的get(key)方法
        Set<String> stringPropertyNames()  返回此属性列表中的键集,其中该键及其对应值是字符串,此方法相当于Map集合中的keySet方法
     */

    private static void show01() {
        //创建Properties集合对象
        Properties prop = new Properties();
        //使用setProperty往集合中添加数据
        prop.setProperty("数学","100");
        prop.setProperty("语文","100");
        prop.setProperty("英语","100");
        //使用stringPropertyNames吧Properties集合中的键取出,存储到一个set集合中
        Set<String> set = prop.stringPropertyNames();

        //遍历set集合,取出 Properties集合的每一个键
        for (String key : set) {
            //s使用getProperty通过key获取value
            String value = prop.getProperty(key);
            System.out.println(key+"="+value);
        }
    }
}

store方法

可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储。

  • void store(OutputStream out, String comments)
  • void store(Writer writer, String comments)

参数:

OutputStream out:字节输出流,不能写中文
Writer writer:字符输出流,可以写中文
String comments:注释,用来解释说明保存的文件 是做什么用的
                不能使用中文,会产生乱码。默认是Unicode编码
                一般使用空字符串

使用步骤

1.创建Properties集合对象,添加数据
2.创建字节输出流/字符输出流,构造方法中绑定要输出的目的地
3.使用Properties集合中的方法store,吧集合中的临时数据,持久化写入到硬盘中存储
4.释放资源
    /*
        可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中存储
        void store(OutputStream out, String comments)
        void store(Writer writer, String comments)
        参数:
            OutputStream out:字节输出流,不能写中文
            Writer writer:字符输出流,可以写中文
            String comments:注释,用来解释说明保存的文件 是做什么用的
                            不能使用中文,会产生乱码。默认是Unicode编码
                            一般使用空字符串
         使用步骤:
            1.创建Properties集合对象,添加数据
            2.创建字节输出流/字符输出流,构造方法中绑定要输出的目的地
            3.使用Properties集合中的方法store,吧集合中的临时数据,持久化写入到硬盘中存储
            4.释放资源

     */

    private static void show02() throws IOException {
        //1.创建Properties集合对象,添加数据
        Properties prop = new Properties();
        //使用setProperty往集合中添加数据
        prop.setProperty("数学","100");
        prop.setProperty("语文","100");
        prop.setProperty("英语","100");
       // 2.创建字节输出流/字符输出流,构造方法中绑定要输出的目的地
        FileWriter fw = new FileWriter("prop.txt");
        //3.使用Properties集合中的方法store,吧集合中的临时数据,持久化写入到硬盘中存储
        prop.store(fw,"save data");
        // 4.释放资源
        fw.close();
        //prop.store(new FileOutputStream("prop.txt"),"");//会乱码
    }

load方法

可以使用Properties集合中的方法load,吧硬盘中保存的文件(键值对),读取到集合中使用。

  • void load(InputStream inStream)
  • void load(Reader reader)

参数

InputStream inStream:字节输入流,不能读取含有中文的键值对
Reader reader:字符输入流,能读取含有中文的键值对

使用步骤:

1.创建Properties集合对象
2.使用Properties集合对象中的方法load读取保存键值对的文件
3.遍历Properties集合

注意

1.存储键值对的文件中,键与值默认的连接符号可以使用=,空格(其他符号)
2.存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取
3.存储键值对的文件中,键与值默认都是字符串,不用再加引号

    /*
    可以使用Properties集合中的方法load,吧硬盘中保存的文件(键值对),读取到集合中使用
    void load(InputStream inStream)
    void load(Reader reader)
    参数:
        InputStream inStream:字节输入流,不能读取含有中文的键值对
        Reader reader:字符输入流,能读取含有中文的键值对
    使用步骤:
        1.创建Properties集合对象
        2.使用Properties集合对象中的方法load读取保存键值对的文件
        3.遍历Properties集合
    注意:
        1.存储键值对的文件中,键与值默认的连接符号可以使用=,空格(其他符号)
        2.存储键值对的文件中,可以使用#进行注释,被注释的键值对不会再被读取
        3.存储键值对的文件中,键与值默认都是字符串,不用再加引号
     */
    private static void show03() throws Exception {
        //1.创建Properties集合对象
        Properties prop = new Properties();
        //2.使用Properties集合对象中的方法load读取保存键值对的文件
         prop.load(new FileReader("prop.txt"));
         //乱码
        //prop.load(new FileInputStream("prop.txt"));
        //3.遍历Properties集合
        Set<String> set = prop.stringPropertyNames();
        for (String key : set) {
            String value = prop.getProperty(key);
            System.out.println(key+"="+value);
        }
    }

学习完基本的一些流,我们要见识一些更强大的流。比如能够高效读写的缓冲流,能够转换编码的转换流,能够持久化存储对象的序列化流等等。这些功能更为强大的流,都是在基本的流对象基础之上创建而来的,就像穿上铠甲的武士一样,相当于是对基本流对象的一种增强。

6、缓冲流

6.1 概述

缓冲流,也叫高效流,是对4个基本的FileXxx 流的增强,所以也是4个流,按照数据类型分类:

  • 字节缓冲流BufferedInputStreamBufferedOutputStream
  • 字符缓冲流BufferedReaderBufferedWriter

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

7_io

6.2 字节缓冲流

构造方法

  • public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。
  • public BufferedOutputStream(OutputStream out): 创建一个新的缓冲输出流。

字节缓冲输出流

构造方法

  • BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
  • BufferedOutputStream(OutputStream out, int size):创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。

参数:

OutputStream out:字节输出流
我们可以传递FileOutStream,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入效率
int size:指定缓冲流内部缓冲区的大小,不指定默认

使用步骤

1.创建一个FileOutStream,构造方法中绑定要输出的目的地
2.创建 BufferedOutputStream对象,构造方法中传递FileOutStream对象,提高FileOutStream对象效率
3.使用BufferedOutputStream对象中的方法write,把数据写入到内部的缓冲区中
4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
5.释放资源(会先调用flush方法刷新数据,第四步可以省略)

/*
    java.io.BufferedOutputStream  extends OutputStream
    BufferedOutputStream:字节缓冲输出流

    继承自父类的共性成员方法:
        * `public void close()` :关闭此输出流并释放与此流相关联的任何系统资源。
        * `public void flush() ` :刷新此输出流并强制任何缓冲的输出字节被写出。
        * `public void write(byte[] b)`:将 b.length字节从指定的字节数组写入此输出流。
        * `public void write(byte[] b, int off, int len)` :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
        * `public abstract void write(int b)` :将指定的字节输出流。
    构造方法:
        BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
        BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。
        参数:
            OutputStream out:字节输出流
                我们可以传递FileOutStream,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入效率
            int size:指定缓冲流内部缓冲区的大小,不指定默认
    使用步骤:
        1.创建一个FileOutStream,构造方法中绑定要输出的目的地
        2.创建 BufferedOutputStream对象,构造方法中传递FileOutStream对象,提高FileOutStream对象效率
        3.使用BufferedOutputStream对象中的方法write,把数据写入到内部的缓冲区中
        4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
        5.释放资源(会先调用flush方法刷新数据,第四步可以省略)
 */
public class Demo01 {
    public static void main(String[] args) throws Exception {
        // 1.创建一个FileOutStream,构造方法中绑定要输出的目的地
        FileOutputStream fos = new FileOutputStream("a.txt");
        //2.创建 BufferedOutputStream对象,构造方法中传递FileOutStream对象,提高FileOutStream对象效率
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //3.使用BufferedOutputStream对象中的方法write,把数据写入到内部的缓冲区中
        bos.write("数据写入到内部缓冲区中".getBytes());
        //4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
        bos.flush();
        //5.释放资源(会先调用flush方法刷新数据,第四步可以省略)
        bos.close();

    }
}

字节缓冲输入流

构造方法

  • BufferedInputStream(InputStream in):创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。
  • BufferedInputStream(InputStream in, int size):创建 BufferedInputStream具有指定缓冲区大小,并保存其参数,输入流 in ,供以后使用。

参数

InputStream in:字节输入流
我们可以传递FileInputStream,缓冲流会给FileInputStream增加一个缓冲区,提高FileInputStream的读取效率
int size:指定缓冲流内部缓冲区的大小,不指定默认

使用步骤

1.创建FileInputStream对象,构造方法中绑定要读取的数据源
2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
3.使用BufferedInputStream对象中的方法read,读取数据
4.释放资源

/*
java.io.BufferedInputStream  extends InputStream
    BufferedInputStream:字节缓冲输入流

    继承自父类的成员方法:
        int read()  从该输入流读取一个字节的数据。
        int read(byte[] b) 从输入流读取一些字节数,并将它们存储到缓冲区 b 。
        void close()  关闭此输入流并释放与流相关联的任何系统资源。
    构造方法:
        BufferedInputStream(InputStream in):创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。
        BufferedInputStream(InputStream in, int size) :创建 BufferedInputStream具有指定缓冲区大小,并保存其参数,输入流 in ,供以后使用。
        参数:
            InputStream in:字节输入流
            我们可以传递FileInputStream,缓冲流会给FileInputStream增加一个缓冲区,提高FileInputStream的读取效率
            int size:指定缓冲流内部缓冲区的大小,不指定默认
    使用步骤:
        1.创建FileInputStream对象,构造方法中绑定要读取的数据源
        2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
        3.使用BufferedInputStream对象中的方法read,读取数据
        4.释放资源
 */
public class Demo02 {
    public static void main(String[] args) throws Exception {
        //1.创建FileInputStream对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("a.txt");
        //2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
        BufferedInputStream bis = new BufferedInputStream(fis);
        //3.使用BufferedInputStream对象中的方法read,读取数据
        //int read()从输入流中读取数据的下一个字节
        /*int len=0;//记录每次读到的字节
        while((len=bis.read())!=-1){
            System.out.println(len);
        }*/
        //int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中
        byte[] buffer = new byte[1024];
        int len=0;
        while((len=bis.read(buffer))!=-1){
            System.out.println(new String(buffer,0,len));
        }
        //4.释放资源
        bis.close();
    }
}

6.3 缓冲流的效率测试

文件复制

不使用缓冲流技术

文件的大小:54,007字节
一次读写一个字节:894毫秒
使用数组缓冲读取多个字节,写入多个字节:4毫秒

/*
    复制文件:一读一写
    明确:
        数据源:G:\testIO\p1.jpeg
        数据的目的地:当前文件夹
    文件复制的步骤:
        1.创建一个字节输入流的对象,构造方法中绑定要读取的数据源
        2.创建一个字节输出流的对象,构造方法中绑定要写如的目的地
        3.使用字节输入流对象中的read方法读取文佳
        4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
        5.释放资源
     文件的大小:54,007字节
     一次读写一个字节:894毫秒
     使用数组缓冲读取多个字节,写入多个字节:4毫秒
 */
public class CopyFile {
    public static void main(String[] args) throws Exception {
        long s = System.currentTimeMillis();
        //1.创建一个字节输入流对象,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("G:\\testIO\\p1.jpeg");
        //2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
        FileOutputStream fos = new FileOutputStream("p1.jpeg");
        //一次读取一个字节写入一个字节的方式
        //3.使用字节输入流对象中的方法read读取文件
        int len=0;
        while ((len=fis.read())!=-1){
            //4.使用字节输出流中的write,把读取到的字节写入到目的地文件中
            fos.write(len);
        }

        //优化
        //使用数组缓冲读取多个字节,写入多个字节
        /*byte[] buffer = new byte[1024];
        int len=0;
        //3.使用字节输入流对象中的方法read读取文件
        while ((len=fis.read(buffer))!=-1){
            //4.使用字节输出流中的write,把读取到的字节写入到目的地文件中
            fos.write(buffer,0,len);
        }*/
        //5.释放资源(先关闭写的,后关闭读的,如果写完了,肯定读完了)
        fos.close();
        fis.close();
        long e = System.currentTimeMillis();
        System.out.println("复制文件共耗时"+(e-s)+"毫秒");

    }
}

使用缓冲流技术

文件的大小:54,007字节
一次读写一个字节:4毫秒
使用数组缓冲读取多个字节,写入多个字节:1毫秒

/*
    文件复制的步骤
        1.创建字节缓冲输入流对象,构造方法中传递字节输入流
        2.创建字节缓冲输出流对象,构造方法中传递字节输出流
        3.使用字节缓冲输入流对象中的方法read,读取文件
        4.使用字节缓冲输出流中的方法write,把读取的数据写入到内部缓冲区中
        5.释放资源(会先把缓冲区数据,刷新到文件中)

      文件的大小:54,007字节
     一次读写一个字节:4毫秒
     使用数组缓冲读取多个字节,写入多个字节:1毫秒
 */
public class BufferedCopyFile {
    public static void main(String[] args) throws Exception {
        long s = System.currentTimeMillis();
        //1.创建字节缓冲输入流对象,构造方法中传递字节输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("G:\\testIO\\p1.jpeg"));
        //2.创建字节缓冲输出流对象,构造方法中传递字节输出流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("p3.jpeg"));
        //3.使用字节缓冲输入流对象中的方法read,读取文件
        //一次读取一个字节写入一个字节的方式
        /*int len = 0;
        while ((len=bis.read())!=0){
            bos.write(len);
        }*/
        //使用数组缓冲读取多个字节,写入多个字节
        byte[] buffer = new byte[1024];
        int len=0;
        while ((len=bis.read(buffer))!=-1){
            bos.write(buffer,0,len);
        }
        bos.close();
        bis.close();

        long e = System.currentTimeMillis();
        System.out.println("复制文件共耗时"+(e-s)+"毫秒");
    }
}

6.3 字符缓冲流

字节缓冲输出流

构造方法

  • BufferedWriter(Writer out):创建使用默认大小的输出缓冲区的缓冲字符输出流。
  • BufferedWriter(Writer out, int sz):创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。

参数

Writer out:字符输出流
我们可以传递FileWriter,缓冲流会给FileWriter增加一个缓冲区,提高FileWriter的写入效率
int sz:指定缓冲区的大小,不写默认大小

特有方法

void newLine():写一行行分隔符。会根据不同的操作系统,获取不同的行分隔符

换行:换行符号:
windows:\r\n
linux:/n
mac:/r

使用步骤:

1.创建字符缓冲输出流对象,构造方法中传递字符输出流
2.调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
3.调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
4.释放资源

/*
java.io.BufferedWriter extends Writer
BufferedWriter:字符缓冲输出流
继承自父类的共性成员方法:
        - `void write(int c)` 写入单个字符。
        - `void write(char[] cbuf) `写入字符数组。
        - `abstract  void write(char[] cbuf, int off, int len) `写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        - `void write(String str) `写入字符串。
        - `void write(String str, int off, int len)` 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
        - `void flush() `刷新该流的缓冲。
        - `void close()` 关闭此流,但要先刷新它。
构造方法:
    BufferedWriter(Writer out) 创建使用默认大小的输出缓冲区的缓冲字符输出流。
    BufferedWriter(Writer out, int sz) 创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。
    参数:   Writer out:字符输出流
            我们可以传递FileWriter,缓冲流会给FileWriter增加一个缓冲区,提高FileWriter的写入效率
            int sz:指定缓冲区的大小,不写默认大小
    特有的成员方法:
        void newLine():写一行行分隔符。会根据不同的操作系统,获取不同的行分隔符
        换行:换行符号:
        windows:\r\n
        linux:/n
        mac:/r
    使用步骤:
        1.创建字符缓冲输出流对象,构造方法中传递字符输出流
        2.调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
        3.调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
        4.释放资源
 */
public class Demo03 {
    public static void main(String[] args) throws IOException {
        //1.创建字符缓冲输出流对象,构造方法中传递字符输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
        // 2.调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
        for (int i = 0; i < 10; i++) {
            bw.write("City");
            bw.newLine();
        }
        // 3.调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
        bw.flush();
        //4.释放资源
        bw.close();

    }
}

字节缓冲输入流

构造方法

  • BufferedReader(Reader in):创建使用默认大小的输入缓冲区的缓冲字符输入流。
  • BufferedReader(Reader in, int sz):创建使用指定大小的输入缓冲区的缓冲字符输入流。

参数

Reader in:字节输入流
我们可以传递FileReader,缓冲流会给FileReader增加一个缓冲区,提高FileReader的读取效率

特有的成员方法

String readLine():读取一个文本行,读取一行数据。

行的终止符号:通过下列字符之一即可认为某行已终止:换行('\n'),回车('\r')或者回车后直接跟着换行(\r\n)。
返回值:包含行的内容的字符串,不包括任何行终止字符,如果已达到流的末尾,则为null

使用步骤

1.创建字符缓冲输入流对象,构造方法中传递字符输入流
2.使用字符缓冲输入流对象中的方法read/readLine读取文本
3.释放资源

/*
    java.io.BufferedReader extends Reader

    继承自父类的共性成员方法
        int read() :读一个字符
        int read(char[] cbuf) :将字符读入数组。
        abstract void close() :关闭流并释放与之相关联的任何系统资源。
    构造方法:
        BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。
        BufferedReader(Reader in, int sz) 创建使用指定大小的输入缓冲区的缓冲字符输入流。
     参数:
        Reader in:字节输入流
        我们可以传递FileReader,缓冲流会给FileReader增加一个缓冲区,提高FileReader的读取效率
     特有的成员方法:
        String readLine() :读取一个文本行,读取一行数据。
        行的终止符号:通过下列字符之一即可认为某行已终止:换行('\n'),回车('\r')或者回车后直接跟着换行(\r\n)。
        返回值:包含行的内容的字符串,不包括任何行终止字符,如果已达到流的末尾,则为null
     使用步骤;
        1.创建字符缓冲输入流对象,构造方法中传递字符输入流
        2.使用字符缓冲输入流对象中的方法read/readLine读取文本
        3.释放资源

 */
public class Demo04 {
    public static void main(String[] args) throws IOException {
        //1.创建字符缓冲输入流对象,构造方法中传递字符输入流
        BufferedReader br = new BufferedReader(new FileReader("b.txt"));
        // 2.使用字符缓冲输入流对象中的方法read/readLine读取文本
        /*String line = br.readLine();
        System.out.println(line);
        line = br.readLine();
        System.out.println(line);
        line = br.readLine();
        System.out.println(line);
        line = br.readLine();
        System.out.println(line);*/
        /*
            使用循环优化
         */
        /*String line;
        while ((line=br.readLine())!=null){
            System.out.println(line);
        }*/
        char[] cs = new char[5];
        int len=0;
        while ((len=br.read(cs))!=-1){
            System.out.print(new String(cs,0,len));
        }
        //3.释放资源
        br.close();
    }
}

6.4 练习对文本内容进行排序

分析

1.创建一个HashMap集合对象,可以:存储每行文本的序号(1,2,3,....);value;存储每行的文本
2.创建字符缓冲输入流对象,构造方法中绑定字符输入流
3.创建字符缓冲输出流对象,构造方法中绑定字符输出流
4.使用字符缓冲输入流的方法readLine,逐行读取文本
5.对读取到的文本进行切割,获取行中的序号和文本内容
6.把切割好的序号和文本的内容存储到HashMap集合中
7.遍历HashMap结合,获取每一个键值对
8.把每一个键值对,拼接为一个文本行
9.把拼接好的文本,使用字符缓冲输出流的方法writr,写入到文件中
10.释放资源

*
    练习:对文本内容进行排序
    分析:
        1.创建一个HashMap集合对象,可以:存储每行文本的序号(1,2,3,....);value;存储每行的文本
        2.创建字符缓冲输入流对象,构造方法中绑定字符输入流
        3.创建字符缓冲输出流对象,构造方法中绑定字符输出流
        4.使用字符缓冲输入流的方法readLine,逐行读取文本
        5.对读取到的文本进行切割,获取行中的序号和文本内容
        6.把切割好的序号和文本的内容存储到HashMap集合中
        7.遍历HashMap结合,获取每一个键值对
        8.把每一个键值对,拼接为一个文本行
        9.把拼接好的文本,使用字符缓冲输出流的方法writr,写入到文件中
        10.释放资源
 */
public class Demo05 {
    public static void main(String[] args) throws IOException {
        // 1.创建一个HashMap集合对象,可以:存储每行文本的序号(1,2,3,....);value;存储每行的文本
        HashMap<String, String> map = new HashMap<>();
        //2.创建字符缓冲输入流对象,构造方法中绑定字符输入流
        BufferedReader br = new BufferedReader(new FileReader("G:\\testIO\\in.txt"));
        //3.创建字符缓冲输出流对象,构造方法中绑定字符输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("G:\\testIO\\out.txt"));
        //4.使用字符缓冲输入流的方法readLine,逐行读取文本
        String line;
        while ((line=br.readLine())!=null){
            //5.对读取到的文本进行切割,获取行中的序号和文本内容
            String[] arr = line.split("\\.");
            //6.把切割好的序号和文本的内容存储到HashMap集合中
            map.put(arr[0],arr[1]);
        }
        // 7.遍历HashMap结合,获取每一个键值对
        for (String key : map.keySet()) {
            String value=map.get(key);
            //8.把每一个键值对,拼接为一个文本行
            line = key+"."+value;
            //9.把拼接好的文本,使用字符缓冲输出流的方法writr,写入到文件中
            bw.write(line);
            bw.newLine();
        }
        //10.释放资源
        bw.close();
        br.close();

    }
}

7、 转换流

7.1 字符编码和字符集

字符编码

计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。

编码:字符(能看懂的)--字节(看不懂的)

解码:字节(看不懂的)-->字符(能看懂的)

  • 字符编码Character Encoding : 就是一套自然语言的字符与二进制数之间的对应规则。

    编码表:生活中文字和计算机中二进制的对应规则

字符集

  • 字符集 Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。

计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。

1_chaeset

可见,当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们最终要关心的。

  • ASCII字符集
    • ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
    • 基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits)表示一个字符,共256字符,方便支持欧洲常用字符。
  • ISO-8859-1字符集
    • 拉丁码表,别名Latin-1,用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。
    • ISO-8859-1使用单字节编码,兼容ASCII编码。
  • GBxxx字符集
    • GB就是国标的意思,是为了显示中文而设计的一套字符集。
    • GB2312:简体中文码表。一个小于127的字符的意义与原来相同。但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名们都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。
    • GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。
    • GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。
  • Unicode字符集
    • Unicode编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。
    • 它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF-32。最为常用的UTF-8编码。
    • UTF-8编码,可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。所以,我们开发Web应用,也要使用UTF-8编码。它使用一至四个字节为每个字符编码,编码规则:
      1. 128个US-ASCII字符,只需一个字节编码。
      2. 拉丁文等字符,需要二个字节编码。
      3. 大部分常用字(含中文),使用三个字节编码。
      4. 其他极少使用的Unicode辅助字符,使用四字节编码。

7.2 编码引出的问题

在IDEA中,使用FileReader 读取项目中的文本文件。由于IDEA的设置,都是默认的UTF-8编码,所以没有任何问题。但是,当读取Windows系统中创建的文本文件时,由于Windows系统的默认是GBK编码,就会出现乱码。

/*
    FileReader可以读取IDE默认编码格式(UTF-8)的文件
    FileReader读取系统默认编码(中文GBK)会产生乱码
 */
public class Demo06 {
    public static void main(String[] args) throws Exception {
        FileReader fr = new FileReader("G:\\testIO\\GBK.txt");
        int len=0;
        while ((len=fr.read())!=-1){
            System.out.print((char) len);
        }
        
        fr.close();
        //�������
    }
}

那么如何读取GBK编码的文件呢? ? ?

转换流的原理

2_charset

转换流是字节与字符间的桥梁!

2_zhuanhuan

7.3 OutputStreamWriter类

转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。

构造方法

  • OutputStreamWriter(OutputStream out):创建一个使用默认字符编码的OutputStreamWriter。
  • OutputStreamWriter(OutputStream out, String charsetName):创建一个使用指定字符集的OutputStreamWriter。

参数

OutputStream out:字节输出流,可以用来写转换之后的字节到文件中
String charsetName:指定的编码表名称,不区分大小写,可以是utf-8,gbk....,不指定使用UTF-8

使用步骤

1.创建OutputStreamWriter对象,构造方法中传递字节输出流和制定的编码表名称
2.使用OutputStreamWriter对象中的方法write,吧字符转换为字节存储到缓冲区中(编码)
3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
4.释放资源

/*
java.io.OutputStreamWriter  extends Writer

OutputStreamWriter:是字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节.(编码:把能看懂的变成看不懂的)
继承自父类的共性成员方法:
        - `void write(int c)` 写入单个字符。
        - `void write(char[] cbuf) `写入字符数组。
        - `abstract  void write(char[] cbuf, int off, int len) `写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
        - `void write(String str) `写入字符串。
        - `void write(String str, int off, int len)` 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
        - `void flush() `刷新该流的缓冲。
        - `void close()` 关闭此流,但要先刷新它。
构造方法:
    OutputStreamWriter(OutputStream out)创建一个使用默认字符编码的OutputStreamWriter。
    OutputStreamWriter(OutputStream out, String charsetName) :创建一个使用指定字符集的OutputStreamWriter。
参数:
    OutputStream out:字节输出流,可以用来写转换之后的字节到文件中
    String charsetName:指定的编码表名称,不区分大小写,可以是utf-8,gbk....,不指定使用UTF-8
使用步骤
    1.创建OutputStreamWriter对象,构造方法中传递字节输出流和制定的编码表名称
    2.使用OutputStreamWriter对象中的方法write,吧字符转换为字节存储到缓冲区中(编码)
    3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
    4.释放资源
 */
public class Demo01 {
    public static void main(String[] args) throws IOException {
        //write_utf_8();
        write_GBK();

    }
    /*
    使用转换流写GBK格式的文件
     */

    private static void write_GBK() throws IOException {
        // 1.创建OutputStreamWriter对象,构造方法中传递字节输出流和制定的编码表名称
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("GBK.txt"),"GBK");
        //2.使用OutputStreamWriter对象中的方法write,吧字符转换为字节存储到缓冲区中(编码)
        osw.write("你好");
        //3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        osw.flush();
        // 4.释放资源
        osw.close();
    }

    /*
    使用转换流写UTF-8格式的文件
     */
    private static void write_utf_8() throws IOException {
        // 1.创建OutputStreamWriter对象,构造方法中传递字节输出流和制定的编码表名称
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("UTF-8.txt"));
        //2.使用OutputStreamWriter对象中的方法write,吧字符转换为字节存储到缓冲区中(编码)
        osw.write("你好");
        //3.使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
        osw.flush();
       // 4.释放资源
        osw.close();

    }
}

7.3 InputStreamReader类

InputStreamReader:是从字节流到字符流的桥:它读取字节,并使用指定的charset将其解码为字符 。(解码:吧看不懂的变成看的懂的).

构造方法

  • InputStreamReader(InputStream in):创建一个使用默认字符集的InputStreamReader。
  • InputStreamReader(InputStream in, String charsetName):创建一个使用命名字符集的InputStreamReader。

参数:

InputStream in:字节输入流,用来读取文件中保存的字节
String charsetName:指定的编码表名称,不区分大小写,可以是utf-8,gbk....,不指定使用UTF-8

使用步骤

1.创建InputStreamReader对象,构造方法中传递字节输入流和制定的编码表名称
2.使用InputStreamReader对象中的方法read读取文件
3.释放资源

注意事项

构造方法中指定的编码表名称要和文件的编码相同,否则会发生乱码

/*
    java.io.InputStreamReader extends Reader
    InputStreamReader:是从字节流到字符流的桥:它读取字节,并使用指定的charset将其解码为字符 。(解码:吧看不懂的变成看的懂的)
    继承父类的共性成员方法:
        int read() :读一个字符
        int read(char[] cbuf) :将字符读入数组。
        abstract void close() :关闭流并释放与之相关联的任何系统资源。
     构造方法:
     InputStreamReader(InputStream in) :创建一个使用默认字符集的InputStreamReader。
     InputStreamReader(InputStream in, String charsetName) :创建一个使用命名字符集的InputStreamReader。
     参数:
     InputStream in:字节输入流,用来读取文件中保存的字节
     String charsetName:指定的编码表名称,不区分大小写,可以是utf-8,gbk....,不指定使用UTF-8
     使用步骤:
     1.创建InputStreamReader对象,构造方法中传递字节输入流和制定的编码表名称
     2.使用InputStreamReader对象中的方法read读取文件
     3.释放资源
     注意事项
     构造方法中指定的编码表名称要和文件的编码相同,否则会发生乱码
 */
public class Demo02 {
    public static void main(String[] args) throws IOException {
       // read_utf_8();
        read_GBK();
    }
    /*
    使用InputStreamReader读取GBK格式的文件
     */

    private static void read_GBK() throws IOException {
        // 1.创建InputStreamReader对象,构造方法中传递字节输入流和制定的编码表名称
        InputStreamReader isr = new InputStreamReader(new FileInputStream("GBK.txt"), "GBK");
        //2.使用InputStreamReader对象中的方法read读取文件
        int len=0;
        while((len=isr.read())!=-1){
            System.out.print((char)len);
        }
        //3.释放资源
        isr.close();
    }
    /*
    使用InputStreamReader读取UTF-8格式的文件
     */

    private static void read_utf_8() throws IOException {
        // 1.创建InputStreamReader对象,构造方法中传递字节输入流和制定的编码表名称
        InputStreamReader isr = new InputStreamReader(new FileInputStream("UTF-8.txt"), "UTF-8");
        //2.使用InputStreamReader对象中的方法read读取文件
        int len=0;
        while((len=isr.read())!=-1){
            System.out.print((char)len);
        }
        //3.释放资源
        isr.close();
    }
}

7.4 联系:转换文件编码

将GBK编码的文本文件,转换为UTF-8编码的文本文件。

分析

  1. 指定GBK编码的转换流,读取文本文件。
  2. 使用UTF-8编码的转换流,写出文本文件
/*
    将GBK编码的文件转换为UTF-8编码的文本文件
    分析:
        1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表示名称GBK
        2.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称UTF-8
        3.使用InputStreamReader对象中的方法read读取文件
        4.使用OutputStreamWriter对象中的方法Write,吧读取的数据写入到文件中。
        5.释放资源。
 */
public class ReverseStream {
    public static void main(String[] args) throws IOException {
        //1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表示名称GBK
        InputStreamReader isr = new InputStreamReader(new FileInputStream("G:\\testIO\\GBK.txt"), "GBK");
        // 2.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称UTF-8
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("G:\\testIO\\UTF-8.txt"));
        //3.使用InputStreamReader对象中的方法read读取文件
        int len=0;
        while((len=isr.read())!=-1){
            //4.使用OutputStreamWriter对象中的方法Write,吧读取的数据写入到文件中。
            osw.write(len);
        }
       // 5.释放资源。
        osw.close();
        isr.close();
    }
}

8、 序列化

8.1 概述

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据对象的类型对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化对象的数据对象的类型对象中存储的数据信息,都可以用来在内存中创建对象。看图理解序列化:

3_xvliehua

原理图

2_xvliehua

8.2 ObjectOutputStream类

java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。

构造方法

  • ObjectOutputStream(OutputStream out):创建一个指定OutputStream的ObjectOutputStream。

序列化操作

  1. 一个对象要想序列化,必须满足两个条件:
  • 该类必须实现java.io.Serializable 接口,Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
  • 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。

序列和反序列化的时候会抛出java.io.NotSerializableException没有序列化异常
类通过java.io.Serializable接口以启用其序列化功能,未实现此接口的类将无法使其任何状态序列化或反序列化
Serializable接口也叫标记性接口
要进行序列化和反序列化的时候,就会检测类上是否有这个标记
有:就可以序列化和反序列化
没有:就会抛出NotSerializableException异常

Person类

/*
    序列和反序列化的时候会抛出java.io.NotSerializableException没有序列化异常
    类通过java.io.Serializable接口以启用其序列化功能,未实现此接口的类将无法使其任何状态序列化或反序列化
    Serializable接口也叫标记性接口
        要进行序列化和反序列化的时候,就会检测类上是否有这个标记
            有:就可以序列化和反序列化
            没有:就会抛出NotSerializableException异常

 */
public class Person implements Serializable {
    private String name;
    private int age;

    public Person() {
    }

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

    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;
    }
}

主方法

/*
java.io.ObjectOutputStream extends OutputStream
ObjectOutputStream:对象的序列化流
作用:把对象以流的方式写入到文件中
构造方法:
    ObjectOutputStream(OutputStream out) 创建一个写入指定的OutputStream的ObjectOutputStream。
    OutputStream out:字节输出流
特有的成员方法:
    void writeObject(Object obj) :将指定的对象写入ObjectOutputStream。
使用步骤:
    1.创建ObjectOutputStream对象,构造方法中传递字节输出流。
    2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
    3.释放资源
 */
public class Demo01 {
    public static void main(String[] args) throws IOException {
       //1.创建ObjectOutputStream对象,构造方法中传递字节输出流。
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
        //2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
        oos.writeObject(new Person("CityD",21));
        //3.释放资源
        oos.close();
    }
}

8.3 ObjectInputStream类

ObjectInputStream:对象的反序列化流
作用:把文件中保存的对象,以流的方式读取出来使用

构造方法

ObjectInputStream(InputStream in) :创建从指定的InputStream读取的ObjectInputStream。

参数

InputStream in:字节输入流

特有的成员方法

Object readObject() :从ObjectInputStream读取一个对象。

使用步骤

1.创建ObjectInputStream对象,构造方法中传递字节输入流
2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件。
3.释放资源
4.使用读取出来的对象(d打印)

反序列化操作

ClassNotFoundException异常

readObject() 方法声明抛出了ClassNotFoundException(class文件找不到)
当不存在对象的class文件时抛出此异常
反序列化前提:
1.类必须实现Serializable
2.必须存在类对应的class文件

/*
java.io.ObjectInputStream extends InputStream
ObjectInputStream:对象的反序列化流
作用:把文件中保存的对象,以流的方式读取出来使用

构造方法:
    ObjectInputStream(InputStream in) :创建从指定的InputStream读取的ObjectInputStream。
    参数:
        InputStream in:字节输入流
特有的成员方法:
    Object readObject() :从ObjectInputStream读取一个对象。
使用步骤:
    1.创建ObjectInputStream对象,构造方法中传递字节输入流
    2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件。
    3.释放资源
    4.使用读取出来的对象(d打印)

    readObject() 方法声明抛出了ClassNotFoundException(class文件找不到)
    当不存在对象的class文件时抛出此异常
    反序列化前提:
        1.类必须实现Serializable
        2.必须存在类对应的class文件
 */
public class Demo02 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.创建ObjectInputStream对象,构造方法中传递字节输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"));
        //2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件。
        Person person = (Person) ois.readObject();
        //3.释放资源
        ois.close();
        //4.使用读取出来的对象(d打印)
        System.out.println(person);

    }
}

InvalidClassException异常

另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。发生这个异

常的原因如下:

  • 该类的序列版本号与从流中读取的类描述符的版本号不匹配
  • 该类包含未知数据类型
  • 该类没有可访问的无参数构造方法

Serializable 接口给需要序列化的类,提供了一个序列版本号。serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。

Serializable

8.4 transient 关键字

  • static关键字:静态关键字

静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
被static修饰的成员变量不能被序列化,序列化的都是对象

private static int age;
oos.writeObject(new Person("CityD",21));
Person person = (Person) ois.readObject();
Person

  • transient 关键字:瞬态关键字

被transient修饰的成员变量,不能被序列化

private transient int age;
oos.writeObject(new Person("CityD",21));
Person person = (Person) ois.readObject();
Person

8.5 练习:序列化集合

  1. 把若干学生对象 ,保存到集合中。
  2. 把集合序列化。
  3. 反序列化读取时,只需要读取一次,转换为集合类型。
  4. 遍历集合,可以打印所有的学生信息

分析

  1. 定义一个存储Person对象的的ArrayList集合
  2. 往ArrayList集合中存储Person对象
  3. 创建一个序列化流ObjectOutputStream对象
  4. 使用ObjectOutputStream对象中的方法writeObject对集合进行序列化
  5. 创建一个序列化流ObjectInputStream对象
  6. 使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
  7. 把Object类型的集合转换为ArrayList的类型
  8. 遍历ArrayList集合
  9. 释放资源
/*
    联系:序列化集合
        当我们想在文件中保存多个对象的时候
        可以把多个对象存储到一个集合中
        对集合进行序列化和反序列化
    分析:
        1.定义一个存储Person对象的的ArrayList集合
        2.往ArrayList集合中存储Person对象
        3.创建一个序列化流ObjectOutputStream对象
        4.使用ObjectOutputStream对象中的方法writeObject对集合进行序列化
        5.创建一个序列化流ObjectInputStream对象
        6.使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
        7.把Object类型的集合转换为ArrayList的类型
        8.遍历ArrayList集合
        9.释放资源
 */
public class Demo03 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.定义一个存储Person对象的的ArrayList集合
        ArrayList<Person> list =  new ArrayList<>();
        //2.往ArrayList集合中存储Person对象
        list.add(new Person("张三",15));
        list.add(new Person("李四",16));
        list.add(new Person("王五",45));
        //3.创建一个序列化流ObjectOutputStream对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));
        // 4.使用ObjectOutputStream对象中的方法writeObject对集合进行序列化
        oos.writeObject(list);
        //5.创建一个序列化流ObjectInputStream对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("list.txt"));
        //6.使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
        Object o = ois.readObject();
        //7.把Object类型的集合转换为ArrayList的类型
        ArrayList<Person> list2 = (ArrayList<Person>) o;
        //8.遍历ArrayList集合
        for (Person person : list2) {
            System.out.println(person);
        }
        // 9.释放资源
        ois.close();
        oos.close();
    }
}

9、打印流

9.1 概述

平时我们在控制台打印输出,是调用print方法和println方法完成的,这两个方法都来自于java.io.PrintStream类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。

PrintStream特点

1.只负责数据的输出,不负责数据的读取
2.与其他输出流不同,PrintStream永远不会抛出IOException
3.有特有的方法,print,println
void print(任意类型的值)
void println(任意类型的值并换行)

构造方法

  • PrintStream(File file):输出的目的地是一个文件
  • PrintStream(OutputStream out):输出的目的地是一个字节输出流
  • PrintStream(String fileName):输出的目的地是一个文件路径

注意:如果使用集成父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97

/*
    java.io.PrintStream :打印流
        PrintStream:为其他输出流添加了功能,使他们能够方便的打印出各种数据值表示形式
    PrintStream特点:
        1.只负责数据的输出,不负责数据的读取
        2.与其他输出流不同,PrintStream永远不会抛出IOException
        3.有特有的方法,print,println
            void print(任意类型的值)
            void println(任意类型的值并换行)
     构造方法:
        1.PrintStream(File file):输出的目的地是一个文件
        2.PrintStream(OutputStream out):输出的目的地是一个字节输出流
        3.PrintStream(String fileName) :输出的目的地是一个文件路径
     java.io.PrintStream extends OutputStream
     继承自父类的成员方法
    * `public void close()` :关闭此输出流并释放与此流相关联的任何系统资源。
    * `public void flush() ` :刷新此输出流并强制任何缓冲的输出字节被写出。
    * `public void write(byte[] b)`:将 b.length字节从指定的字节数组写入此输出流。
    * `public void write(byte[] b, int off, int len)` :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
    * `public abstract void write(int b)` :将指定的字节输出流。
    注意:如果使用集成父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
         如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
 */
public class Demo01 {
    public static void main(String[] args) throws FileNotFoundException {
        //创建打印流PrintStream对象,构造方法中绑定要输出的目的地
        PrintStream ps = new PrintStream("print.txt");
        //如果使用集成父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
        ps.write(97);
        // 如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
        ps.println(97);
        ps.println(8.8);
        ps.println('a');
        ps.println("hello world");
        //释放资源
        ps.close();
    }
}

可以改变输出语句的目的地(打印流的流向)
输出语句,默认在控制台输出
使用System.setOut()方法改变输出语句的目的地改为参数中传递的打印流的目的地
static void setOut(PrintStream out)
重新分配"标准"输出流

    /*
    可以改变输出语句的目的地(打印流的流向)
    输出语句,默认在控制台输出
    使用System.setOut()方法改变输出语句的目的地改为参数中传递的打印流的目的地
        static void setOut(PrintStream out)
            重新分配"标准"输出流
     */
    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("我在控制台输出");
        PrintStream ps = new PrintStream("i.txt");
        System.setOut(ps);
        System.out.println("我在i.txt中输出");
        ps.close();
    }
}
posted @   CityD  阅读(183)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
点击右上角即可分享
微信分享提示