IO流
在java.io.*
包下,按数据流方向分为输入/输出流;按处理数据单位分为字符流(Reader
/Writer
)和字节流(InputStream
/OutputStream
);按功能不同分为节点流和处理流(又称为包装流,包装在别的流外边,关闭时只需关闭最外层的处理流即可)
流通用步骤
1.创建资源对象
2.创建字节(/字符)输入(/输出)源对象
3.具体的读取(/写入)源内容操作
4.关闭输入(/输出)对象和源对象
FileInputStream/FileOutputStream
文件输入/输出字节流,其中输出字节流默认为w
(覆盖)模式,如果要改为a
(追加)模式,则在实例化的第二个参数里设置为true
。其提供了常用方法如下:
1.read()
:输入流下的方法,该方法有以下几种传参方法:read(byte)
/read(byte[])
/read(byte[], index, len)
,分别为读取单个字节;读取一定长度字节并从索引位置为0开始存入字节数组;读取一定长度字节并从索引位置开始存入字节数组。当读取位置内容为空,则会返回-1
;第二种方法返回的是读取的字节长度,如果读取长度为0,则也返回-1
2.write()
:输出流下的方法,该方法有以下几种传参方法:write(byte)
/write(byte[])
/write(byte[], index, len)
,分别为传入单个字节/传入字节数组/从索引位置开始传入一定长度的字节数组
使用举例:
import java.io.*;
public static void main(String[] args){
int b = 0;
FileInputStream in = null;
FileOutputStream out = null;
try{
in = new FileInputStream("f:/a/1.txt"); //读取文件内容
out = new FileOutputStream("f:/a/2.txt"); //输出内容到文件,默认w模式,第二个参数如果设置为true,则为a模式
while((b=in.read())!=-1){
out.write(b);
}
in.close();
out.close();
System.out.println("文件复制成功!");
}catch(FileNotFoundException e){
System.out.println("找不到文件!");
System.exit(-1);
}catch(IOException e){
System.out.println("文件读取错误!");
System.exit(-1);
}
注:
资源使用后需要记得关闭,但是手动关闭十分麻烦,而且上面的代码也没用很好处理关闭时的异常,于是可以使用jdk1.7后提供的新特性里try语句的新用法:try(资源对象){异常语句}catch(){}
,其会在使用完资源对象后自动关闭,举例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class test {
public static void main(String[] args) throws Exception {
int b = 0;
try ( //jdk 1.7后新特性,将需要结束后关闭的资源对象代码放这里,将会自动关闭资源
FileInputStream in = new FileInputStream("f:/a/1.txt");
FileOutputStream out = new FileOutputStream("f:/a/2.txt");
) { //这里放可能会出现异常的代码
while ((b = in.read()) != -1) {
out.write(b);
}
// in.close(); //会自动关闭资源,不需要手动关闭
// out.close();
System.out.println("文件复制成功!");
} catch (FileNotFoundException e) {
System.out.println("找不到文件!");
System.exit(-1);
} catch (IOException e) {
System.out.println("文件读取错误!");
System.exit(-1);
}
}
}
FileReader/FileWriter
文件输入输出字符流,直接把上面的InputStream/OutputStream换成Reader/Writer就行,但要注意的是输出流的内容需要执行flush()
以后才能将内容从内存读写到磁盘当中,在执行close()
之前JVM也会自动调用一次flush()
BufferedInputStream/BufferOutputStream
缓冲输入输出字节流,是一种处理流,即包在节点流/处理流之上,会在内容先写到内存缓冲区,之后再一起写入到硬盘,举例:
int b = 0;
try{
BufferedInputStream bin = new BufferedInputStream(new FileInputStream("f:/a/1.txt")); //包在节点流之上
BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream("f:/a/2.txt"));
while((b=bin.read())!=-1){
bout.write(b);
System.out.println((char)b); //转字符后输出,否则输出的是数字
}
bout.flush(); //先把缓冲区的都写进硬盘
bout.close(); //然后在关闭
bin.close();
System.out.println("文件复制成功!");
}catch
同样缓冲流中也有BufferedReader
/BufferedWriter
的字符流,其中BufferedReader
提供了readLine()
方法,支持一行一行读取
InputStreamReader/InputStreamWriter
转换流,可以进行将字节输入(/输出)流转换成字符输入(/输出)流,也是处理流的一种,举例:
int b = 0;
try{
FileInputStream in = new FileInputStream("f:/a/1.txt");
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream("f:/a/2.txt", true), "gbk"); //第一个参数是节点/处理流,第二个是编码格式
while((b=in.read())!=-1){
out.write(b);
}
System.out.println(out.getEncoding()); //输出GBK
out.close();
}
DataInputStream/DataOutputStream
数据流,处理流的一种
PrintWriter/PrintStream
打印流,处理流的一种,只有输出流,其输出操作不会抛出异常,并且当调用printf
/println
/format
时会进行自动刷新(flush()
),如果没有开启自动刷新,则当缓冲区满时会进行自动刷新
PipeInputStream/PipeOutputStream
管道流,可以实现两个线程之间的数据交互
序列化和反序列化
https://www.cnblogs.com/xdp-gacl/p/3777987.html
实现序列化和反序列化
ObjectOutputStream/ObjectInputStream
实现对象序列化和反序列化的包装流,只能将支持序列化标记接口Serializable
的对象写入流中,举例:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class test {
public static void main(String[] args) throws Exception {
//序列化
File file = new File("f:/a/1.txt");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
out.writeObject(new Stu(100, "aaa")); //将Stu对象序列化至文件当中
out.close();
//反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Stu stu = (Stu)in.readObject();
System.out.println(stu); //id:100,name:aaa
in.close();
}
}
class Stu implements Serializable { // 继承序列化接口
private int id;
private String name;
public Stu(int id, String name) {
this.id = id;
this.name = name;
}
public String toString() {
return "id:" + this.id + ",name:" + this.name;
}
}
对于上面那样的一般情况,序列化会把所有的内容都进行反序列化,但是如果像密码这样不希望被反序列化的数据,那么可以通过设置成静态和瞬态变量来实现,其中瞬态变量可以通过关键字transient
修饰完成。举例:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class test {
public static void main(String[] args) throws Exception {
//序列化
File file = new File("f:/a/1.txt");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
out.writeObject(new Stu(100, "aaa", "bbb"));
out.close();
//反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Stu stu = (Stu)in.readObject();
System.out.println(stu); //id:100,name:aaa,password:null,可以看到password字段内容没有被访问到
in.close();
}
}
class Stu implements Serializable {
private int id;
private String name;
transient String password; //设置瞬态变量
public Stu(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public String toString() {
return "id:" + this.id + ",name:" + this.name + ",password:" + this.password;
}
}
序列化版本问题
对于序列化对象,如果该对象的类被修改后再进行反序列化,那么就会发现因为序列化版本不同的问题而无法成功反序列化。因此有下面的解决方案:给类设置版本号,举例:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class test {
public static void main(String[] args) throws Exception {
//序列化
File file = new File("f:/a/1.txt");
// ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
// out.writeObject(new Stu(100, "aaa", "bbb")); //先进行序列化,然后修改类的内容
// out.close();
//反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
Stu stu = (Stu)in.readObject();
System.out.println(stu); //把password内容都注释后读取,会发现依然可以反序列化,结果变成:id:100,name:aaa
in.close();
}
}
class Stu implements Serializable {
private static final long serialVersionUID = 1L; //设置版本号
private int id;
private String name;
transient String password;
public Stu(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public String toString() {
return "id:" + this.id + ",name:" + this.name + ",password:" + this.password;
}
}
注:
前面的序列化标记接口是不可控制的,如果想要可以自己控制序列化过程那么可以使用Externalizable
接口
网友评论