缓冲流、转换流、序列化流、打印流总结

第一章 缓冲流
缓冲流 , 也叫高效流,是对 4 个基本的 FileXxx 流的增强,所以也是 4 个流,按照数据类型分类:
字节缓冲流 BufferedInputStream BufferedOutputStream
字符缓冲流 BufferedReader BufferedWriter
缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统 IO
次数,从而提高读写的效率。
 1.1 字节缓冲流
构造方法
public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。
public BufferedOutputStream(OutputStream out) : 创建一个新的缓冲输出流。
构造举例,代码如下:
// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream ( new FileInputStream ( "a.txt" ));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream ( new FileOutputStream ( "b.txt" ));
缓冲流,代码如下:
// 记录开始时间
long start = System . currentTimeMillis ();
// 创建流对象
try (
BufferedInputStream bis = new BufferedInputStream ( new FileInputStream ( "jdk8.exe" ));
BufferedOutputStream bos = new BufferedOutputStream ( new FileOutputStream ( "copy.exe" ));
){
// 读写数据
int b ;
while (( b = bis . read ()) != - 1 ) {
bos . write ( b );
}
} catch ( IOException e ) {
e . printStackTrace ();
}
// 记录结束时间
long end = System . currentTimeMillis ();
System . out . println ( " 缓冲流复制时间 :" + ( end - start ) + " 毫秒 " );
 
 
如何更快,使用数组的方式,代码如下:
// 记录开始时间
long start = System . currentTimeMillis ();
// 创建流对象
try (
BufferedInputStream bis = new BufferedInputStream ( new FileInputStream ( "jdk8.exe" ));
BufferedOutputStream bos = new BufferedOutputStream ( new FileOutputStream ( "copy.exe" ));
){
// 读写数据
int len ;
byte [] bytes = new byte [ 1024*10 ];
while (( len = bis . read ( bytes )) != - 1 ) {
bos . write ( bytes , 0 , len );
}
} catch ( IOException e ) {
e . printStackTrace ();
}
// 记录结束时间
long end = System . currentTimeMillis ();
System . out . println ( " 缓冲流使用数组复制时间 :" + ( end - start ) + " 毫秒 " );
 
1.2  字符缓冲流
构造方法
public BufferedReader(Reader in) :创建一个 新的缓冲输入流。
public BufferedWriter(Writer out) : 创建一个新的缓冲输出流。
构造举例,代码如下:
// 创建字符缓冲输入流
BufferedReader br = new BufferedReader ( new FileReader ( "br.txt" ));
// 创建字符缓冲输出流
BufferedWriter bw = new BufferedWriter ( new FileWriter ( "bw.txt" ));
特有方法
字符缓冲流的基本方法与普通字符流调用方式一致,不再阐述,我们来看它们具备的特有方法。
BufffferedReader public String readLine() : 读一行文字。
BufffferedWriter public void newLine() : 写一行行分隔符 , 由系统属性定义符号。
readLine 方法演示,代码如下:
 
// 创建流对象
BufferedReader br = new BufferedReader ( new FileReader ( "a.txt" ));
// 定义字符串 , 保存读取的一行文字
String line = null ;
// 循环读取 , 读取到最后返回 null
while (( line = br . readLine ()) != null ) {
System . out . print ( line );
System . out . println ( "------" );
}
// 释放资源
br . close ();
 
newLine 方法演示,代码如下:
// 创建流对象
BufferedWriter bw = new BufferedWriter ( new FileWriter ( "a.txt" ));
// 写出数据
bw . write ( "硅谷 " );
// 写出换行
bw . newLine ();
bw . write ( " 程序 " );
bw . newLine ();
bw . write ( " " );
bw . newLine ();
// 释放资源
bw . close ();
第二章 转换流
2.1 字符编码和字符集
字符编码
计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制
数转换之后的结果。按照某种规则,将字符存储到计算机中,称为 编码 。反之,将存储在计算机中的二进制数按照
某种规则解析显示出来,称为 解码 。比如说,按照 A 规则存储,同样按照 A 规则解析,那么就能显示正确的文本 f
号。反之,按照 A 规则存储,再按照 B 规则解析,就会导致乱码现象。
编码 : 字符转换为字节
解码 : 字节转换为字符
字符编码 Character Encoding : 就是一套自然语言的字符与二进制数之间的对应规则。
生活中的文字和计算机文字的对应关系 a-->97-->1100001 -->20013--100111000101101
 
