java IO流进阶

缓冲流

缓冲流的简介

基本介绍
给普通的IO流, 套上⼀个缓冲区。 所有的使⽤缓冲流进⾏的读写操作, 都是和缓冲区进⾏交互的, 避免了频繁的IO操作。 这样⼀来, 带来的好处就是可以提⾼读写的效率。 这个缓冲区, 其实是⼀个数组。
缓冲流的作⽤
为了提⾼读写的能⼒,本身没有读写的能⼒,要想进⾏读写就必须借助于字符流/字节流实现.
可以将缓冲流类⽐于催化剂或者⾼速的⼩⻋
常⻅的缓冲流:
​ BufferedInputStream : 缓冲字节输⼊流
​ BufferedOutputStream : 缓冲字节输出流
​ BufferedReader : 缓冲字符输⼊流
​ BufferedWriter : 缓冲字符输出流
字符流和缓冲字符流对⽐

在这里插入图片描述

图示分析:

使⽤缓冲流实现读写的步骤与字符流⼀样,只是需要我们先通过构造⽅法传⼊⼀个字
符流对象.同时缓冲流可以提⾼读写效率.
总结:
⼤家在使⽤流读写数据时,尽量使⽤缓冲流,缓冲流中尽量使⽤缓冲字符流,在字符缓冲流中⽐缓冲字节流多了readLine()和newLine()⽅法.

缓冲字节流

try (BufferedInputStream bufferedInputStream = new
BufferedInputStream(new FileInputStream("file\\day26\\source")))
try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new
FileOutputStream("file\\day26\\target")))

缓冲字符流

try (BufferedReader bufferedReader = new
BufferedReader(new FileReader("file\\day26\\src")))
try (BufferedWriter bufferedWriter = new
BufferedWriter(new FileWriter("file\\day26\\dst")))

缓冲字符流中的特殊⽅法

BufferedReader 类中多了⼀个⽅法 readLine()
意义: 读取缓冲流中的⼀⾏数据, 可以逐⾏读取。 ⼀直到读取到的数据是null,表示数据读取完了, 没有下⼀⾏数据了。
注意事项: readLine() 是逐⾏读取, 但是, 只能读取到⼀⾏中的内容, 并不能读取⾛换⾏符。

BufferedWriter 类中多了⼀个⽅法 newLine()

写换⾏符 ,不同的系统使⽤的默认换⾏符不⼀样 windows系统 \r\n linux \n
意义: ⽆参的⽅法, 写⼀个换⾏符,⽀持跨平台(平台⽆关性)

LineNumberReader

是BufferedReader的⼦类,不能读.但是可以提⾼效率,特有功能:设置⾏号,获取⾏号

//LineNumberReader--BufferedReader的子类
//多了一个添加行号的功能
//默认行号从0开始,从1显示
LineNumberReader lineNumberReader = new LineNumberReader(new FileReader("BD2102java_day0805\\file\\data.txt"));
lineNumberReader.setLineNumber(10);

装饰设计模式

设计模式简介
设计模式, 前⼈总结出来的对⼀些常⻅问题的解决⽅案,后⼈直接拿来使⽤.
常⽤的设计模式:单例,⼯⼚,代理,适配器,装饰,模板,观察者等,⼀共23种
装饰设计模式
基于已经实现的功能,提供增强的功能.
装饰设计模式特点
装饰设计模式的由来就来⾃于对缓冲流的实现.
从缓冲流的⻆度讲解
1.使流原来的继承体更加的简单
2.提⾼了效率
3.由于是在原有的基础上提⾼增强的功能,所以他还要属于原来的体系
如果⾃⼰设计装饰设计模式,怎么处理?
1.原来的类 Test—Reader
2.装饰类 BTest----MyBufferedReader
步骤:
1.让BTest 继承⾃Test
2.在BTest内有⼀个Test类型的成员变量
3.通过BTest内⼀个带参数的构造⽅法接收外部传⼊的⼀个Test类型的对象,交给内部的Test的属性
4.在实现功能的时候,调⽤传⼊的Test类型的对象实现原有的功能,⾃⼰实现增强的功能.

