Java Lecture 17 对象流、字节流、字符流、转换字符流和缓冲字符流

一、对象流(也是一种高级流)

对象流作用:进行对象的序列化和反序列化的操作

对象的序列化:将一个对象按照其结构转换成为一组字节的过程。

对象的反序列化:将一组字节(必须是序列化的一组字节)还原为一个对象的过程。

好处:有了对象流,我们可以很方便区读写任何Java对象

对象输入流:java.io.ObjectInputStream

对象输出流:java.io.ObjectOutputStream

对象如果需要序列化是需要为当前对象的模板类添加实现Serializable接口。

用一个例子来说明(待续):

人的模板类:

package io;
import java.io.Serializable;
import java.util.Arrays;
/**
* 人类模板
* 当一个类实现了可序列化接口时,最好显示的定义serialVersionUID序列化版本号
* 当对象输出流在进行对象的序列化时,会查看是否有显示的定义版本号,如果没有则会根据当前类结构自动
计算版本号,
* 只要当前类的结构发生变化,这个类的版本号就会发生改变,而版本号不一致,就会导致反序列化失效,所以
我们要固定版本号. */
public class Person implements Serializable { //固定当前类的版本号为1,创建的对象都是这个版本号 static final long serialVersionUID = 1L; private String name;
    private int age;
    private String gender;
    /*
* 当一个属性被transient修饰后,那么当进行对象序列化时,该属性的值会被忽略
* 当然,反序列化时,该属性只会采用默认值
* transient: 短暂的,转瞬即逝的 */
private transient String[] otherInfo;//其它信息
private int salary;//薪资
//1.在当前空白区域 alt + insert 或 右键 找到 Generate 选中 //选择 get and set 方法 全选生成
//重复操作1. 选择 toString
//重复操作1. 选择 第一个选择 生成构造方法 全选 生成。
public Person(String name, int age, String gender, String[] otherInfo, int
salary) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.otherInfo = otherInfo;
        this.salary = salary;
    }
@Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", otherInfo=" + Arrays.toString(otherInfo) +
                ", salary=" + salary +
                '}';
}
    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;
}
    public String getGender() {
        return gender;
}
    public void setGender(String gender) {
        this.gender = gender;
}
    public String[] getOtherInfo() {
        return otherInfo;
}
    public void setOtherInfo(String[] otherInfo) {
        this.otherInfo = otherInfo;
}
    public int getSalary() {
        return salary;
}
    public void setSalary(int salary) {
        this.salary = salary;
} }

OOS是如何将对象读入读出的呢?

本质上来说,就是把对象变成二进制编码传入和读出文件。

对象输出流的生动形象的例子(序列化):

对象输入流的生动形象的例子(反序列化):

注意:下面这代码里用到的Person类就是刚刚写的那个Person类,切记,要使用对象流读取或输出某个对象,这个对象的类必须implements了seriazable接口。

对象输出流的例子: 