字符集
字符集 Charset :也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符
号、数字等。
计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符
集有 ASCII 字符集、 GBK 字符集、 Unicode 字符集等。
 
 
当指定了 编码 ,它所对应的 字符集 自然就指定了,所以 编码 才是我们最终要关心的。
 
ASCII 字符集
ASCII American Standard Code for Information Interchange ,美国信息交换标准代码)是基于拉丁
字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显
示字符(英文大小写字符、阿拉伯数字和西文符号)。
基本的 ASCII 字符集,使用 7 位( bits )表示一个字符,共 128 字符。 ASCII 的扩展字符集使用 8 位( bits
表示一个字符,共 256 字符,方便支持欧洲常用字符。
ISO-8859-1 字符集
拉丁码表,别名 Latin-1 ,用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。
ISO-5559-1 使用单字节编码,兼容 ASCII 编码。
GBxxx 字符集
GB 就是国标的意思,是为了显示中文而设计的一套字符集。
GB2312 :简体中文码表。一个小于 127 的字符的意义与原来相同。但两个大于 127 的字符连在一起时,
就表示一个汉字,这样大约可以组合了包含 7000 多个简体汉字,此外数学符号、罗马希腊的字母、日文
的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这
就是常说的 " 全角 " 字符,而原来在 127 号以下的那些就叫 " 半角 " 字符了。
GBK :最常用的中文码表。是在 GB2312 标准基础上的扩展规范,使用了双字节编码方案,共收录了
21003 个汉字,完全兼容 GB2312 标准,同时支持繁体汉字以及日韩汉字等。
GB18030 :最新的中文码表。收录汉字 70244 个,采用多字节编码,每个字可以由 1 个、 2 个或 4 个字节
组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。
Unicode 字符集
Unicode 编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国
码。
它最多使用 4 个字节的数字来表达每个字母、符号,或者文字。有三种编码方案, UTF-8 UTF-16 UTF-
32 。最为常用的 UTF-8 编码。
UTF-8 编码,可以用来表示 Unicode 标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用
中,优先采用的编码。互联网工程工作小组( IETF )要求所有互联网协议都必须支持 UTF-8 编码。所以,
我们开发 Web 应用,也要使用 UTF-8 编码。它使用一至四个字节为每个字符编码,编码规则:
1. 128 US-ASCII 字符,只需一个字节编码。
2. 拉丁文等字符,需要二个字节编码。
3. 大部分常用字(含中文),使用三个字节编码。
4. 其他极少使用的 Unicode 辅助字符,使用四字节编码。
 
2.2 编码引出的问题
 
IDEA 中,使用 FileReader 读取项目中的文本文件。由于 IDEA 的设置,都是默认的 UTF - 8 编码,所以没有任何
问题。但是,当读取 Windows 系统中创建的文本文件时,由于 Windows 系统的默认是 GBK 编码,就会出现乱码。
 
FileReader fileReader = new FileReader ( "D:\\a.txt" );
int read ;
while (( read = fileReader . read ()) != - 1 ) {
System . out . print (( char ) read );
}
fileReader . close ()
 
输出结果:
���
 
2.3 InputStreamReader
 
转换流 java.io.InputStreamReader ,是 Reader 的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定
的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
 
构造方法
InputStreamReader(InputStream in) : 创建一个使用默认字符集的字符流。
InputStreamReader(InputStream in, String charsetName) : 创建一个指定字符集的字符流。
构造举例,代码如下
InputStreamReader isr1  = new InputStreamReader ( new FileInputStream ( "a.txt" ));
InputStreamReader isr2 = new InputStreamReader ( new FileInputStream ( "a.txt" ) , "GBK" );
指定编码读取
 
// 定义文件路径 , 文件为 gbk 编码
String FileName = "D:\\a.txt" ;
// 创建流对象 , 默认 UTF8 编码
InputStreamReader isr1  = new InputStreamReader ( new FileInputStream ( FileName ));
// 创建流对象 , 指定 GBK 编码
InputStreamReader isr2 = new InputStreamReader ( new FileInputStream ( FileName ) , "GBK" );
// 定义变量 , 保存字符
int read ;
// 使用默认编码字符流读取 , 乱码
while (( read = isr1 . read ()) != - 1 ) {
System . out . print (( char ) read ); // ��
}
isr1 . close ();
// 使用指定编码字符流读取 , 正常解析
while (( read = isr2 . read ()) != - 1 ) {
System . out . print (( char ) read ); // 你好
}
isr2 . close ();
2.4 OutputStreamWriter
 
转换流 java.io.OutputStreamWriter ,是 Writer 的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符
编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
 
构造方法
OutputStreamWriter(OutputStream in) : 创建一个使用默认字符集的字符流。
OutputStreamWriter(OutputStream in, String charsetName) : 创建一个指定字符集的字符流。
构造举例,代码如下:
OutputStreamWriter isr = new OutputStreamWriter ( new FileOutputStream ( "a.txt" ));
OutputStreamWriter isr2 = new OutputStreamWriter ( new FileOutputStream ( "a.txt" ) , "GBK" );
指定编码写出
 
// 定义文件路径
String FileName = "D:\\a.txt" ;
// 创建流对象 , 默认 UTF8 编码
OutputStreamWriter osw = new OutputStreamWriter ( new FileOutputStream ( FileName ));
// 写出数据
osw . write ( " 你好 " ); // 保存为 6 个字节
osw . close ();
// 定义文件路径
String FileName2 = "D:\\b.txt" ;
// 创建流对象 , 指定 GBK 编码
OutputStreamWriter osw2 = new OutputStreamWriter ( new FileOutputStream ( FileName2 ), "GBK" );
// 写出数据
osw2 . write ( " 你好 " ); // 保存为 4 个字节
osw2 . close ();
转换流理解图解
 
 
第三章 序列化
 
Java 提供了一种对象 序列化 的机制。用一个字节序列可以表示一个对象,该字节序列包含该 对象的数据 对象的
类型 对象中存储的属性 等信息。字节序列写出到文件之后,相当于文件中 持久保存 了一个对象的信息。
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行 反序列化 对象的数据 对象的类型 对象中
存储的数据 信息,都可以用来在内存中创建对象。
3.1 ObjectOutputStream
java.io.ObjectOutputStream 类,将 Java 对象的原始数据类型写出到文件 , 实现对象的持久存储。
 
构造方法
public ObjectOutputStream(OutputStream out) : 创建一个指定 OutputStream ObjectOutputStream
构造举例,代码如下:
 
FileOutputStream fileOut = new FileOutputStream ( "a.txt" );
ObjectOutputStream out = new ObjectOutputStream ( fileOut );
序列化操作
1. 一个对象要想序列化,必须满足两个条件 :
该类必须实现 java.io.Serializable 接口, Serializable 是一个标记接口,不实现此接口的类将不会使任
何状态序列化或反序列化,会抛出 NotSerializableException
该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient 关键字修饰。
 
public class Employee implements Serializable {
public String name ;
public String address ;
public transient int age ; // transient 瞬态修饰成员 , 不会被序列化
public void addressCheck () {
System . out . println ( "Address check : " + name + " -- " + address );
 }
}
2. 写出对象方法
 
public final void writeObject (Object obj) : 将指定的对象写出。
 
public class SerializeDemo {
public static void main ( String [] args ) {
Employee e = new Employee ();
e . name = "lisi"
e . address = "renminlu" ;
e . age = 20 ;
try {
// 创建序列化流对象
ObjectOutputStream out = new ObjectOutputStream ( new FileOutputStream ( "employee.txt" ));
// 写出对象
out . writeObject ( e );
// 释放资源
out . close ();
fileOut . close ();
System . out . println ( "Serialized data is saved" ); // 姓名,地址被序列化,年龄没有被序列
化。
} catch ( IOException i ) {
i . printStackTrace ();
}
}
3.3 ObjectInputStream
ObjectInputStream 反序列化流,将之前使用 ObjectOutputStream 序列化的原始数据恢复为对象。
 
构造方法
public ObjectInputStream(InputStream in) : 创建一个指定 InputStream ObjectInputStream
反序列化操作 1
如果能找到一个对象的 class 文件,我们可以进行反序列化操作,调用 ObjectInputStream 读取对象的方法:
public final Object readObject () : 读取一个对象。
Employee e = null ;
try {
// 创建反序列化流
FileInputStream fileIn = new FileInputStream ( "employee.txt" );
ObjectInputStream in = new ObjectInputStream ( fileIn );
// 读取一个对象
e = ( Employee ) in . readObject ();
// 释放资源
in . close ();
fileIn . close ();
} catch ( IOException i ) {
// 捕获其他异常
i . printStackTrace ();
return ;
} catch ( ClassNotFoundException c ) {
// 捕获类找不到异常
System . out . println ( "Employee class not found" );
c . printStackTrace ();
return ;
}
// 无异常 , 直接打印输出
System . out . println ( "Name: " + e . name ); 
System . out . println ( "Address: " + e . address ); 
System . out . println ( "age: " + e . age ); 
}
对于 JVM 可以反序列化对象,它必须是能够找到 class 文件的类。如果找不到该类的 class 文件,则抛出一个
ClassNotFoundException 异常。
 
反序列化操作 2
 
另外,当 JVM 反序列化对象时,能找到 class 文件,但是 class 文件在序列化对象之后发生了修改,那么反序列化操
作也会失败,抛出一个 InvalidClassException 异常。 发生这个异常的原因如下:
该类的序列版本号与从流中读取的类描述符的版本号不匹配
该类包含未知数据类型
该类没有可访问的无参数构造方法
Serializable 接口给需要序列化的类,提供了一个序列版本号。 serialVersionUID 该版本号的目的在于验证序
列化的对象和对应类是否版本匹配。
 
public class Employee implements Serializable {
// 加入序列版本号
private static final long serialVersionUID = 1L ;
public String name ;
public String address ;
// 添加新的属性 , 重新编译 , 可以反序列化 , 该属性赋为默认值 .
public int eid ;
public void addressCheck () {
System . out . println ( "Address check : " + name + " -- " + address );
}
}
3.4 序列化集合
 
1. 将存有多个自定义对象的集合序列化操作,保存到 list.txt 文件中。
2. 反序列化 list.txt ,并遍历集合,打印对象信息。
案例分析
1. 把若干学生对象 ,保存到集合中。
2. 把集合序列化。
3. 反序列化读取时,只需要读取一次,转换为集合类型。
4. 遍历集合,可以打印所有的学生信息
案例实现
public class SerTest {
public static void main ( String [] args ) throws Exception {
// 创建 学生对象
Student student = new Student ( " 老王 " , "laow" );
Student student2 = new Student ( " 老张 " , "laoz" );
Student student3 = new Student ( " 老李 " , "laol" );
ArrayList < Student > arrayList = new ArrayList <> ();
arrayList . add ( student );
arrayList . add ( student2 );
arrayList . add ( student3 );
// 序列化操作
// serializ(arrayList);
// 反序列化
ObjectInputStream ois = new ObjectInputStream ( new FileInputStream ( "list.txt" ));
// 读取对象 , 强转为 ArrayList 类型
ArrayList < Student > list = ( ArrayList < Student > ) ois . readObject ();
for ( int i = 0 ; i < list . size (); i ++ ){
Student s = list . get ( i );
System . out . println ( s . getName () + "--" + s . getPwd ());
}
}
private static void serializ ( ArrayList < Student > arrayList ) throws Exception {
// 创建 序列化流
ObjectOutputStream oos = new ObjectOutputStream ( new FileOutputStream ( "list.txt" ));
// 写出对象
oos . writeObject ( arrayList );
// 释放资源
oos . close ();
}
}
 
第四章 打印流
平时我们在控制台打印输出,是调用 print 方法和 println 方法完成的,这两个方法都来自于
java.io.PrintStream 类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。
 
4.1 PrintStream
public PrintStream(String fileName) : 使用指定的文件名创建一个新的打印流。
构造举例,代码如下:
PrintStream ps = new PrintStream ( "ps.txt" )
System.out 就是 PrintStream 类型的,只不过它的流向是系统规定的,打印在控制台上。不过,既然是流对象,
我们就可以 将数据输出到指定文本文件中。
// 调用系统的打印流 , 控制台直接输出 99
System . out . println ( 97 );
// 创建打印流 , 指定文件的名称
PrintStream ps = new PrintStream ( "ps.txt" );
// 设置系统的打印流流向 , 输出到 ps.txt
System . setOut ( ps );
// 调用系统的打印流 ,ps.txt 中输出 99
System . out . println ( 99 );

猜你喜欢

转载自blog.csdn.net/shkstart/article/details/108077277
今日推荐