Scanner类

简介

这个类, 并不是⼀个IO流。 是⼀个扫描器, 这个类最主要的作⽤, 是从⼀个⽂件中或者从⼀个流中浏览数据。 在这个类中封装了若⼲个⽅法, ⽅便了数据的读取。

API

在这里插入图片描述

注意事项
这⾥nextLine和BufferedReader中的readLine都可以读取⼀⾏数据。 但是区别在于: 结束条件不同。
BufferedReader: 如果读取到的数据是null, 说明没有下⼀⾏数据了。
Scanner: 如果没有下⼀⾏了,再去读取,会出现异常。 所以, 此时的结束条件是 hasNextLine() 为false。

标准输⼊输出流

简介

标准输⼊流: System.in : “标准”输⼊流。此流已打开并准备提供输⼊数据。通常,此流对应于键盘输⼊或者由主机环境或⽤户指定的另⼀个输⼊源。

输⼊源:可以发送数据到内存的设备
输出源:可以接收内存的数据的设备
1.当前的流已经打开并关联了输⼊源--键盘
2.如果不想让键盘充当输⼊源,可以通过setIn进⾏更换
3.是⼀个字节流

标准输出流: System.out : 标准”输出流。此流已打开并准备接受输出数据。通常,此流对应于显示器输出或者由主机环境或⽤户指定的另⼀个输出⽬标。

标准输⼊流

