Java编程那些事儿90——装饰流利用1
副标题#e#
11.3.3 装饰流利用
除了凭据流的偏向可以把流分别为输入流和输出流两类,凭据流读写数据的根基单元把流分别为字节约和字符流两类以外,还可以凭据流是否直接毗连实际数据源,譬喻文件、网络、字节数组等,将流又可以分别为实体流和装饰流两大类。
个中实体流指直接毗连数据源的流类,如前面先容的FileInputStream/FileOutputStream和FileReader和FileWriter,该类流直接实现将数据源转换为流工具,在实体流类中实现了流和数据源之间的转换,实体流类均可单独举办利用。
而装饰流指不直接毗连数据源,而是以其它流工具(实体流工具或装饰流工具)为基本成立的流类,该类流实现了将实体流中的数据举办转换,加强流工具的读写本领,较量常用的有DataInputStream/DataOutputStream和BufferedReader/BufferedWriter等,装饰流类不行以单独利用,必需共同实体流或装饰流举办利用。
由于装饰流都是在已有的流工具基本长举办建设的,所以这种建设流的方法被称作“流的嵌套”,通过流的嵌套,可以修饰流的成果,譬喻使读写的速度增加可能提供更多的读写方法,利便数据名目标处理惩罚。
装饰流不改变本来实体流工具中的数据内容,只是从实体流工具基本上建设出的装饰流工具相对付实体流工具举办了一些成果的加强。
流的嵌套是进修IO编程时必需把握的常识,利用它才可以让你真正体会到IO类设计时的设计思路,也可以利便的利用IO类。
下面别离以DataInputStream/DataOutputStream和BufferedReader/BufferedWriter为例子,具体先容装饰类的利用。
11.3.3.1 DataInputStream/DataOutputStream
在前面的示例中,在向流中写入的数据必需首先转换为byte数组或char数组,当写入的数据较量少、较量简朴时,则向流中写入数据时照旧不是很贫苦的,可是假如向流中写入数据较量多时,手动转换数据名目则会较量贫苦。虽然,许多文件都是按照文件存储的需要设计了专门的存储名目,可是这些名目一般都较量巨大,需要阅读专门的名目文档才可以读写这些特命名目标文件。
为了简化措施员对付流的操纵,使得措施员可以从繁杂的数据名目中摆脱出来,在IO类中专门设计了两个类——DataInputStream/DataOutputStream类简化流数据的读写,利用这两个类,可以实现以加强型的读写要领读写数据,使得读写流的数据变得较量简朴。
在实际利用这两个类时,必需匹配起来举办利用。也就是说,只有利用DataOutputStream流名目写入的数据,在实际读取时才可以利用DataInputStream举办读取。因为在利用DataOutputStream向流中写入数据时,除了写入实际的数据内容以外,还写入了特定的数据名目,该名目对付措施员来说是透明的,这种特定的名目不需要措施员熟悉,而只需要利用DataInputStream读取即可,读取时的顺序和写入时的顺序和范例保持一致即可。
在DataInputStream类中,增加了一系列readXXX的要领,譬喻readInt、readUTF、readBoolean等等,而在DataOutputStream类中,也增加了一系列writeXXX的要领,譬喻writeInt、writeUTF、writeBoolean等等,使得对付数据的读写越发利便很容易。
下面以读写文件为例子,演示DataInputStream/DataOutputStream类的根基利用。
/**
* 模仿需要存储到文件中的数据
* 该类中生存4种范例的数据
*/
public class MyData {
boolean b;
int n;
String s;
short sh[];
public MyData(){}
public MyData(boolean b,int n,String s,short sh[]){
this.b = b;
this.n = n;
this.s = s;
this.sh = sh;
}
}
#p#副标题#e#
在该示例中,需要将MyData范例的工具内部生存的数据凭据必然的名目存储到文件中,这里罗列了2种根基数据范例boolean和int,以及两种引用数据范例String和数组,在下面的示例代码中将会以必然的名目写入到文件中。
import java.io.*;
/**
* 利用DataOutputStream书写具有必然名目标文件
*/
public class WriteFileUseDataStream {
public static void main(String[] args) {
short sh[] = {1,3,134,12};
MyData data =new MyData(true,100,"Java语言",sh);
//写入文件
writeFile(data);
}
/**
* 将MyData工具凭据必然名目写入文件中
* @param data 数据工具
*/
public static void writeFile(MyData data){
FileOutputStream fos = null;
DataOutputStream dos = null;
try{
//成立文件流
fos = new FileOutputStream("test.my");
//成立数据输出流,流的嵌套
dos = new DataOutputStream(fos);
//依次写入数据
dos.writeBoolean(data.b);
dos.writeInt(data.n);
dos.writeUTF(data.s);
//写入数组
int len = data.sh.length;
dos.writeInt(len); //数组长度
//依次写入每个数组元素
for(int i = 0;i < len;i++){
dos.writeShort(data.sh[i]);
}
}catch(Exception e){
e.printStackTrace();
}finally{
try {
dos.close();
fos.close();
} catch (Exception e2){
e2.printStackTrace();
}
}
}
}
#p#分页标题#e#
在该示例代码中,首先成立一个实体流fos,该实体流毗连到数据源——文件,然后以该实体流工具为基本,利用流的嵌套,成立装饰流工具dos,由于需要写入流中的工具data中包括的数据较量多,所以需要以必然的名目写入流,这里利用DataOutputStream制止自界说数据名目,而写入流中的顺序就是该流的名目,也就是文件test.my的名目,这种名目对付措施员来说是透明的。
利用工具dos中对应的writeXXX要领依次将需要存储的数据写入流中,在写入字符串时,为了使字符编码保持一致,一般利用writeUTF写入字符串,也就是先将字符串转换为utf-8名目标byte数组,然后再将该数组以必然的名目写入到流中。而在写入数组时,则首先写入数组的长度,然后再将数组的内容依次写入到流中,利用这种方法就可以很利便的将数组写入到流中。
这样文件test.my文件就具有了本身特定的文件名目,措施员需要影象的就是该文件在写入时的写入顺序,可以很利便的利用DataInputStream读取出来。
下面的代码是利用DataInputStream读取test.my文件的代码,留意文件名目标处理惩罚。
import java.io.*;
/**
* 利用DataInputStream读取自界说名目标文件
*/
public class ReadFileUseDataStream {
public static void main(String[] args) {
MyData data = readFile();
System.out.println(data.b);
System.out.println(data.n);
System.out.println(data.s);
int len = data.sh.length;
for(int i = 0;i < len;i++){
System.out.println(data.sh[i]);
}
}
/**
* 从文件test.my中读取数据,并利用读取到的数据初始化data工具
* @return 读取到的工具内容
*/
public static MyData readFile(){
MyData data = new MyData();
FileInputStream fis = null;
DataInputStream dis = null;
try {
//成立文件流
fis = new FileInputStream("test.my");
//成立数据输入流,流的嵌套
dis = new DataInputStream(fis);
//依次读取数据,并赋值给data工具
data.b = dis.readBoolean();
data.n = dis.readInt();
data.s = dis.readUTF();
int len = dis.readInt();
data.sh = new short[len];
for(int i = 0;i < len;i++){
data.sh[i] = dis.readShort();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
dis.close();
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return data;
}
}
在该示例代码中,首先成立实体流fis,然后以该流工具为基本成立dos装饰流,然后凭据写入文件的顺序,依次将流中的数据读取出来,并将读取到的数值赋值给data工具中对应的属性,从而实现将数据从文件中规复到实际的工具。
#p#分页标题#e#
最后再次强调,DataInputStream和DataOutputStream必需匹配起来举办利用,也就是利用DataInputStream读取的流数据必需是利用DataOutputStream流写入的数据,这样才气保持名目上的统一。
虽然,利用DataInputStream和DataOutputStream和其它的实体流也可以匹配起来举办利用,譬喻和ByteArrayInputStream和ByteArrayOutputStream匹配利用将可以实现利便的把数据转换为特命名目标byte数组以及将byte数组规复返来,利用的名目和上面的示例雷同,这里就不再反复了。