package io;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/**
* 对象流
* java.io.ObjectOutputStream ObjectInputStream
* 对象流是一对高级流,在流链接中完成对象与字节的转换,即:对象序列化和反序列化,操作了有了他,就可
以轻松读写任何的java对象 */
public class OOSDemo {
    public static void main(String[] args) throws IOException {
//ctrl+alt+L 快速整理代码格式
String name = "陈子豪";
int age = 18;
String gender = "男";
String[] otherInfo = {"是一个对鹏哥有感情的男人", "来自合肥", "是一个帅哥", "广
大妇女之友"};
int salary = 10000;
} }
//快速生成变量名 ctrl+alt+V或alt+enter
Person p = new Person(name, age, gender, otherInfo, salary); 
System.out.println(p);
 //将当前Person对象p序列化(写入)到person.txt文件中 
//准备一个低级的文件字节输出流
FileOutputStream fos = new FileOutputStream("./person.txt"); 
//准备一个高级的对象输出流,将fos链接上oos流
ObjectOutputStream oos = new ObjectOutputStream(fos); 
//将对象p通过oos写入到person.txt文件中
oos.writeObject(p);
System.out.println("对象写出完毕!");
//将oos关闭
oos.close();

ctrl+alt+L 快速整理代码格式

快速生成变量名 ctrl+alt+V或alt+enter(做这一步的时候要把鼠标放在new Person(name, age, gender, otherInfo,  salary);的后面才可以)

对象输入流的例子:

记得readObject()不需要加参数,括号里面啥也没不需要,直接复制给某个参数即可。

package io;
import java.io.*;
public class OISDemo {
    public static void main(String[] args) throws IOException,
ClassNotFoundException { 
//创建一个低级的文件字节输入流
FileInputStream fis = new FileInputStream("./person.txt"); 
//创建一个高级的对象字节输入流
ObjectInputStream ois = new ObjectInputStream(fis); 
//使用ois将person.txt文件中存储的序列化对象反序列化(读取)出来 
//对于方法而言,不知道文件中的对象类型
//但是对于我们而言,知道文件中的对象类型,所以要声明真实类型 
Person p = (Person) ois.readObject();
System.out.println(p);
ois.close();

注意!如果序列化后的版本和反序列化的版本不相同,就会导致版本号也不同,从而报错。所以为了避免这种错误,我们可以选择固定版本号来解决。

具体方式:在类那里加一个属性,static final long serialVersionUID = xxL

xx可以是某个数字,由自己决定,这样就是固定版本号,就能保证每一次更改都能序列化和反序列化了,不会报版本号错误的问题了。

如果想在序列化对象时,忽略它某个属性的值(就是不重要的),可以在那个属性的前面加个transient关键字,放在访问修饰符后,减少资源开销。

那在反序列化对象时,这个属性的值就会变成默认的,比如这个属性是String的话,那反序列化后,这个属性就会自动被赋值为None。

二、字节流和字符流

所有的字符流都是高级流。

在Java中,根据处理的数据单位不同,分为字节流和字符流。

字节流: 一个字节(byte)一个字节的去读取, 或者写出
字符流: 一个字符一个字符的去读取, 或者写出

1. 字节流

字节流(stream):针对二进制文件(文本,图片,音频,视频...等)

InputStream(包含input都是输入流)

-- FileInputStream
-- BufferedInputStream

-- ObjectInputStream

OutputStream(包含output都是输出流)

-- FileOutputStream
-- BufferedOutputStream
-- ObjectOutputStream

2. 字符流

字符流(Reader,Writer):针对文本文件,读写容易发生乱码现象,在读写时最好指定编码集为utf-8

Reader(Reader结尾的都是字符输入流)

-- FileReader
-- BufferedReader
-- InputStreamReader

Writer(Writer结尾的都是字符输出流)

-- FileWriter
-- BufferedWriter
-- OutputStreamWriter
-- PrintWriter/PrintStream

3. 转换字符流

3.1 OutputStreamWriter

java IO将流按照读写的单位分为字节流和字符流

java.io.InputStream和OutputStream是所有字节流的超类,读写的最小单位是字节

java.io.Reader和Writer是所有字符流的超类,读写的最小单位是字符
因此需要注意:字符流仅适合读写文本数据(字符或者字符串)
字符流都是高级流
InputStreamReader和OutputStreamWriter 转换流(可以理解为连接两个流的接口)
这对流是一对高级流,作用有两个:

 1)在流链接中,衔接其他的高级字符流和下面的字节流
2)负责将字符与对应的字节按照指定的字符集自动转换,方便读写的操作

package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
/**
* java IO将流按照读写的单位分为字节流和字符流
* java.io.InputStream和OutputStream是所有字节流的超类,读写的最小单位是字节 * java.io.Reader和Writer是所有字符流的超类,读写的最小单位是字符
* 因此需要注意:字符流仅适合读写文本数据(字符或者字符串)
* 字符流都是高级流
* InputStreamReader和OutputStreamWriter 转换流
* 这对流是一对高级流,作用有两个:
* 1)在流链接中,衔接其他的高级字符流和下面的字节流
* 2)负责将字符与对应的字节按照指定的字符集自动转换,方便读写的操作
*/
public class OSWDemo {
    public static void main(String[] args) throws IOException {
} }
//创建一个低级的字节输出流,向osw.txt中写入文本数据 
FileOutputStream fos = new FileOutputStream("./osw.txt"); 
//创建一个高级的转换流,直接可以写出字符串 
//一般也会传入第二个参数,用于指定编码
OutputStreamWriter osw = new OutputStreamWriter(fos,StandardCharsets.UTF_8); 
//支持直接写入字符串
osw.write("哇哈哈哈哈哈哈"); 
osw.write("呜呜呜呜呜呜呜");
osw.write("嘿嘿嘿嘿嘿嘿嘿"); 
System.out.println("写出完毕!");
osw.close();

在使用这个osw.write的时候,可以直接传入字符串,这一点相比起之前方便很多。

但是一定要注意,在新建OutputStreamWriter类的时候,要记得把输出流传入,并限定好编码集。

3.2 InputStreamReader

先搞一个低级流(是输入还是输出流?)

再搞一个高级流来转换。

因为目前的read()只能读取一个字符,所以这里需要写while循环来读取内容。

package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
/**
* 使用转换流测试读取文本数据 */
public class ISRDemo {
    public static void main(String[] args) throws IOException {
//将osw.txt中的所有内容读取出来,并输出到控制台 
FileInputStream fis = new FileInputStream("./osw.txt"); 
InputStreamReader isr = new InputStreamReader(fis,StandardCharsets.UTF_8);
/*
* read() 读取一个字符,返回一个int值
* 但是如果返回的是-1,就说明读取到了末尾 
*/
        int d;
        while ((d = isr.read()) != -1) {
            System.out.print((char)d);
        }
        isr.close();
    }
}

4. 缓冲字符流

* java.io.BufferedWriter和BufferedReader
* 但是因为java.io.PrintWriter是具有自动行刷新功能的缓冲字符流,内部自己链接了BufferedWriter * 所以实际上我们使用缓冲字符流时,都是用PrintWriter来写出,用BufferedReader来读取。

PrintWriter的代码例子:

注意,这里直接使用PrintWriter类直接读取,不需要用低级流!为什么?因为PrintWriter内部已经给我们自动连接好了低级流,所以我们不需要自己再写一遍了。但不是每个流都是这样的,所以需要每个流具体分析。

package io;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
/**
* 缓冲字符流
* java.io.BufferedWriter和BufferedReader
* java.io.PrintWriter是具有自动行刷新功能的缓冲字符流,内部自己链接了BufferedWriter 
* 所以实际上我们使用缓冲字符流时,都是用PrintWriter
* 特点:
* 1)可以按行写入字符串
* 2)可以自动行刷新
* 3)可以提高写出字符串的效率 */
public class PWDemo {
    public static void main(String[] args) throws FileNotFoundException,
UnsupportedEncodingException { 
//向pw.txt中写入字符串
//声明一个高级的字符流
//一般我们传入第二个参数指定编码,但是因为PrintWriter这个流出现的比较早,
不支持StandardCharsets.UTF_8这种语法结构,
所以需要使用字符串指定编码类型
} }
PrintWriter pw = new PrintWriter("./pw.txt","UTF-8"); 
//ctrl+D 快速复制
pw.println("阿瓦达啃大瓜");
pw.println("阿瓦达啃大瓜");
pw.println("阿瓦达啃大瓜"); 
pw.println("阿瓦达啃大瓜"); 
pw.println("阿瓦达啃大瓜");
pw.println("阿瓦达啃大瓜"); 
System.out.println("写出完毕!");
pw.close();

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq030928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值