try (BufferedInputStream bis = new
BufferedInputStream(System.in)) {

标准输出流

PrintStream original = System.out;
// PrintStream: 是⼀个打印流,可以将数据输出到指定位置。
try (PrintStream ps = new PrintStream(new
FileOutputStream("file\\day26\\logs", true))) {
// ps.println("hello world!");
// 重定向标准输出流
System.setOut(ps);

转换流

为什么要⽤转换流

在进⾏⽂件读取的时候, 如果项⽬采⽤的字符集和⽂件的字符集不同,会出现乱码的情况。

输⼊流

try (InputStreamReader reader = new
InputStreamReader(new FileInputStream("file\\day26\\src"),"GBK"))

输出流

try (OutputStreamWriter writer = new
OutputStreamWriter(new FileOutputStream("file\\day26\\dst",true), "GBK"))

打印流

打印流分类

除了拥有输出流的特点之外,还有打印的功能.
字节打印流:PrintStream
字符打印流:PrintWriter

字节打印流

字节打印流⽀持的设备:
1.File类型的⽂件
2.字符串类型的⽂件
3.字节输出流

字符打印流

字符打印流⽀持的设备:
1.File类型的⽂件
2.字符串类型的⽂件
3.字节输出流
4.字符写出流
注意点:
⽅法:public PrintWriter(Writer out, boolean autoFlush)
autoFlush - boolean 变量;如果为 true,则 println、printf 或 format ⽅法将
⾃动刷新输出缓冲区
但是执⾏print⽅式时需要⼿动刷新

编码问题

开发中的编码

​ 常⽤字符集
​ 中国的字符集:GBK/GB2312
​ 欧洲的:ISO8859-1
​ 通⽤的:UTF-8

​ 美国的:ASCII

对中⽂的处理
⼀个汉字:GBK:2个字节 ISO8859-1:1个字节 utf-8:3个字节 unicode:2个字节(内部编码)
说明:GBK,UTF-8是⽀持中⽂的,ISO8859-1不⽀持中⽂
编码:将字符串转化成byte序列的过程
解码:是将byte序列转成字符串的过程
编码错误:乱码:在执⾏读与写的时候,由于使⽤的字符集不同,造成了编码的错误.
解决办法:再编码再解码
常⽤编码解码⽅法

编码:

byte[] getBytes() //对于中⽂ 默认的格式
使⽤平台的默认字符集将此 String 编码为 byte 序列,
并将结果存储到⼀个新的 byte 数组中。
    
byte[] getBytes(Charset charset)
使⽤给定的 charset 将此 String 编码到 byte 序列,并将结果存储到新的
byte 数组。
    
解码:

String(byte[] bytes) //对于中⽂ 默认是格式
通过使⽤平台的默认字符集解码指定的 byte 数组,构造⼀个新的 StringString(byte[] bytes, Charset charset)
通过使⽤指定的 charset 解码指定的 byte 数组,构造⼀个新的 String

中⽂乱码处理

注意点:乱码解决的办法是再编码再解码
但是如果是编码出错了,⽆法解决.
如果是解码出错了,可以利⽤再编码再解码
使⽤常⽤字符集GBK,utf8,ISO8859-1,进⾏编码解码出现乱码的情况分析

/* 编码		解码		 	结果
* GBK 		utf8 		不可以(GBK2个字节,utf83个字节)
* GBK 		ISO8859-1 	可以
* utf8 		GBK 		有时可以
* utf8 		ISO8859-1 	可以
* ISO8859-1 GBK 		不可以(编码就出错了)
* ISO8859-1 utf8 		不可以(编码就出错了)
*/

总结:只有使⽤GBK或utf8进⾏编码,使⽤ISO8859-1进⾏解码,才能使⽤再编码再解码解决乱码问题

转换流的编码问题演示

由于转换流实现的是字节流和字符流之间的转换,所以会存在编码问题
示例代码
分析:写⼊数据—“冰冰”
⾸先使⽤GBK编码,utf-8解码
然后使⽤utf-8编码,GBK解码
代码运⾏结果

readData1:冰冰
readData2:鍐板啺
readData3:����
readData4:冰冰

这⾥使⽤的是utf8和GBK进⾏的编码解码测试,只有在编码和解码使⽤同⼀种字符集时,才是正确的编码⽅法

序列化流

简介

将短期存储的数据实现⻓期存储,这个过程对应的流就是序列化流
数据的存储分成两类:
1.短期存储:存放在内存中,随着程序的关闭⽽释放—对象,集合,变量,数组
2.⻓期存储:存储在磁盘中,即使程序关闭了,数据仍然存在------⽂件
序列化:将数据从内存放⼊磁盘,可以实现数据的⻓久保存
反序列化:将数据从磁盘放回内存

注意事项

ObjectInputStream、 ObjectOutputStream, 主要是⽤来做对象的序列化和反序列化的。
序列化、 反序列化, 是对象的持久化存储的⼀种常⽤⼿段。
所有的要序列化到本地的类的对象, 类必须实现java.io.Serilizable 接⼝。

实现了Serializable接⼝的类可以达到的⽬的:
1.可以进⾏序列化
2.进⾏序列化的类的元素都必须⽀持序列化
3.可序列化类的所有⼦类型本身都是可序列化的。
4.接⼝本身没有⽅法或字段,只是⽤来表示可序列化的语义

如果需要序列化多个⽂件到本地, 尽量不要序列化到⼀个⽂件中。 如果需要序列化多个⽂件到本地, 通常采⽤的⽅式, 是存集合。 将多个对象存⼊⼀个集合中, 将这个集合序列化到本地。

常见问题总结

注意点:
1. ClassNotFoundException:当前的类没有找到
分析:将Person对象进⾏序列化之后,将Person类删除,再进⾏反序列化的时候出现了
异常
原因:反序列化在执⾏的时候依赖字节码⽂件,当类没有了,字节码⽂件⽆法创建,反序列化失败
2.java.io.InvalidClassException ⽆效的类
出现的原因:没有声明⾃⼰的serialVersionUID,⽽使⽤系统的.在进⾏反序列化的时候,类被改动了,系统认为现在的类
已经不是原来的类了(在使⽤系统的id进⾏识别的时候,重写给Person设置了id),认为此类⽆效
3.使⽤系统的serialVersionUID与⾃定义的ID的区别?
使⽤系统的,序列化和反序列化,id不能⼿动设置,使⽤的是编译器默认⽣成的,⼀旦类发⽣了改动,id会重新赋值
使⽤⾃定义的,序列化和反序列化,id不会发⽣改变,所以当反序列化的时候,即使对Person类进⾏了⼀些改动,也能继续反序列化

总结

合理使⽤序列化流和反序列化流,要与输⼊流与输出流配合使⽤
进⾏序列化的类⼀定要实现Serializable接⼝,只要实现了接⼝就可以序列化.包括集合,包装类等
进⾏序列化的类要保证当前类与内部的类都要实现Serializable接⼝

Properties

简介

概述
Properties也不是⼀个IO流, 是⼀个集合。 是Hashtable的⼦类。
使⽤Properties主要是为了描述程序中的属性列表⽂件。 有时候, 我们会将⼀些⽐较简单的项⽬的配置信息, 以 .properties 格式的⽂件进⾏存储。 可以使⽤Properties对象读写 .properties ⽂件。
注意
因为存储的是属性,属性本来就是以键值对的⽅式存储.这⾥的键和值都必须是字符串.所以不需要考虑泛型
为什么要在这⾥讲Properties?
因为他的使⽤与流紧密相关
Properties作⽤
1.是HashTable的⼦类,所以也是键值对形式,保存,操作更容易
2.默认键值都是字符串,所以不需要再考虑泛型
3.提供了⼀批好⽤的⽅法,⽅便使⽤(load(),store(),list()等)

基本使⽤

public static void fun1(){
	Properties properties = new Properties();
	properties.setProperty("first","java");
	properties.setProperty("second","php");
	properties.setProperty("third","python");
	System.out.println(properties);//重写了toString⽅法
	properties.setProperty("first","haha");//key是唯⼀的,后⾯
的值会将前⾯的值覆盖
	System.out.println(properties.get("first"));
	//当前的key在Properties中不存在时,会打印c
System.out.println(properties.getProperty("first1","c"));
	System.out.println(properties);
}

获取系统属性

//获取系统属性
Properties properties = System.getProperties();
//System.out.println(properties);
//获取所有键的名字
Set<String> set = properties.stringPropertyNames();
//遍历得到值
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()){
	String key = iterator.next();
	System.out.println("key:"+key+"
	value:"+properties.getProperty(key));
}
//第⼀次修改属性信息
properties.setProperty("user.language","ch");

实际应⽤

加载⼀个 .properties ⽂件中的数据,后缀名也可以不是properties.但是⼀般我们写成properties,⽅便使⽤.
a.properties⽂件内容
注意:
1.这⾥是修改过⼀次的⽂件
2.Propertes⽂件中对应的也应该是键值对
3.键和值之间可以是=或者空格或者冒号
4.默认每⾏只写⼀个键值对

name=我们
wss|06=
zhaoliu,05=
zhangsan=02
lisi=03
bingbing=buok
zhaoliu=04

value:"+properties.getProperty(key));
}
//第⼀次修改属性信息
properties.setProperty(“user.language”,“ch”);


## 实际应⽤

加载⼀个 .properties ⽂件中的数据,后缀名也可以不是properties.但是⼀般我们写成properties,⽅便使⽤.
		a.properties⽂件内容
**注意:**
1.这⾥是修改过⼀次的⽂件
2.Propertes⽂件中对应的也应该是键值对
3.键和值之间可以是=或者空格或者冒号
4.默认每⾏只写⼀个键值对

```java
name=我们
wss|06=
zhaoliu,05=
zhangsan=02
lisi=03
bingbing=buok
zhaoliu=04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值