十九、缓冲流、转换流、序列化流、打印流

1、缓冲流

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

  • 字节缓冲流: BufferedInputStream , BufferedOutputStream
  • 字符缓冲流: BufferedReader , BufferedWriter

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

(1)字节缓冲流
java.io.BufferedOutputStream extends OutputStream
BufferedOutputStream:字节缓冲输出流

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

构造方法:

  • BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
  • BufferedInputStream(InputStream in) 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。

代码实现:

public void testBufferedStream() throws IOException {

        // 创建流对象
        String src = "E:\\javatest文件\\src\\1.png";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
        String des = "E:\\javatest文件\\des\\bis.png";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(des));

        //int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
        byte[] bytes = new byte[1024];//存储每次读取的数据
        int len = 0; //记录每次读取的有效字节个数
        while ((len = bis.read(bytes)) != -1) {
            bos.write(bytes, 0, len);
        }

        //释放资源
        bos.close();
        bis.close();
    }
(2)字符缓冲流
java.io.BufferedWriter extends Writer
BufferedWriter:字符缓冲输出流

java.io.BufferedReader extends Reader
BufferedReader:字符缓冲输入流

构造方法:

  • BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流。
  • BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流。

特有方法:

  • BufferedWriter: void newLine() 写入一个行分隔符。会根据不同的操作系统,获取不同的行分隔符。
  • BufferedReader: String readLine() 读取一个文本行。读取一行数据行的终止符号:通过换行 ('\n')、回车 ('\r') 或回车后直接跟着换行(\r\n)字符之一即可认为某行已终止。

代码实现:

/*
3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉 以咨之,然后施行,必得裨补阙漏,有所广益。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不 宜偏私,使内外异法也。 
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外 者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以 塞忠谏之路也。
*/
public void testBuffered() throws IOException {
        //1.创建一个HashMap集合对象,可以:存储每行文本的序号(1,2,3,..);value:存储每行的文本
        HashMap<String, String> map = new HashMap<>();
        //2.创建字符缓冲输入流对象,构造方法中绑定字符输入流
        String src = "E:\\javatest文件\\src\\in.txt";
        BufferedReader br = new BufferedReader(new FileReader(src));
        //3.创建字符缓冲输出流对象,构造方法中绑定字符输出流
        String des = "E:\\javatest文件\\des\\out.txt";
        BufferedWriter bw = new BufferedWriter(new FileWriter(des));
        //4.使用字符缓冲输入流中的方法readline,逐行读取文本
        String line;
        while ((line = br.readLine()) != null) {
            //5.对读取到的文本进行切割,获取行中的序号和文本内容
            String[] arr = line.split("\\.");
            //6.把切割好的序号和文本的内容存储到HashMap集合中(key序号是有序的,会自动排序1,2,3,4..)
            map.put(arr[0], arr[1]);
        }

        //7.遍历HashMap集合,获取每一个键值对
        for (String key : map.keySet()) {
            String value = map.get(key);
            //8.把每一个键值对,拼接为一个文本行
            line = key + "." + value;
            //9.把拼接好的文本,使用字符缓冲输出流中的方法write,写入到文件中
            bw.write(line);
            bw.newLine();//写换行
        }
        //10.释放资源
        bw.close();
        br.close();
    }

2、转换流

(1)字符编码和字符集
  • 字符编码 Character Encoding:就是一套自然语言的字符与二进制数之间的对应规则。
  • 字符集 Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符 号、数字等。

常见字符 集有ASCII字符集、GBK字符集、Unicode字符集等。

  • ASCII码表:0~ 9(48~ 57)、A~ Z(65~ 90)、a~ z(97~ 122)
  • GBK:最常用的中文码表。是在GB2312(简体中文码表)标准基础上的扩展规范,使用了双字节编码方案。
  • UTF-8:它使用一至四个字节为每个字符编码。
    编码规则:①128个US-ASCII字符,只需一个字节编码。 ②拉丁文等字符,需要二个字节编码。 ③大部分常用字(含中文),使用三个字节编码。④其他极少使用的Unicode辅助字符,使用四字节编码。
(2)InputStreamReader和OutputStreamWriter
java.io.OutputStreamWriter extends Writer
OutputStreamWriter:是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。(编码:把能看懂的变成看不懂)

java.io.InputStreamReader extends Reader
InputStreamReader:是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。(解码:把看不懂的变成能看懂的)

构造方法:

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

小贴士:String charsetName表示指定的编码表名称,不区分大小写,可以是utf-8/UTF-8、gbk/GBK、...不指定默认使用UTF-8。

代码实现:

