继续学习IO流:
分类:
- 字节流
- OutputStream
- FileOutputStream
- BufferedOutputStream
- ObjectOutputStream
- FileOutputStream
- InputStream
- FileInputStream
- BufferedInputStream
- ObjectInputStream
- FileInputStream
- OutputStream
- 字符流
- Writer
- OutputStreamWriter
- FileWriter
- BufferedWriter
- OutputStreamWriter
- Reader
- InputStreamReader
- FileReader
- BufferedReader
- InputStreamReader
- Writer
上篇学习完了一些基本的输入输出流,本篇学习一些更强大的流——高效读写的缓冲流、转换编码的转换流、持久化存储对象的序列化流:
- 缓冲流(BufferedOutputStream、BufferedInputStream、BufferedWriter、BufferedReader)
- 转换流(OutputStreamWriter、InputStreamReader)
- 序列化流(ObjectOutputStream、ObjectInputStream)
缓冲流
也叫高效流,创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO的次数,从而提高读写的效率。
缓冲流中并没有增加什么新的方法,
缓冲流都是和基本流一起来使用,提高了读写效率。(构造方法中都要传入一个 基本流)
字节缓冲流
BufferedOutputStream
继承自FileOutputStream,它的构造方法中通常传的也是FileOutputStream对象
构造方法:
-
public BufferedOutputStream(OutputStream out)创建一个BufferedOutputStream,保存其参数,输出流供以后使用(默认缓冲区大小,8192)
-
public BufferedOutputStream(OutputStream out,int size)创建一个BufferedOutputStream,保存其参数,输出流供以后使用,并指定其缓冲区大小
BufferedInputStream
继承自FileInputStream,它的构造方法中通常传的也是FileInputStream对象
构造方法:
- public BufferedInputStream(InputStream in)创建一个BufferedInputStream,保存其参数,输入流供以后使用
- public BufferedInputStream(InputStream in,int size)创建一个BufferedInputStream,保存其参数,输入流供以后使用,并指定其缓冲区大小
效率测试:
import java.io.*;
/**
* 字节流拷贝文件
*/
public class Test1 {
public static void main(String[] args) throws IOException {
long startTime = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("F:\\FileTest\\jre-8u202-windows-x64.exe");
FileOutputStream fos = new FileOutputStream("F:\\FileTest\\jre.exe");
byte[] b = new byte[1024];
int i=fis.read(b);
while (i!=-1){
fos.write(b);
i = fis.read(b);
}
fos.flush();
fis.close();
fos.close();
long endTime = System.currentTimeMillis();
System.out.println("耗时:"+(endTime-startTime)+"毫秒");
//660毫秒
}
}
import java.io.*;
/**
* 字节缓冲流拷贝文件
*/
public class Test2 {
public static void main(String[] args) throws IOException {
long startTime = System.currentTimeMillis();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("F:\\FileTest\\jre-8u202-windows-x64.exe"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("F:\\FileTest\\jre2.exe"));
byte[] b = new byte[1024];
int i = bis.read(b);
while (i!=-1){
bos.write(b);
i =bis.read(b);
}
bos.flush();
bos.close();
bis.close();
long endTime = System.currentTimeMillis();
System.out.println("耗时:"+(endTime-startTime)+"毫秒");
//130毫秒
}
}
可以自己测试一下,拷贝越大的文件效果越明显
缓冲流增加了读写的效率,同样因为字符缓冲区的存在,占用的内存会比基本流多
字符缓冲流
BufferedWriter
继承自Writer,构造方法中通常传入一个它父类的实现类FileWriter的对象
构造方法:
- public BufferedWriter(Writer out)创建一个字符缓冲流,输出流供以后使用(默认缓冲区大小,8192)
- public BufferedWriter(Writer out,int size)创建一个字符缓冲流,输出流供以后使用,并指定其缓冲区大小
特有方法:
- public void newline():写一行行分隔符(就是换行的作用)
import java.io.*;
public class Test3 {
public static void main(String[] args) throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\FileTest\\bufferedWriter.txt"));
bw.write("一个字符串");
bw.newLine(); //换行
bw.write("第二个字符串");
bw.close();
}
}
BufferedReader
继承自Reader,构造方法中通常传入一个它父类的实现类FileReader的对象
构造方法:
- public BufferedReader(Reader in)创建一个字符缓冲流,输入流供以后使用(默认缓冲区大小,8192)
- public BufferedReader(Reader in,int size)创建一个字符缓冲流,输入流供以后使用,并指定其缓冲区大小
特有方法:
- public String readLine():读一行字符
import java.io.*;
public class Test4 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("F:\\FileTest\\bufferedWriter.txt"));
String str = br.readLine(); //每次读取一行字符,返回一个字符串
while (str!=null){
System.out.println(str);
str = br.readLine();
}
br.close();
}
}
转换流
从字节流到字符流的桥梁。它使用指定的字符集将其编码和解码为字符,所使用的字符集可以自己指定。
OutputStreamWriter
继承自Writer,输出流的转换流,它的直接子类是FileWriter
构造方法:
- public OutputStreamWriter(OutputStream out)使用默认的字符集创建一个转换流
- public OutputStreamWriter(OutputStream out,Charset cs)使用指定的字符集创建一个转换流
import java.io.*;
public class Test5 {
public static void main(String[] args) throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("F:\\FileTest\\a.txt"),"GBK");
osw.write("123abc");
osw.write("哈哈");
osw.flush();
osw.close();
}
}
InputStreamReader
继承自Reader,输入流的转换流,它的直接子类是FileReader
构造方法:
- public InputStreamReaderr(InputStream in)使用默认的字符集创建一个转换流
- public InputStreamReader(InputStream in,Charset cs)使用指定的字符集创建一个转换流
import java.io.*;
public class Test6 {
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("F:\\FileTest\\a.txt"),"GB18030");
int i = isr.read();
//返回此流使用的编码格式
System.out.println(isr.getEncoding());
while (i!=-1){
System.out.println((char) i);
i = isr.read();
}
isr.close();
}
}
序列化流(对象输入输出流)
Java中提供了一种对象序列化的机制,用一个字节序列可以表示一个对象,该字节序列包含对象的数据、对象的类型、对象中存储的属性等信息,字节序列写出文件后,相当于文件中持久存储了一个对象信息。
反之,该字节序列还可以从文件中读取出来,重构对象,这就叫做反序列化,对象的数据,对象的流星和对象中存储的数据信息,都可以用来在内存中创建对象。
序列化后的数据可以以二进制的形式在网络中传输或者保存到本地,通过反序列化可以在任何一个终端进行数据的还原。
序列化一个对象的条件:
- 该类必须实现java.io.Serializable接口,Serializable是一个标记接口,凡是实现了这个接口的类的对象都可以被序列化
- 该类中的所有属性必须是可序列化的,如果有一个属性不需要序列化,则该属性需要使用transient(瞬态)修饰。
- 该类中还应该有一个序列化版本号,如果没有会自动生成,但是在反序列化时,会用到版本号,在没有版本号的情况下,就会发生异常,反序列化失败
//自定义一个Student类,实现Serializable接口
public class Student implements Serializable {
//定义一个版本号(可以使用工具生成,也可以自己写,格式不能错)
private static final long serialVersionUID = -4426508077041924239L;
//成员属性···
//成员方法···
}
ObjectOutputStream(序列化)
构造方法:
- public ObjectOutputStream(OutputStream out) 创建一个写入指定out的对象输出流
常用方法:
返回值 | 方法名 | 说明 |
---|---|---|
void | writeObject(Object obj) | 写入一个obj对象到ObjectOutputStream |
void | flush() | 刷新流 |
void | close() | 关闭流 |
ObjectOutputStream中不止能写入对象,还有很多写入基本数据类型的方法:writerByte、writerShort、writerInt、writerLong、writerFloat、writerDouble、writerBoolean、writerChar。还有:writerBytes写入一个字符串作为一个字节序列、writerChars写入一个字符串作为字符序列
import java.io.*;
public class Test7 {
public static void main(String[] args) {
File file = new File("F:\\student.txt");
try (
OutputStream os = new FileOutputStream(file,true);
ObjectOutputStream oos = new ObjectOutputStream(os);
){
Student s1 = new Student(1001,"张三",20);
Student s2 = new Student(1002,"李四",20);
Student s3 = new Student(1003,"王五",20);
oos.writeObject(s1);
oos.writeObject(s2);
oos.writeObject(s3);
}catch (Exception e){
e.printStackTrace();
}
}
}
写出的数据是二进制的形式,保存的文件类型可以随意,使用txt打开我们也看不懂,只有反序列化流才能读懂它。
ObjectInputStream(反序列化)
构造方法:
- public ObjectInputStream(InputStream in) 创建一个读取指定in的对象输入流
常用方法:
返回值 | 方法名 | 说明 |
---|---|---|
int | available() | 返回可读取而不阻塞的字节数 |
Object | readObject() | 从ObjectInputStream中读取一个Object对象 |
void | close() | 关闭流 |
相应的ObjectOutputStream中不止能读取一个对象,还有很多读取基本数据类型的方法:readerByte、readerShort、readerInt、readerLong、readerFloat、readerDouble、readerBoolean、readerChar。
反序列化时要注意,此时反序列化的对象要和序列化时相同,而且版本号也要相同
import java.io.*;
public class Test8 {
public static void main(String[] args) {
File file = new File("F:\\student.txt");
try (
InputStream is = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(is);
){
while (is.available()>0){
Student student = (Student) ois.readObject();
System.out.println(student);
}
}catch (Exception e){
e.printStackTrace();
}
}
}