本帖最后由 小石姐姐 于 2018-4-27 11:38 编辑
黑马七期day10笔记
Day10
今日内容概览\
# 按照流的用途分:
文件输入输出流
|_ 字节流
|_ FileInputStream
|_ FileOutputStream
|_ 字符流
|_ FileReader
|_ FileWriter
缓冲输入输出流
|_ 字节流
|_ BufferedInputStream
|_ BufferedOutputStream
|_ 字符流
|_ BufferedReader
|_ BufferedWriter
对象输入输出流
|_ 字节流
|_ ObjectInputStream
|_ ObjectOutputStream
打印输出流
|_ 字节流
|_ PrintStream
|_ 字符流
|_ PrintWriter
转换流
|_ 字符流
|_ InputStreamReader
|_ OutputStreamWriter
# 体系结构:
字节流
|_ InputStream # 字节输入流
| |_ FileInputStream # 专门操作文件的字节输入流
| |_ BufferedInputStream # 带有缓冲区的字节输入流, 效率高
| |_ ObjectInputStream # 对象输入流
|
|_ OutputStream # 字节输出流
|_ FileOutputStream # 专门操作文件的字节输出流
|_ BufferedOutputStream # 带有缓冲区的字节输出流, 效率高
|_ ObjectOutputStream # 对象输出流
|_ PrintStream # 字节打印流
字符流
|_ Reader # 字符输入流
| |_ BufferedReader # 带有缓冲区的字符输入流, 效率高
| |_ InputStreamReader # 将字节流转换为字符流输入的转换输入流
| |_ FileReader # 专门操作文件的字符输入流
|
|_ Writer # 字符输出流
|_ BufferedWriter # 带有缓冲区的字符输出流, 效率高
|_ OutputStreamWriter # 将字符流转换为字节流输出的转换输出流
|_ FileWriter # 专门操作文件的字符输出流
|_ PrintWriter # 字符打印流
IO流
|_ 标准流
|_ System.in(InputStream) 键盘录入
|_ System.out(PrintStream) 控制台输出
|_ 转换流
|_ OutputStreamWriter 以操作字符流的方式按字节流写出(实现这种转换: Writer -> OutputStreamWriter -> OutputStream)
|_ InputStreamReader 以操作字符流的方式按字节流读入
|_ 打印流
|_ PrintWriter 具有自动刷新, 自动换行的特点
|_ 对象流
|_ ObjectInputStream 从文件中读入一个对象到内存中
|_ ObjectOutputStream 将内存中的对象写入到文件中
集合
|_ Properties 用于与IO流结合读取配置文件, 键值对形式, 实现Map(key-value)
乱码
|_ 乱码的原因: 多种编码表查错了
|_ 字符串编码的转换
补充String类
构造方法(作用byte[] 转String)
String(byte[] bys, int index, int length):使用byte数组的一部分按照系统默认编码表(GBK)创建String, 即将byte数组转为String
成员方法(作用: String 转 byte[])
byte[] getBytes(): 按照默认编码表获取字符串的byte数组, 即将String转为byte数组
// 转换的作用
// 多态接收
Writer w = new OutputStreamWriter(System.out);
w.write(char/char[]/String);
// OutputStreamWriter外层也可套上缓冲流BufferedWriter
// BufferedWriter -> OutputStreamWriter -> OutputStream
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
bw.write(char/char[]/String);
bw.newLine();
InputStreamReader类
是Reader的子类, 属于字符输入流. 是字节流通向字符流的桥梁
作用: 以读取字符流的方式编写代码(char, char[], String), 而底层以字节流读取(byte, byte[])
// 多态创建对象
Reader r = new InputStreamReader(System.in);
int len;
char[] chs = new char[1024];
while ((len = r.read(chs)) != -1) {
// ...
}
打印流分类
PrintStream: 字节打印流
PrintWriter: 字符打印流, 我们学的
只有输出流, 因为打印是输出操作
PrintWriter, 字符打印输出流的特点
自动换行. println(). 会根据系统自动确定换行符
自动刷新. 通过构造方法的配置可以实现(只在调用println(), printf(), format()时有用)
也是包装流, 类似于BufferedWriter, 自身没有写出功能
可以把字节输出流转换为字符输出流
不能输出byte字节, 可以输出其他任意类型(要输出字节可以使用PrintStream)
关流不会抛出异常(此类中的方法不会抛出 I/O 异常,尽管其某些构造方法可能抛出异常)
PrintWriter类
构造方法
PrintWriter PrintWriter(String filepath): 创建对象
PrintWriter PrintWriter(Writer out, boolean autoFlush): 包装字符输出流创建对象, 同时设置是否自动刷新
PrintWriter(OutputStream out, boolean autoFlush): 包装字节输出流创建对象, 同时设置是否自动刷新
成员方法
void write(String s): 写一个字符串
void print(String s): 输出字符串, 没有换行
void println(String s): 输出字符串并换行. 如果启动了自动刷新, 则会执行自动刷新写入数据
void printf(Locale l, String format, Object... args): 使用指定格式字符串和参数将格式化的字符串写入输出流. 如果启动了自动刷新, 则会执行自动刷新写入数据
void format(Locale l, String format, Object... args): 使用指定格式字符串和参数将格式化的字符串写入输出流. 如果启动了自动刷新, 则会执行自动刷新写入数据
对象操作流概述
对象操作流 (都属于字节流)
ObjectInputStream
构造方法
ObjectInputStream(InputStream in): 创建对象输入流
成员方法
Object readObject(): 从对象输入流读取对象, 一次只读取一个对象. 当读不到时抛出EOFException.
ObjectOutputStream
构造方法
ObjectOutputStream(OutputStream out): 创建对象输出流
成员方法
void writeObject(Object o): 将对象写入对象输出流
作用:
读写任意类型的对象
注意:
使用对象输出流写出对象到文件, 则该文件只能使用对象输入流读取对象, 其他流不行
只能将实现了java.io.Serializable接口的类的对象写入流中, 对象内部有自定义类型的成员变量, 也要实现该接口
java.io.Serializable接口:
没有成员, 是一个标识接口
用于序列化
序列化: 将对象的信息转换为可存储的信息的过程
如何判断对象流读取完毕?
通过捕获EOFException
EOFException:
End of File Exception, 文件末尾异常, 当输入过程中意外达到文件或流的末尾时, 抛出此异常
5分钟练习: 读写对象
继续使用项目s2-day10, 建包com.itheima.practice_04
建类Student
实现Serializable接口
成员变量
私有姓名, 年龄
空参, 有参构造
get/set
toString
建类Test
创建2个学生对象
创建对象输出流对象, 目标a.txt
创建对象输入流对象, 目标a.txt
先写2个对象, writeObject()
然后使用while循环读取readObject(), 通过捕获EOFException来结束
解决对象输入流读取对象异常的问题
捕获EOFException很不优雅, 有没有更好的方法?
一个文件只保存一个对象, 只读取一次
对于多个对象, 可以将多个对象放入一个集合对象中, 就可以实现
注意: 集合中的对象类型, 也需要实现Serializable接口, 所有相关自定义类型的成员变量也要
5分钟练习: 使用集合改进
继续使用项目s2-day10, 建包com.itheima.practice_05
复制上一个练习的代码
修改写入和读取
先将2个学生对象放入一个List集合中, 然后写这个集合对象
读取时只读一次, 不捕获EOFException异常, 强转为List, 获取元素打印
private static final long serialVersionUID: 序列版本号
作用:
相当于根据类的成员信息生成的身份证号, 通过序列化流写对象到文件中时, 会将该序列号写入文件. 如果类有改动, 那么代码中序列版本号也会变化. 可以根据代码中的序列号和文件中的序列号是否一致来判断对象与类是否对应
默认不定义序列号时, 会自动生成随机的
如果不想因为类的改动导致序列号变化, 则要在定义类时手动定义序列号, 使其固定为一个值
InvalidClassException用对象读取流时发生该异常的原因:
该类的序列版本号与从流中读取的类描述符的版本号不匹配
该类包含未知数据类型(即可能添加了新的成员变量或方法)
该类没有可访问的无参数构造方法
注意:
如果固定了序列号, 当读取文件中没有属性时, 返回值就是null
序列号的影响只与成员变量有关, 与方法无关
如果类中比文件中增多了属性, 则该属性值为null
如果类中比文件中减少了属性, 则无法获取该属性
Properties概述
java.util.Properties
构造方法
Properties(): 创建对象
成员方法
可以使用Map接口的方法
String getProperty(String key): 根据键获取值. 如果找不到该键, 则返回null
String getProperty(String key, String defaultValue): 根据键获取值, 如果值不存在, 则使用指定的默认值
void setProperty(String key, String value): 设置键值对
继承了Hashtable<K, V>, 实现Map<K,V>接口, 以键值对方式存储数据
作用: 保存键值对信息, 可与IO流结合使用, 将数据方便的写入到文件, 或从文件中读取到集合
应用场景: 保存程序的配置信息
配置文件的格式和语法:
在配置文件中的保存形式是每行一个键=值
#是注释
Properties和IO流结合的功能
Properties类常用方法
从配置文件中加载到Properties集合
void load(InputStream in): 从字节输入流中读取Properties
void load(Reader reader): 同上, 只是流不同
从Properties集合写出到配置文件
void list(PrintStream out): 将Properties输出到指定的字节打印输出流.
会自动加一个-- listing properties --文件头
void list(PrintWriter writer): 同上, 只是流不同
会自动加一个-- listing properties --文件头
void store(Writer writer, String comments): 将Properties输出到指定的输出流, 并添加一个注释. 如不想增加注释可填写null.
无论是否有注释, 都会自动添加时间字符串
void store(OutputStream os, String comments): 同上, 只是流不同
无论是否有注释, 都会自动添加时间字符
list()和store()的区别
list只能接收打印流(PrintStream, PrintWriter)
store可以接收任何输出流(OutputStream, Writer)
常用Propertis的读写操作步骤
写出到文件
创建Properties对象, 添加键值对, 使用list()或store()保存到文件中
从文件读取
创建Properties对象, 使用load()从文件加载数据, 使用getProperties()根据指定键获取值, 或使用遍历Map的方式遍历所有键和值
Properties和IO流结合的功能
Properties类常用方法
从配置文件中加载到Properties集合
void load(InputStream in): 从字节输入流中读取Properties
void load(Reader reader): 同上, 只是流不同
从Properties集合写出到配置文件
void list(PrintStream out): 将Properties输出到指定的字节打印输出流.
会自动加一个-- listing properties --文件头
void list(PrintWriter writer): 同上, 只是流不同
会自动加一个-- listing properties --文件头
void store(Writer writer, String comments): 将Properties输出到指定的输出流, 并添加一个注释. 如不想增加注释可填写null.
无论是否有注释, 都会自动添加时间字符串
void store(OutputStream os, String comments): 同上, 只是流不同
无论是否有注释, 都会自动添加时间字符
list()和store()的区别
list只能接收打印流(PrintStream, PrintWriter)
store可以接收任何输出流(OutputStream, Writer)
常用Propertis的读写操作步骤
写出到文件
创建Properties对象, 添加键值对, 使用list()或store()保存到文件中
从文件读取
创建Properties对象, 使用load()从文件加载数据, 使用getProperties()根据指定键获取值, 或使用遍历Map的方式遍历所有键和值
Java中字符串的编码
编码:
是字节(byte)转换为字符(char)过程中的操作, 读写文本数据, 都要查询编码表
编码表:
保存数字和字符对应关系的表, 也称为字符集(charset)
Java中的字符串的默认编码
ANSI编码, 即当前系统是什么编码就是什么编码
String类中常用方法
构造方法
String(byte[] bytes): 将byte数组转化为字符串, 使用默认编码
String(byte[] bytes, String charsetName): 将byte数组转换为字符串, 使用指定编码
String(byte[] bytes, int offset, int len, String charsetName): 使用指定编码, 将byte数组的一部分转换为字符串
成员方法
byte[] getBytes(): 获取字符串的byte数组, 使用默认编码
byte[] getBytes(String charsetName): 获取字符串的byte数组, 使用指定编码
乱码
查找编码表找不到对应的字符, 或找错了字符, 显示为错误字符.
举例, 哈在GBK编码表中的值是11(假设), 我们把11写入到文件, 然后读取文件, 读的时候使用UTF-8编码, 11在UTF-8编码表中对应的字符为?(假设), 就显示乱码了
原因: 读的编码与写的编码不一致
解决方法: 保证读和写的编码一致, 即可解决
编码涉及的异常
UnsupportedEncodingException: 不支持的编码类型
处理乱码的2种方式:
String通过指定编码转为byte数组, 然后再创建String: (GBK字符串转UTF-8字符串写入文件)
先将String通过目标编码转为byte数组: byte[] bys = "月薪过万".getBytes("UTF-8");
再将byte数组转换为String: String str = new String(bys);
写入到文件: fw.write(str);
OutputStreamWriter可以指定编码写入文件, 免去使用String通过编码转换为byte数组的步骤
OutputStreamWriter OutputStreamWriter(OutputStream out, String charsetName): 创建转换流对象, 并指定编码
|
|