今日学习
转换流
编码/解码:
- 编码:将字符按某种规则存储到计算机中
- 解码:存储在计算机中的二进制数据按某种规则解析显示
字符集(编码表):生活中问庄子与计算机中二进制的对应规则
例如:ASCII
、GBK
、UNICODE
(UTF-8
、UTF-16
、UTF-32
)
字符集
ASCII
基于拉丁字母,显示:英语、控制字符、数字等- 基本
ASCII
:7位表示一个字符->128个 - 扩展
ASCII
:8位表示一个字符->256个
- 基本
ISO-8859-1
显示欧洲语言,单字节编码,兼容ASCII
编码GBK
显示中文GB2312
简体中文表,兼容ASCII
编码GBK
最常用的编码表,双字节编码,兼容ASCII
编码,支持繁体、日韩文字等GB18030
最新的编码表,多字节,支持少数民族文字、繁体、日韩
UNICODE
万国码,最多用4个字节表示UTF-8
:128个ASCII
,占1个字节;拉丁文,占2个字节;大部分(含中文),占3个字节;其他极少用,占4个字节UTF-16
UTF-32
乱码问题
FileReader
读取windows
系统创建文本文件(中午系统默认GBK
),容易引发乱码问题
原因:FileReader
只能读取IDEA
默认编码(UTF-8
)的文件
注意:FileReader
底层调用FileInputStream
读取字节
转换流
OutputStreamWriter
java.io.OutputStreamWriter extends Writer
将字符流按指定的charset
转换为字节流(编码)
-
构造方法
OutputStreamWriter osw = new OutputStreamWriter(OutputStream out);
按默认编码表转换OutputStreamWriter osw = new OutputStreamWriter(OutputStream out, String charsetName);
按指定编码表转换
注意:charsetName
不区分大小写!
-
使用步骤
- 创建
OutputStreamWriter
对象,构造方法中传递字节输出流和指定的编码表 - 调用
write()
方法,将字符流按指定编码转换为字节流存储到内存缓冲区中 - 调用
flush()
方法,将缓冲区的数据刷新到文件中(可省略) - 调用
close()
方法,关闭输出流,释放资源
- 创建
InputStreamReader
java.io.InputStreamReader extends Reader
将字节流按指定的charset
转换为字符流(解码)
-
构造方法
InputStreamReader isr = new InputStreamReader(InputStream in);
按默认编码表转换InputStreamReader osw = new InputStreamReader(InputStream in, String charsetName);
按指定编码表转换
注意:charsetName
不区分大小写!
-
使用步骤
- 创建
InputStreamReader
对象,构造方法中传递字节输入流和指定的编码表 - 调用
read()
方法,将字节流按指定编码转换为字符流,读取 - 调用
close()
方法,关闭输入流,释放资源
注意:构造方法中指定的编码必须与待读取文件的编码一致,否则会出现乱码问题
- 创建
序列化和反序列化
序列化
将对象以流的形式写入到文件中,即写对象,也叫对象的序列化
注意:对象中包含的不仅仅有字符,所以要用字节流
java.io.ObjectOutputStream extends OutputStream
- 构造方法:
ObjectOutputStream oos = new ObjectOutputStream(OutputStream out);
- 特有方法:
void writeObject(Object o);
- 使用步骤:
- 创建
ObjectOutputStream
对象,构造方法中传递字节输出流对象 - 调用
writeObject
方法将对象序列化写入文件中 - 调用
close
方法释放资源
- 创建
反序列化
将文件保存的对象数据,以流的形式读取出来,即读对象,也叫对象的反序列化
注意:文件中保存的不仅仅有字符,所以要用字节流
java.io.ObjectInputStream extends InputStream
- 构造方法:
ObjectInputStream ois = new ObjectInputStream(InputStream in);
- 特有方法:
Object readObject();
- 使用步骤:
- 创建
ObjectInputStream
对象,构造方法中传递字节输入流对象 - 调用
readObject
方法读取文件中的对象 - 调用
close
方法释放资源
注意:readObject
方法声明中抛出ClassNotFoundException
(class
文件找不到异常),当不存在对象的class
文件时会抛出此异常
实现反序列化的前提:- 实现
Serializable
接口 - 必须存在类对应的
class
文件
- 实现
- 创建
抛出异常
NotSerializableException
没有序列化异常
序列化和反序列化时会抛出NotSerializableException
异常,类通过实现java.io.Serializable
接口以启用其序列化功能。
注意:要进行序列化/反序列化的类必须实现java.io.Serializable
接口,给类加一个标记
当进行序列化/反序列化时,会检测类是否有这个标记:- 有:进行操作
- 无:抛出
NotSerializableException
异常
ClassNotFoundException
readObject
方法声明中抛出ClassNotFoundException
(class
文件找不到异常),当不存在对象的class
文件时会抛出此异常InvalidClassException
无效类异常
当JVM
反序列化对象时,能找到class
文件,但是class
文件在序列化对象之后发生改变,则反序列化就会失败,抛出异常。
javac.exe
会把类java文件编译为对应的class
文件,此类实现了Serializable
接口,会根据类的定义给class
文件中添加序列化号serialVersionUID
再进行反序列化时,会验证class文件与序列化后的文件的序列号:
- 相同,反序列化顺利执行 ;
- 否则,会抛出
InvalidClassException
解决方法:在序列化/反序列化的类中定义成员变量设置序列号private static final long serialVersionUID = 任意值L;
瞬态关键字
`瞬态关键字``transient:被它修饰的成员变量不能被序列化 静态关键字
static```:优先于非静态加载到内存中,也不能被序列化
文件中保存多个对象
分析:
- 创建一个集合,保存多个对象
- 对集合进行序列化
打印流
java.io.PrintStream extends OutputStream
- 特点
- 只负责数据输出
- 用于不会抛出
IOException
- 存在特有的方法:
print()/prntln()
可以输出任意类型的数据
- 构造方法
PrintStream(File file)
,目的地是文件PrintStream(OutputStream out)
,目的地是输出流PrintStream(String fileName)
,目的地是文件路径
- 注意
若使用继承自父类的write()
方法写数据,那么查看数据会查询编码表
如使用特有方法print()/println()
方法,那么原样输出 System
的静态方法static void setOut(PrintStream ps)
重新分配“标准”输出流
格式://将输出内容写道打印流中,而非控制台 PrintStream out = new PrintStream(file); System.setOut(out); System,out.println("你好");