IO流的分类
根据流方向分为输入流、输出流
根据操作的内容的不同可以分为字符流、字节流
得到四大基本流:
输入流 | 输入流 | |
---|---|---|
字符流 | Reader | Writer |
字符流 | InputStream | OutputStream |
这四大基本流都是抽象的,使用时通常使用这些抽象类的具体实现类
整个IO流体系
字符输入流
Reader -> InputStreamReader -> FileReader
部分构造方法:
FileReader(File file)
创建一个新的 FileReader ,给出 File读取。
FileReader(String fileName)
创建一个新的 FileReader ,给定要读取的文件的名称。
重要方法:
int read():读一个字符。
int read(char[] cbuf):将字符读入数组。
abstract int read(char[] cbuf, int off, int len):将字符读入数组的一部分。
abstract void close():关闭流并释放与之相关联的任何系统资源。
为什么read 方法返回的不是char 而是 int ?
因为如果返回的是char ,则为无法用任意的char 表示到达了文件的结尾,所以此处不能返回char 而是返回int 类型。正常情况下返回的是正数,强转为char 则得到对应的字符,当达到文件结尾时返回-1表示。
字符输出流
Writer -> OutputStreamWriter -> FileWriter
部分构造方法
FileWriter(File file)
给一个File对象构造一个FileWriter对象。
FileWriter(File file, boolean append)
给一个File对象构造一个FileWriter对象。
FileWriter(String fileName)
构造一个给定文件名的FileWriter对象。
FileWriter(String fileName, boolean append)
构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。
通过boolean的append参数,可以指定数据是否追加,如果传入false(默认就是false)则,会产生新的文件覆盖旧的文件,如果传入true,则在原有文件的基础上进行追加。
重要方法:
abstract void close()
关闭流之前先刷新。
abstract void flush()
刷新该流的缓冲。
void write(char[] cbuf)
写入一个字符数组。
abstract void write(char[] cbuf, int off, int len)
写入字符数组的一部分。
void write(int c)
写一个字符
void write(String str)
写一个字符串
void write(String str, int off, int len)
写一个字符串的一部分。
在输出数据时,有部分数据可能会缓冲在流的内部,通过调用flush()方法可以强制刷新流,将缓存在流内部的数据刷出去,所以在做writer()方法之后,最好做一次flush()冲刷。
调用close()方法时,close方法内部会隐含的进行一次flush(),防止在关流时有数据死在缓冲区。
字节输出流
OutputStream -> FileOutputStream
构造方法:
FileOutputStream(File file)
创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(File file, boolean append)
创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(String name)
创建文件输出流以指定的名称写入文件。
FileOutputStream(String name, boolean append)
创建文件输出流以指定的名称写入文件。
通过boolean的append参数,可以指定数据是否追加,如果传入false(默认就是false)则,会产生新的文件覆盖旧的文件,如果传入true,则在原有文件的基础上进行追加。
重要方法:
void close()
关闭此输出流并释放与此流相关联的任何系统资源。
void flush()
刷新此输出流并强制任何缓冲的输出字节被写出。
void write(byte[] b)
将 b.length字节从指定的字节数组写入此输出流。
voidwrite(byte[] b, int off, int len)
从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。
在利用输出流输出数据的过程中,流的底层具有缓冲机制提升效率,但同时也有可能造成部分数据堆积在底层流的缓冲区中,一时无法写出,此时可以调用flush()方法,手动的将流中缓冲的数据写出。
close()方法关闭流,在关闭流的过程中,会隐含的调用一次flush() 保证不会有数据死在缓冲区里。
流中的异常处理
1、要将流对象放到try之外定义并且赋值为null,放到try之内创建。
2、在关流之前需要判断是否为空。
3、关流之后需要将流对象强制置为null。
4、写完数据之后需要手动冲刷一次缓冲区。
自定义缓冲区
对于字符流是自定义一个char 类型的数组作为缓冲区
对于字节流是自定义一个byte 类型的数组作为缓冲区
这样一次拷贝一个数组,来提升新能。
@Test
public void test4(){
File file1 = new File("E:\\Intellij IDEA\\javaTest\\src\\demo_17\\s1.txt");
File file2 = new File("E:\\Intellij IDEA\\javaTest\\src\\demo_17\\s2.txt");
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
try {
inputStream = new FileInputStream(file1);
outputStream = new FileOutputStream(file2);
// 创建一个字节数组作为缓冲区
byte[] bytes = new byte[5];
// 定义一个变量表示读取的字节个数
int len = 0;
while ((len = inputStream.read(bytes))!=-1){
outputStream.write(bytes,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inputStream!=null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
缓冲流
java 中提供了自带缓冲区的流BufferedReader、BufferedWriter,用于装饰其他流,提升读写性能,并提供了新的方法。利用FileReader来构建了BufferedReader,然后再BufferedReader对读取功能做了增强,这种方式称之为装饰设计模式 —- 利用了同类对象构建自己对象本身,对对象身上的功能做了增强或者改善。
BufferedWriter提供了一个更大的缓冲区,能到很大的提升复制的效率。
public class BufferedRead erextends Reader
构造方法:
BufferedReader(Reader in)
创建使用默认大小的输入缓冲区的缓冲字符输入流。
BufferedReader(Reader in, int sz)
创建使用指定大小的输入缓冲区的缓冲字符输入流。
重要方法:
int read()
读一个字符
int read(char[] cbuf, int off, int len)
将字符读入数组的一部分。
StringreadLine()
读一行文字。
void close()
关闭流并释放与之相关联的任何系统资源。
public class BufferedWriter extends Writer
构造方法:
BufferedWriter(Writer out)
创建使用默认大小的输出缓冲区的缓冲字符输出流。
BufferedWriter(Writer out, int sz)
创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。
重要方法:
void close()
关闭流,先刷新。
void flush()
刷新流。
void newLine()
写一行行分隔符。
void write(char[] cbuf, int off, int len)
写入字符数组的一部分。
void write(int c)
写一个字符
void write(String s, int off, int len)
写一个字符串的一部分。
/**
* 字节缓冲流实现文本的复制
*/
@Test
public void test5(){
File file1 = new File("E:\\Intellij IDEA\\javaTest\\src\\demo_17\\s1.txt");
File file2 = new File("E:\\Intellij IDEA\\javaTest\\src\\demo_17\\s3.txt");
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
FileInputStream fis = new FileInputStream(file1);
FileOutputStream fos = new FileOutputStream(file2);
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
int len = 0;
byte[] bytes = new byte[1024];
while ((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (bos!=null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis!=null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字节流和字符流的桥梁 - 转换流
转换流概述:
字符流底层也是字节流,只不过在字节流的基础上增加了缓冲区和编解码器。
字符流内置的编解码器默认采用的编码集是系统码,并且无法修改。
这在使用字符流读入非系统码的字符数据集时就会造成乱码。
此时无法通过字符流来解决,java 提供了转换流,可以字节编写字节流来读取数据,再通过转换流转换为字符流,并在这个过程中手动指定码表,从而实现指定码表的自定义字符流。
public class InputStreamReader extends Reader
部分构造方法:
InputStreamReader(InputStream in)
创建一个使用默认字符集的 InputStreamReader。
InputStreamReader(InputStream in, String charsetName)
创建使用指定字符集的 InputStreamReader。
public class OutputStreamWriter extends Writer
部分构造方法:
OutputStreamWriter(OutputStream in)
创建一个使用默认字符集的 OutputStreamWriter。
OutputStreamWriter(OutputStream in, String charsetName)
创建使用指定字符集的 OutputStreamWriter。
图解:
实例:
拷贝一个utf-8编码集包含中文的文本文件 要求产生的文件是gbk编码。
/**
* 转换流
*
* 案例:通过转换流 生成自定义码表的字符流
* 复制文件 将utf-8文件转换为gbk格式的文件
*/
@Test
public void test6(){
File file1 = new File("E:\\Intellij IDEA\\javaTest\\src\\demo_17\\s1.txt");
File file2 = new File("E:\\Intellij IDEA\\javaTest\\src\\demo_17\\s3.txt");
InputStreamReader inputStreamReader = null;
OutputStreamWriter outputStreamWriter = null;
try {
// 1.创建字节流
FileInputStream fis = new FileInputStream(file1);
FileOutputStream fos = new FileOutputStream(file2);
// 2.创建转换流 将字节流转换为字符流 并显式指定码表为utf-8
inputStreamReader = new InputStreamReader(fis,"utf-8");
outputStreamWriter = new OutputStreamWriter(fos,"utf-8");
char[] chars = new char[1024];
int len = 0;
while ((len = inputStreamReader.read(chars)) != -1){
outputStreamWriter.write(chars,0,len);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (inputStreamReader!=null){
try {
inputStreamReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStreamWriter!=null){
try {
outputStreamWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
对象流-序列化与反序列化
对象的序列化
-
凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
private static final long serialVersionUID;
serialVersionUID用来表明类的不同版本间的兼容性。 简言之,其目的是以序列化对象
进行版本控制,有关各版本反序列化时是否兼容。
如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自
动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,
显式声明。 -
简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验
证版本一致性的。 在进行反序列化时,JVM会把传来的字节流中的
serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同
就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异
常。(InvalidCastException) -
由于大部分作为参数的类如String 、Integer 等都实现了java.io.Serializable 的接口,也可以利用多态的性质,作为参数使接口更灵活。
/** 序列化*/
@Test
public void test7(){
File file1 = new File("E:\\Intellij IDEA\\javaTest\\src\\demo_17\\s1.txt");
ObjectOutputStream objectOutputStream = null;
try {
objectOutputStream = new ObjectOutputStream(new FileOutputStream(file1));
objectOutputStream.writeObject(new Persion(1,"张三"));
objectOutputStream.flush();
objectOutputStream.writeObject(new String("李四"));
objectOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (objectOutputStream!=null){
try {
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 反序列化
* */
@Test
public void test8() {
File file1 = new File("E:\\Intellij IDEA\\javaTest\\src\\demo_17\\s1.txt");
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(new FileInputStream(file1));
Persion persion = (Persion) objectInputStream.readObject();
String s = (String) objectInputStream.readObject();
System.out.println(persion);
System.out.println(s);
} catch (IOException e) {
e.printStackTrace();
}catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
if (objectInputStream!=null){
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}