/*将GBK编码的文本文件,转换为UTF-8编码的文本文件。*/
public void testReverseStream() throws IOException {
       //1.创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称GBK
        String src = "E:\\javatest文件\\src\\gbk.txt";
        InputStreamReader isr = new InputStreamReader(new FileInputStream(src), "GBK");
        //2.创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称UTF-8
        String des = "E:\\javatest文件\\des\\utf8.txt";
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(des), "UTF-8");
        //3.使用InputStreamReader对象中的方法read读取文件
        char[] cs = new char[1024];//存储读取到的多个字符
        int len = 0;//记录的是每次读取的有效字符个数
        while ((len = isr.read(cs)) != -1) {
            //4.使用OutputStreamWriter对象中的方法write,把读取的数据写入到文件中
            osw.write(cs, 0, len);
        }
        //5.释放资源
        osw.close();
        isr.close();
    }

3、序列化流

01- 看图理解序列化.jpg
(1)ObjectOutputStream和ObjectInputStream
java.io.ObjectOutputStream extends OutputStream
ObjectOutputStream(对象的序列化流):把对象以流的方式写入到文件中保存。

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

构造方法:

  • ObjectOutputStream(OutputStream out) 创建写入指定 OutputStream 的 ObjectOutputStream。
  • ObjectInputStream(InputStream in) 创建从指定 InputStream 读取的 ObjectInputStream。

特有方法:

  • ObjectOutputStream:void writeObject(Object obj) 将指定的对象写入ObjectOutputStream。
  • ObjectInputStream:Object readObject() 从ObjectInputStream读取对象。

序列化操作:
一个对象要想序列化,必须满足两个条件:

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

反序列化操作:
对于JVM可以反序列化对象,它必须是能够找到class文件的类。
如果找不到该类的class文件,则抛出一个 ClassNotFoundException 异常。
如果能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操 作也会失败,抛出一个 InvalidClassException 异常。
发生这个异常的原因如下:

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

小贴士:
Serializable 接口给需要序列化的类,提供了一个序列版本号。 serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
被static修饰的成员变量不能被序列化的,序列化的都是对象。
被transient修饰成员变量,不能被序列化。

02- 序列号冲突异常的原理和解决方案.png

代码实现:

/*将GBK编码的文本文件,转换为UTF-8编码的文本文件。*/
public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    //被transient修饰成员变量,不能被序列化
    private transient String sex;
    private int age;

    public Person() {
    }

    public Person(String name, String sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
    //省略toString方法和get、set方法
}

public void testObjectStream() throws IOException, ClassNotFoundException {
        //1.定义一个存储Person对象的ArrayList集合
        ArrayList<Person> list = new ArrayList<>();
        //2.往ArrayList集合中存储Person对象
        list.add(new Person("张三", "男", 18));
        list.add(new Person("李四", "男", 19));
        list.add(new Person("王五", "男", 20));
        //3.创建一个序列化流ObjectOutputStream对象
        String pathName = "E:\\javatest文件\\obj.txt";
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(pathName));
        //4.使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
        oos.writeObject(list);
        //5.创建一个反序列化ObjectInputStream对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(pathName));
        //6.使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
        Object o = ois.readObject();
        //7.把Object类型的集合转换为ArrayList类型
        ArrayList<Person> list2 = (ArrayList<Person>) o;
        //8.遍历ArrayList集合
        for (Person p : list2) {
            System.out.println(p);
            //打印输出:
            //Person{name='张三', sex='null', age=18}
            //Person{name='李四', sex='null', age=19}
            //Person{name='王五', sex='null', age=20}
        }
        //9.释放资源
        ois.close();
        oos.close();
    }

4、打印流

(1)PrintStream
java.io.PrintStream extends OutputStream
PrintStream(打印流):为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。

构造方法:

  • PrintStream(File file):输出的目的地是一个文件
  • PrintStream(OutputStream out):输出的目的地是一个字节输出流。
  • InputStreamReader(InputStream in) 创建一个使用默认字符集的 InputStreamReader。
  • PrintStream(String fileName):输出的目的地是一个文件路径。

改变打印流向:
System.out 就是 PrintStream 类型的,只不过它的流向是系统规定的,打印在控制台上。既然是流对象, 我们就可以改变它的流向。
使用System.setOut方法改变输出语句的目的地改为参数中传递的打印流的目的地:static void setOut(PrintStream out)重新分配“标准”输出流。

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

代码实现:

public void testPrintStream() throws IOException {

        //创建打印流PrintStream对象,构造方法中绑定要输出的目的地
        String pathName = "E:\\javatest文件\\print.txt";
        PrintStream ps = new PrintStream(pathName);

        //如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
        ps.write(97);
        ps.write("\r\n".getBytes());
        //如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
        ps.println(97);
        ps.println(8.8);
        ps.println('a');
        ps.println("HelloWorld");
        ps.println(true);

        System.out.println("在控制台输出");
        System.setOut(ps);//把输出语句的目的地改变为打印流的目的地
        System.out.println("在打印流的目的地中输出");

        //释放资源
        ps.close();
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容