详解IO流的使用(下)
前面对基本的字节输入输出流,字符输入输出流以及缓冲字节与字符输入输出流。本节对其他IO流的使用进行学习。
1.解码与编码:InputStreamReader与OutputStreamWriter
解码与编码输入输出流的使用,可以进行字节输入流输出流与字符输入输出流的转换。
1.1 解码:InputStreamReader
- 解码:字节输入流转字符输入流的转换流,同时还可以制定编码方式
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class MyTest {
public static void main(String[] args) throws IOException {
/**
* 解码:字节输入流转字符输入流的转换流,同时还能指定编码方式
* InputStreamReader
*
*
*/
/* //乱码的原因:k盘的io.txt文件编码是GBK,平台是UTf-8
FileReader fr = new FileReader("K:\\io.txt");
char[] arr = new char[1024];
int len;
while((len=fr.read(arr))!=-1){
String s = new String(arr, 0, len);
System.out.println(s);
}
fr.close();
*/
FileInputStream fis = new FileInputStream("K:\\io.txt");
//将fis流中的字节流按照GBK进行解码为字符流
InputStreamReader isr = new InputStreamReader(fis,"GBK");
//数据流向:K:\io.txt(GBK)-->fis(按字节)-->isr(GBK)解成字符流-->按字符进行读取
char[] arr = new char[10];
int len;
while ((len = isr.read(arr)) != -1) {
String s = new String(arr, 0, len);
System.out.println(s);
}
}
}
1.2 编码:OutputStreamReader
- 编码:将字符转换流转换为字节流的转换流,并且可以指定编码
import java.io.*;
public class MyTest2 {
public static void main(String[] args) throws IOException {
/**
*编码:将字符-->字节
* 把字符流转为字节流的转换流,并且可以指定编码
*
*/
/* String s="念的都是你,心里都是你,小小的爱,大大城里好甜蜜。";
FileWriter fw = new FileWriter("K:\\io.txt",true);
fw.write(s);
fw.close();
*/
/* //当前平台是UTF-8,文件是GBK
//String:编码方法 getBytes()
//编码:字符转换为字节
String s1="念的都是你,心里都是你,小小的爱,大大城里好甜蜜。";
FileOutputStream fos1 = new FileOutputStream("K:\\io.txt");
fos1.write(s1.getBytes());
fos1.close();
*/
//数据流向:str(字符)-->osw(字符)按照GBK编码为字节流-->fos-->io.txt
String s2="力宏:念的都是你,心里都是你,小小的爱,大大城里好甜蜜。";
FileOutputStream fos = new FileOutputStream("K:\\io.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK");
osw.write(s2);
osw.close();
fos.close();
}
}
2.基本数据字节输入输出流:DataInputStream与DataOutputStream
- 程序运行中,希望能够输出各种类型的数据,读取时能够得到各种类型的数据,因为这些数据不是纯文本,那么只能选择字节流,此处需要用到DataInputStream与DataOutputStream。
public class MyTest3 {
public static void main(String[] args) throws IOException {
/**
* 程序中有这样一组数据:
* int num=10;
* char c='张';
* double d=89.12;
* String s="java";
* boolean b=true;
*
* DataInputStream:在InputStream基础上增加了很多方法
* read***()
* DataOutputStream:在OutputStream基础上增加了很多方法
* write***()
* 要求:用DataOutputStream写文件或者数据,得用DataInputStream来进行读取
* 并且需要注意的是读的顺序与写的顺序需要保持一致
*
* 程序运行中,想要临时退出,下次希望从这个状态继续恢复执行
* 希望能够输出各种类型的数据,读取时能够得到各种类型的数据
* 因为这些数据不是纯文本,那么只能选择字节流
*Java中IO流的类的体系设计,隐含了一个设计模式:装饰者设计模式
*/
int num=10;
char c='张';
double d=89.12;
String s="java";
boolean b= true;
/**
* 因为这些数据不是纯文本,那么只能选择字节流
* 其次因为所含的数据类型较多,使用DataDataInputStream,DataOutputStream处理
*
*/
FileOutputStream fos = new FileOutputStream("data.dat");
DataOutputStream dos = new DataOutputStream(fos);
dos.writeInt(num);
dos.writeChar(c);
dos.writeDouble(d);
dos.writeBytes(s);
dos.writeBoolean(b);
FileInputStream fis = new FileInputStream("data.dat");
DataInputStream dis = new DataInputStream(fis);
int num1 = dis.readInt();
char c1 = dis.readChar();
double d1 = dis.readDouble();
byte s1 = dis.readByte();
boolean b1 = dis.readBoolean();
System.out.println(num1);
System.out.println(c1);
System.out.println(d1);
System.out.println(s1);
System.out.println(b1);
//关闭流
dos.close();
fos.close();
}
}
3.序列化与反序列化:ObjectOutputStream与ObjectIntputStream
序列化可以简单理解为排队的意思,具体的含义为把对象转换为字节序列,序列化的过程。
3.1 序列化与反序列的实现
- ObjectOutputStream:用于输出对象,把对象转换成字节数据输出,对象的输出过程称为序列化
- ObjectOutputStream比Outputstream多了很多方法,其中一个是writeObject(obj)
- ObjectIntputStream:用于输入对象,把字节序列转换为对象读取,对象的读取过程称为反序列化
- ObjectInputStream比Inputstream多了很多方法,其中一个是readObject(obj)
public class MyTest4 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
/***
*序列:排队
* 把对象转换为字节序列,序列化的过程
*
* ObjectOutputStream:用于输出对象,把对象转换成字节数据输出,对象的输出过程称为序列化
* ObjectOutputStream比Outputstream多了很多方法,其中一个是writeObject(obj)
*
* ObjectIntputStream:用于输入对象,把字节序列转换为对象读取,对象的读取过程称为反序列化
* ObjectInputStream比Inputstream多了很多方法,其中一个是readObject(obj)
*/
User u = new User("张三", "123456", 24,'男',"123456");
FileOutputStream fos = new FileOutputStream("user.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
//数据:程序--->oos--->fos--->user.dat
oos.writeObject(u);
/***
* 如果实现Serializable接口的类型,在序列化时会报NotSerializableException,不能序列化
* 如果要解决问题,User类中需要实现java.io.Serializable接口
*
*/
FileInputStream fis = new FileInputStream("user.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject();
//把字节流中的数据,转为一个对象,读取过程中会创建对象,new对象时需要找到对象的类型
User user= (User) o;
System.out.println(user);
//关闭流
ois.close();
fis.close();
oos.close();
fos.close();
}
}
import java.io.Serializable;
public class User implements Serializable {
private String name;
private String passwd;
private int age;
private char gener;
private String phone;
public User() {
}
public User(String name, String passwd, int age, char gener, String phone) {
this.name = name;
this.passwd = passwd;
this.age = age;
this.gener = gener;
this.phone = phone;
}
public static long getSerialVersionUID() {
return serialVersionUID;
}
public char getGener() {
return gener;
}
public void setGener(char gener) {
this.gener = gener;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", passwd='" + passwd + '\'' +
", age=" + age +
", gener=" + gener +
", phone='" + phone + '\'' +
'}';
}
}
3.2 当对象修改时,序列化与反序列化的注意事项
当对象已经输出到文件后,修改了类,再次读取这个文件时,InvalidClassException
报InvalidClassException错的原因:
流中的serialVersionUID与本地的serialVersionUID对不上,报此错
如何解决:
(1):修改本地的serialVersionUID为流中的serialVersionUID
(2):或者,在当初实现Serializable接口时就固定一个serialVersionUID
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class MyTest5 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
/**
* 当对象已经输出到文件后,修改了类,再次读取这个文件时,报InvalidClassException
* 报InvalidClassException错的原因:
* 流中的serialVersionUID与本地的serialVersionUID对不上,报此错
* 如何解决:
* (1):修改本地的serialVersionUID为流中的serialVersionUID
* (2):或者,在当初实现Serializable接口时就固定一个serialVersionUID
*/
FileInputStream fis = new FileInputStream("user.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject();
//把字节流中的数据,转为一个对象,读取过程中会创建对象,new对象时需要找到对象的类型
System.out.println(o);
import java.io.Serializable;
public class User implements Serializable {
//实现Serializable接口时就固定一个serialVersionUID
private static final long serialVersionUID =1L;
private String name;
private String passwd;
private int age;
private char gener;
private String phone;
public User() {
}
public User(String name, String passwd, int age, char gener, String phone) {
this.name = name;
this.passwd = passwd;
this.age = age;
this.gener = gener;
this.phone = phone;
}
public static long getSerialVersionUID() {
return serialVersionUID;
}
public char getGener() {
return gener;
}
public void setGener(char gener) {
this.gener = gener;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", passwd='" + passwd + '\'' +
", age=" + age +
", gener=" + gener +
", phone='" + phone + '\'' +
'}';
}
}
}
}
3.3 当对象的某个属性不需要序列化,可以加关键字transient
import java.io.*;
public class MyTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
/**
* 需求:不是对象中所有的属性都需要序列化的。
* 如果某个属性不需要序列化,可以在属性的前面加一个关键字,transient
*
*
*/
//现在需要序列化这个产品的信息,但是希望销量100不进行序列化(即100不能进行写入)
Goods goods = new Goods("裙子", 120, 100);
FileOutputStream fos = new FileOutputStream("goods.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(goods);
FileInputStream fis = new FileInputStream("goods.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject();
System.out.println(o);
}
}
import java.io.Serializable;
public class Goods implements Serializable {
private String name;
private double price;
private transient int sale;//transient表示sale属性不需要序列化
private static final long serialVersionUID=1;
public Goods() {
}
public Goods(String name, double price, int sale) {
this.name = name;
this.price = price;
this.sale = sale;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getSale() {
return sale;
}
public void setSale(int sale) {
this.sale = sale;
}
public static long getSerialVersionUID() {
return serialVersionUID;
}
@Override
public String toString() {
return "Goods{" +
"name='" + name + '\'' +
", price=" + price +
", sale=" + sale +
'}';
}
}
3.4 对象的引用数据类型属性都要实现Serializable
import java.io.Serializable;
public class Husband implements Serializable {
private static final long serialVersionUID=1L;
private String name;
private Wife wife;
public Husband() {
}
public Husband(String name, Wife wife) {
this.name = name;
this.wife = wife;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Wife getWife() {
return wife;
}
public void setWife(Wife wife) {
this.wife = wife;
}
@Override
public String toString() {
return "Husband{" +
"name='" + name + '\'' +
", wife=" + wife.getName() +
'}';
}
}
import java.io.Serializable;
public class Wife implements Serializable {
/**
* 对象的引用数据类型属性都要实现Serializable
*
*
*/
private String name;
private Husband husband;
public Wife() {
}
public Wife(String name, Husband husband) {
this.name = name;
this.husband = husband;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Husband getHusband() {
return husband;
}
public void setHusband(Husband husband) {
this.husband = husband;
}
@Override
public String toString() {
return "Wife{" +
"name='" + name + '\'' +
", husband=" + husband +
'}';
}
}
4.Serializable接口与Externalizable接口实现序列化与反序列化的区别
java.io.Serializable接口 |
---|
1. Serializable类通过实现 java.io.Serializable 接口以启用其序列化功能 |
2.未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。 序列化接口没有方法或字段,仅用于标识可序列化的语义。 |
3.如果实现Serializable接口,对象如何序列化,各个属性序列化的顺序是什么,都是默认的,程序员无法指定,也不用关心 |
4.如果属性前面有static和transient修饰,不参与序列化 |
java.io.Externalizable |
---|
1.Externalizable 实例类的唯一特性是可以被写入序列化流中,该类负责保存和恢复实例内容。 |
2.若某个要完全控制某一对象及其超类型的流格式和内容,则它要实现 Externalizable 接口writeExternal 和 readExternal 方法。 |
3.程序员在writeExternal方法中,自定制哪些属性需要序列化,顺序是怎样 |
4…程序员在readExternal方法中,自定制哪些属性需要反序列化,顺序和writeExternal保持一致 |
import java.io.*;
public class MyTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Goods g = new Goods("短袖", 99.9, 100);
g.setBrand("nike");
FileOutputStream fos = new FileOutputStream("goods2.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(g);
FileInputStream fis = new FileInputStream("goods2.dat");
ObjectInputStream ois = new ObjectInputStream(fis);
Object o = ois.readObject();
System.out.println(o);
}
}
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class Goods implements Externalizable {
private static String brand="adiads";
private String name;
private double price;
private transient int sale;
public Goods(String name, double price, int sale) {
this.name = name;
this.price = price;
this.sale = sale;
}
public Goods() {
}
public static String getBrand() {
return brand;
}
public static void setBrand(String brand) {
Goods.brand = brand;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getSale() {
return sale;
}
public void setSale(int sale) {
this.sale = sale;
}
@Override
public String toString() {
return "Goods{" +"brand="+brand+
",name='" + name + '\'' +
", price=" + price +
", sale=" + sale +
'}';
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
//程序员定制要序列化的内容,顺序等
//这两个方法是在对象在序列化与反序列化的过程中,jvm帮我们调用
out.writeUTF(brand);//静态也能序列化
out.writeUTF(name);
out.writeDouble(price);
out.writeInt(sale);//有transient也进行序列化
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
//程序员自己定制要反序列化的内容,顺序等,建议与序列化顺序保持一致
brand = in.readUTF();
name = in.readUTF();
price = in.readDouble();
sale = in.readInt();
}
}
5.打印流:PrintStream与PrintWriter
import java.io.FileNotFoundException;
import java.io.PrintStream;
public class MyTest {
public static void main(String[] args) throws FileNotFoundException {
/**
* 打印流:1.PrintStream
* 经典代表:SyStem.out
* System.err
* 2.PrintWriter
* Web阶段学习时,从服务器端往客户端返回消息时用到response
* response.getWriter()可以返回PrintWriter对象。即Web服务器
* 往客户端(例如:浏览器)返回html网页时,用的是PrintWriter对象的输出方法
*
* new PrintStream(文件名)
* new PrintStream(文件名,编码)
* new PrintStream(另一个字节输出流)
*/
PrintStream out =System.out;
out.println("hello");
//所有数据类型写出去都是按照文本进行处理
PrintStream ps = new PrintStream("a.txt");
ps.println("hello");
ps.println("world");
ps.close();
}
}
6.文本扫描流:Scanner
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class MyTest2 {
public static void main(String[] args) throws FileNotFoundException {
/***
*
* System.in是一个InputStream,默认从键盘进行输入
* Scanner是一个简单的文本扫描器
* Scanner:可以从你指定的文件、流中读取文本数据
*
*
*/
/*Scanner input = new Scanner(System.in);
System.out.println("请输入一个整数:");
int num= input.nextInt();
System.out.println("num="+num);
input.close();
*/
Scanner input = new Scanner(new FileInputStream("a.txt"));
while (input.hasNextLine()) {
String s = input.nextLine();
System.out.println(s);
}
}
}
7.JDK1.7中新增的一种try…catch处理的方法
- try…catch代码语法:
try(
需要关闭的资源对象
){
可能发生异常的逻辑代码
}catch(异常类型 e){
异常处理代码
}
凡是在try()中声明的资源对象,
都会自动进行关闭,不论是否发生异常
案例:从d:/1.txt(GBK)文件中,读取内容,写到项目根目录下1.txt中,利用try…catch进行捕获异常
mport java.io.*;
public class MyTest {
public static void main(String[] args) {
//从d:/1.txt(GBK)文件中,读取内容,写到根目录下1.txt中
FileInputStream fis = null;
InputStreamReader isr = null;
FileOutputStream fos = null;
OutputStreamWriter osw = null;
try {
fis = new FileInputStream("d:\\1.txt");
isr = new InputStreamReader(fis, "GBK");
fos = new FileOutputStream("1.txt");
osw = new OutputStreamWriter(fos,"UTF-8");
int len;
char[] chars = new char[1024];
while((len=isr.read(chars))!=-1) {
osw.write(chars,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
osw.close();
fos.close();
isr.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
总结
本节主要对经常使用的处理流进行介绍:例如解码与编码,基本数据输入输出流,序列化与反序列化以及打印流,文本扫描流。掌握这些经常使用处理流对于后面的网络编程等知识能有更好的理解。