1、IO异常的处理(JDK1.7前后)
1、JDK1.7以前的IO异常处理
/*
JDK1.7以前的
1.创建IO流的时候发现FileInputStream有异常,选择try...catch
2.在finally中写上关闭IO流语句后,且作用域不够,故将fis的定义放到try外面,
3.发现fis需要赋初值,故赋值为null
4.当fis为null值的时候,没办法调用close语句,故在finally中先判断fis!=null才去关流
5.close又有异常,选择try...catch
6.使用IO流读写数据又有异常,抓异常,添加catch分支(Add Enter的第一项)
*/
public static void test01() {
FileInputStream fis = null;
try {
fis = new FileInputStream("day10demo/abc/1.txt");
int read = fis.read();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2、JDK1.7以前的异常处理
/*
JDK1.7的处理方式
自动关流
try (创建流的代码,可以有多条创建语句) {
操作流的代码
} catch (异常类名 变量名) {
}
*/
public static void test02() {
try (FileInputStream fis = new FileInputStream("day10demo/abc/1.txt");
FileOutputStream fos = new FileOutputStream("day10demo/abc/2.txt");
) {
int read = fis.read();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
2、Properties
1、概念:
(1)java.util.Properties 继承于 Hashtable ,来表示一个持久的属性集。
(2)Properties实现了Map接口,而Map接口就是一个双列集合。键和值都是字符串(作为集合的使用)
Map
↑实现
Hashtable
↑继承
Properties
(3)可以将Properties中的数据通过IO流保存到文件,也可以从文件中加载数据保存到Properteis中(和IO流的关联使用)
2、Properties作为集合的基本使用
Properties具有父类Map的方法。但也有其特有的方法:
Object setProperty(String key, String value) 添加键和值
String getProperty(String key) 通过键找值
Set<String> stringPropertyNames() 获取所有的键
public static void main(String[] args) {
// 1.创建Properties对象
Properties pp = new Properties();
// 2.调用方法
// Object setProperty(String key, String value) 添加键和值
pp.setProperty("name", "蔡徐坤");
pp.setProperty("age", "22");
pp.setProperty("address", "泰国");
System.out.println(pp); // {address=泰国, name=蔡徐坤, age=22}
// String getProperty(String key) 通过键找值
String name = pp.getProperty("name");
String age = pp.getProperty("age");
String address = pp.getProperty("address");
System.out.println(name + ":" + age + ":" + address);
// 遍历Properties,相当于遍历Map
// Set<String> stringPropertyNames() 获取所有的键
// 1.得到所有的键
Set<String> keySet = pp.stringPropertyNames();
// 2.遍历得到每个键
for (String key : keySet) {
// 3.通过一个键找到一个值
String value = pp.getProperty(key);
System.out.println(key + "::" + value);
}
}
3、Properties保存和加载文件
(1)保存数据到文件
void store(OutputStream out, String comments) 将Properties中的键值对数据保存到文件
//OutputStream out: 字节输出流
//String comments: 往文件写注释内容
public static void testStore() throws IOException {
// 1.创建Properties
Properties pp = new Properties();
// 2.往Properties中存储数据
pp.setProperty("小明","18");
pp.setProperty("小花","19");
pp.setProperty("小白","20");
pp.setProperty("小李","21");
// 3.调用store方法将数据写入流中
// void store(OutputStream out, String comments) 将Properties中的键值对数据保存到文件
// OutputStream out: 字节输出流
// String comments: 往文件写注释内容
FileOutputStream fos = new FileOutputStream("day10demo/abc/info.properties");
pp.store(fos, "I am zhushi");
}
以下是info.propertise文件内容
其中’‘#‘’是注释的标记;\u开头,为unicode码,表示后面的字符为中文
#I am zhushi
#Thu Jul 25 21:02:28 CST 2019
\u5C0F\u660E=18
\u5C0F\u674E=21
\u5C0F\u767D=20
\u5C0F\u82B1=19
(2)从流中加载数据到Properties中
void load(InputStream inStream) 从流中加载数据到Properties中
依据上述的info.properties文件,从该文件中读取数据到流中并加载到pp(Properties对象)中
public static void main(String[] args) throws IOException {
// 1.创建Properties对象
Properties pp = new Properties();
// 2.创建一个文件输入流,并调用load方法
// void load(InputStream inStream) 从流中加载数据到Properties中
FileInputStream fis = new FileInputStream("day06/abc/info.properties");
pp.load(fis);
System.out.println(pp);//{小明=18, 小李=21, 小白=20, 小花=19}
}
/*输出结果
{小明=18, 小李=21, 小白=20, 小花=19}
*/
3、缓冲流
1、概念
我们知道在操作基本流的时候使用不带数组读取的次数很多,效率低;而带数组,会减少读取次数,提高效率。
既然带数组可以提高效率,Java就自己写好了4个类,这四个类带数组,这个数组称为缓冲区,这四个类叫做缓冲流
基本流: 字节流 字符流
输入流 FileInputStream FileReader
输出流 FileOutputStream FileWriter
这4个流称为缓冲流,高效流,类名都是以Buffered开头
缓冲流:
字节流 字符流
输入流缓冲流 BufferedInputStream BufferedReader
输出缓冲流 BufferedOutputStream BufferedWriter
2、缓冲流的优点
内部自带数组,提高IO流的效率
3、字节缓冲流
1、字节输出缓冲流: BufferedOutputStream
(1)构造方法:
BufferedOutputStream(OutputStream out)
(缓冲流只是提供数组,来提高IO流的效率真的操作文件的是参数传入的基本流)
(2)普通方法:
BufferedOutputStream继承了OutputStream使用的还是OutputStream里面那些写数据的方法
public static void test01() throws IOException {
// 1.创建缓冲流,传入基本流
// FileOutputStream fos = new FileOutputStream("day10demo/abc/bos.txt");
// BufferedOutputStream bos = new BufferedOutputStream(fos);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day10demo/abc/bos.txt"));
// 2.调用写数据的方法
byte[] bytes = new byte[] {65, 66, 67, 68, 69};
bos.write(bytes);
// 3.关闭流
bos.close();
}
2、字节输入缓冲流: BufferedInputStream
(1)构造方法:
BufferedInputStream(InputStream in):
(缓冲流只是提供数组,来提高IO流的效率真的操作文件的是参数传入的基本流)
(2)普通方法:
BufferedInputStream继承了InputStream,故使用的还是InputStream里面的那些读取数据的方法
public static void test02() throws IOException {
// 1.创建字节输入缓冲流,传入基本流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day10demo/abc/bos.txt"));
// 2.调用方法读取数据
int len;
byte[] buf = new byte[1024];
while ((len = bis.read(buf)) != -1) {
System.out.println(new String(buf, 0, len));
}
// 3.关闭流
bis.close(); // 关闭缓冲流的时候,会自动帮我们关闭基本流
}
4、字符缓冲流
1、字符输出缓冲流: BufferedWriter
(1)构造方法
BufferedWriter(Writer out) 需要传入一个基本流
(2)普通方法
BufferedWriter继承了Writer使用的是Writer里面写数据的方法
特有方法
写一个换行符 void newLine() "\r\n"
1、字符输入缓冲流: BufferedReader
(1)构造方法
BufferedReader(Reader r) 需要传入一个基本流
(2)普通方法
BufferedReader继承了Reader,使用的是Reader里面读取数据的方法
特有方法
特有方法:String readLine() 返回读取到的那行数据
public static void main(String[] args) throws IOException {
test01();
// test02();
}
/*
字符输入缓冲流: BufferedReader
BufferedReader继承了Reader,使用的是Reader里面读取数据的方法
BufferedReader构造方法:
BufferedReader(Reader r) 需要传入一个基本流
特有方法:String readLine() 返回读取到的那行数据
*/
public static void test02() throws IOException {
BufferedReader br = new BufferedReader(new FileReader("day10demo/abc/bw.txt"));
/*
int len;
char[] chs = new char[1024];
while ((len = br.read(chs)) != -1) {
System.out.println(new String(chs, 0, len));
}
*/
// 特有方法:String 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) {//当当前行数为空时,返回值是null
System.out.println(line);
}
br.close();
}
/*
字符输出缓冲流: BufferedWriter
BufferedWriter继承了Writer使用的是Writer里面写数据的方法
BufferedWriter构造方法:
BufferedWriter(Writer out) 需要传入一个基本流
特有方法: 写一个换行符 void newLine() (之前换行符表示为:"\r\n")
*/
public static void test01() throws IOException {
// 1.创建字符输出缓冲流
BufferedWriter bw = new BufferedWriter(new FileWriter("day10demo/abc/bw.txt"));
// 2.写数据
bw.newLine();
bw.write("事实证明寒冷的天气可以使人年轻,张大爷今年80岁了,冻的像个孙子似的");
bw.newLine();
// 3.关闭流
bw.close();
}
4、编码
5、字符转换流
操作字符时如何选择流?
基本流:FileReader/FileWriter
转换流:InputStreamReader/OutputStreamWriter
如果不需要指定字符集使用基本流,
如果需要指定字符集使用转换流
1、InputStreamReader转换流读字符数据
1、概念:
InputStreamReader,
(1)属于输入字符流,
(2)继承了Reader,使用Reader的那些读数据的方法。
2、构造方法
InputStreamReader(InputStream in) 使用默认的UTF-8
InputStreamReader(InputStream in, String charsetName) 使用指定的字符集
真正操作文件靠的参数的基本流,转换流负责将读取到的数据去查码表转成对应文字
3、作用
可以指定编码来读取文件的数据
// InputStreamReader(InputStream in) 使用默认的UTF-8
// InputStreamReader: 属于输入字符流,继承了Reader,使用Reader的那些都数据的方法
public static void test01() throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("day10demo/abc/china_utf8.txt"));
int ch;
while ((ch = isr.read()) != -1) {
System.out.println((char)ch);
}
isr.close();
}
// InputStreamReader(InputStream in, String charsetName) 使用指定的字符集 gbk/GBK utf-8/UTF-8
public static void test02() throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("day10demo/abc/china_gbk.txt"), "gbk");//第二个参数填不存在的编码字符如"nba"则会报错
int ch;
while ((ch = isr.read()) != -1) {
System.out.println((char)ch);
}
isr.close();
}
2、OutputStreamWriter转换流写字符数据
1、概念
OutputStreamWriter:
(1)属于字符输出流,
(2)继承了Writer使用Writer中的那些写数据方法
2、构造方法
OutputStreamWriter(OutputStream out) //创建一个使用默认字符编码的OutputStreamWriter
OutputStreamWriter(OutputStream out, String charsetName) //创建一个使用指定字符编码的 OutputStreamWriter
真正操作文件靠基本流.转换流负责查询码表
3、作用
可以指定编码写文本数据
// OutputStreamWriter(OutputStream out, String charsetName) 创建一个使用指定字符编码的OutputStreamWriter
public static void test02() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day10demo/abc/osw_gbk.txt"), "GBK");
osw.write("你不要乱来!");
osw.close();
}
// OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter
public static void test01() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day10demo/abc/osw_utf8.txt"));
osw.write("你不要乱来!");
osw.close();
}
3、转换文件编码核心
使用指定的编码读取文件数据,再使用指定编码写数据
案例:将GBK编码的文本文件,转换为UTF-8编码的文本文件
步骤:
1.创建一个InputStreamReader,指定字符集为GBK
2.创建一个OutputSreamWriter,指定字符集为UTF-8
3.循环读写
4.关闭流
public static void main(String[] args) throws IOException {
// 1.创建一个InputStreamReader,指定字符集为GBK
InputStreamReader isr = new InputStreamReader(new FileInputStream("day10demo/abc/gbk.txt"), "gbk");
// 2.创建一个OutputSreamWriter,指定字符集为UTF-8
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day10demo/abc/convertoutf-8.txt"));
// 3.循环读写
int len;
char[] chs = new char[1024];
while ((len = isr.read(chs)) != -1) {
osw.write(chs, 0, len);
}
// 4.关闭流
osw.close();
isr.close();
}
6、序列化流与反序列化流
1、ObjectOutputStream对象字节输出流(序列化流)
1、序列化?
将程序中的对象的数据保存到文件中
2、构造方法
ObjectOutputStream(OutputStream in) 需要传入一个基本流写数据到哪个文件
3、要被序列化的类需要实现serializable接口
(1)Serializable接口属于标记接口,内部任何内容都没有
(2)类在实现了Serializable接口后,类会自动拥有一个序列化号
4、在类中指定成员变量不序列化到文件的办法
用transient或者static修饰。被这两个关键字修饰的成员变量不会被序列化。
5、该类的将对象写入文件的方法
void writeObject(Object obj)
6、对象序列化流将对象写入文件中步骤
1.定义个Person类
2.创建Person对象
3.创建序列化流ObjectOutputStream
4.调用方法将对象的数据写入文件中
5.关闭流
public static void main(String[] args) throws IOException {
// 1.定义个Person类
// 2.创建Person对象
Person p1 = new Person("凤姐", 18);
// 3.创建序列化流ObjectOutputStream
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day10demo/abc/persons.txt"));
// 4.调用方法将对象的数据写入文件中
// void writeObject(Object obj)
oos.writeObject(p1);
// 5.关闭流
oos.close();
}
2、ObjectInputStream对象字节输入流(反序列化流)
1、反序列化?
将文件中对象的数据读取到程序中
2、构造方法
ObjectInputStream(InputStream in) 需要传入一个基本流读取哪个文件
3、该类的将对象写入文件的方法
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 1.创建反序列化流ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day10demo/abc/persons.txt"));
// 2.调用readObject读取一个对象
// Object obj = new Person("凤姐", 18);
Object obj = ois.readObject();
// 实际上走的是右边person对象的toString
System.out.println(obj.toString()); // Person{name='凤姐', age=18}
// 3.关闭流
ois.close();
}
3、序列化号冲突的问题
1、什么是序列化流冲突问题
(1)有上述可知,类实现了Serializable接口后,类会自动拥有一个序列化号
(2)将该实现了Serializable接口后的类序列化到文件中,类的序列化号也会保存到文件中
(3)在使用对象反序列化流将对象取出来的时候,会对保存到文件中的列的序列化流进行验证,验证其与类中的序列化流是否一致。
(4)若一致,则读取成功
(5)若中途修改了类的内容,如添加、删改类的成员变量等,类的序列化号就发生改变。验证就会不一致,则读取失败,抛出异常(异常中会显示前后的序列化号)
2、如何避免、解决序列化流冲突的问题
在类中定义一个序列化号的成员变量,序列化号随机。
这样修改类也不会改变类的序列化号
public class Person implements Serializable {//实现Serializable接口
public static final long serialVersionUID = 6668889999L;//在类中定义一个序列化号的成员变量
private String name;
private transient int age;
// private double height;//修改类的内容
}
4、序列化和反序列化集合
1、概念
将集合序列化到文件
2、步骤:
1.定义一个集合
2.保存一些数据到集合中
3.将集合序列化到文件
4.读取文件中的集合数据(反序列化集合)
3、注意:
集合中的数据也要实现序列化接口
存储的是什么类型,取的就是什么类型
// 序列化集合
public static void test01() throws IOException {
// 1.定义一个集合
ArrayList<Person> list = new ArrayList<>();
// 2.保存一些数据
list.add(new Person("樱木花道", 18));
list.add(new Person("流川枫", 19));
list.add(new Person("赤木晴子", 16));
list.add(new Person("仙道", 17));
// 3.将集合序列化到文件
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day10demo/abc/glgs.txt"));
oos.writeObject(list);
oos.close();
}
// 4.读取文件中的集合数据(反序列化集合)
public static void test02() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day10demo/abc/glgs.txt"));
//虽然返回值是Object类型,但当初存入文件的是ArrayList<Person>类型,故可以用该类型进行接收
// Object obj = ois.readObject();
ArrayList<Person> list = (ArrayList<Person>) ois.readObject();
for (Person p : list) {
System.out.println(p);
}
}
7、打印流
1、特点:
(1)只有输出方向,没有输入方向
(2)原样输出,写什么就打印什么
2、分类
字节打印流:PrintStream
字符打印流:PrintWriter(不常用)
3、PrintStream类
(1)构造方法
PrintStream(String fileName) 使用指定的文件名创建新的打印流,无需自动换行。
PrintStream(File file) 使用指定的文件创建一个新的打印流,而不需要自动换行。
PrintStream(OutputStream out) 创建一个新的打印流。
(2)普通方法
print(Xxx x) 不换行原样打印数据
println(Xxx x) 原样打印数据,并换行
public static void test01() throws FileNotFoundException {
// 1.创建打印流
PrintStream ps = new PrintStream("day10demo/abc/ps.txt");
// 2.调用打印的方法
// 不换行打印
/*ps.print(100);
ps.print('a');
ps.print("嘎嘎");
ps.print(true);*/
// 换行打印
ps.println(111);
ps.println('号');
ps.println("鸡鸡");
ps.println(6.66);
ps.println(false);
ps.close();
}
4、扩展了解打印输出语句sout
平时在代码的某些地方添加一些打印信息,开发的时候打印在控制台,而在项目部署的时候,打印到文件------>叫做"收集log信息"
// 扩展:System.out是一个打印流,往控制台打印
// 我们知道,类名能够调用Out,说明Out是static修饰的,out是一变量
System.out.println("ooooo");
System.out.print("111");
System.out.print("222");
// 我们可以给他换一个打印流,让他往指定位置来打印
PrintStream ps = new PrintStream("day10demo/abc/666.txt");
System.setOut(ps);//设置Out变量的方法setOut()
System.out.println("333");
System.out.println("444");
8、流的总结
[外链图片转存失败(img-h3VYBIsV-1564157618826)(E:\1a黑马java\黑马java\就业班\day10—\source\字节流总结.png)]
[外链图片转存失败(img-0KyHtZzE-1564157618830)(E:\1a黑马java\黑马java\就业班\day10—\source\字符流总结.png)]
rint(‘a’);
ps.print(“嘎嘎”);
ps.print(true);*/
// 换行打印
ps.println(111);
ps.println('号');
ps.println("鸡鸡");
ps.println(6.66);
ps.println(false);
ps.close();
}
##### 4、扩展了解打印输出语句sout
平时在代码的某些地方添加一些打印信息,开发的时候打印在控制台,而在项目部署的时候,打印到文件------>叫做"收集log信息"
```java
// 扩展:System.out是一个打印流,往控制台打印
// 我们知道,类名能够调用Out,说明Out是static修饰的,out是一变量
System.out.println("ooooo");
System.out.print("111");
System.out.print("222");
// 我们可以给他换一个打印流,让他往指定位置来打印
PrintStream ps = new PrintStream("day10demo/abc/666.txt");
System.setOut(ps);//设置Out变量的方法setOut()
System.out.println("333");
System.out.println("444");