4.5 其他流
void close()到目前为止,使用的字节流、字符流都是无缓冲的输入、输出流,这就意味着,每次的读、写操作都会交给操作系统来处理。这样的做法可能会对系统的性能造成很大的影响,因为每次操作都可能引发磁盘硬件的读、写或网络的访问,这些磁盘硬件读、写和网络访问会占用大量系统资源,影响效率。
4.5.1 缓冲流
void close()前面介绍的字节流和字符流,因为没有使用缓冲区等其他原因,一般不直接使用。在实际编程过程中,这些对象的引用还要传入到装饰类中去,动态地给这些对象增加额外的功能,形成新的对象,这些新的对象才是实际需要的字节流和字符流对象,这个过程同时也说明了装饰器模式是使用的。装饰类的使用如下所示:
FileInputStream fis = new FileInputStream("Car.java");
装饰器类in = new 装饰器类(fis);
void close()缓冲流是一种装饰器类,目的是让原字节流、字符流新增缓冲的功能。以字符缓冲流为例进行说明,字符缓冲流从字符流中读取、写入字符,不立刻要求系统进行处理,而是缓冲部分字符,从而实现按规定字符数、按行等方式的高效的读取或写入。缓冲流缓冲区的大小可以指定(通过缓冲流构造方法指定),也可以使用默认的大小,多数情况下默认大小已够使用。
void close()通过一个输入字符流和输出字符流创建输入字符缓冲流和输出字符缓冲流的代码如下。
BufferedReader in = new BufferedReader(new FileReader("Car.java"));
BufferedWriter out = new BufferedWriter(new FileWriter("Truck.java "));
void close()输入字符缓冲流类和输出字符缓冲流类的方法和输入字符流类和输出字符流类的方法类似,下面通过一个例子来演示缓冲流的使用。
import java.io.*;
public class TestBufferStream{
public static void main(String[] args) throws IOException {
BufferedReader in = null;
BufferedWriter out = null;
try{
in = new BufferedReader(new FileReader("C:\\com\\bd\\zuche\\Vehicle.java"));
out = new BufferedWriter(new FileWriter("C:\\com\\bd\\zuche\\Vehicle2.java"));
//逐行读取、存入字符串,实现文件复制
String s;
while ((s = in.readLine()) != null) {
out.write(s);
//写入一个分行符,否则内容在一行显示
out.newLine();
}
}catch(IOException e){
System.out.println(e.getMessage());
}finally{
if(in != null){
in.close();
}
if(out != null){
out.close();
}
}
}
}
void close()上面的代码在读取数据时,使用的是BufferedReader缓冲流的readLine()方法,获取该行字符串并存储到String对象s里。在输出的时候,使用的是BufferedWriter缓冲流的write(s)方法,把获取的字符串输出到Vehicle2.java文件。有一个地方需要注意,在每次调用write(s)方法之后,要调用输出缓冲流的newLine()方法写入一个分行符,否则所有内容将在同一行显示。
void close()有些情况下,不是非要等到缓冲区满,才向文件系统写入。例如在处理一些关键数据时,需要立刻将这些关键数据写入文件系统,这时则可以调用flush()方法,手动刷新缓冲流。另外,在关闭流时,也会自动刷新缓冲流中的数据。
void close()flush()方法的作用就是刷新该流的缓冲。如果该流已保存缓冲区中各种write()方法的所有字符,则立即将它们写入预期目标。如果该目标是另一个字符或字节流,也将其刷新。因此,一次flush()调用将刷新Writer和OutputStream链中的所有缓冲区。
4.5.2 字节流转换为字符流
void close()假设有这样的需求:使用一个输入字符缓冲流读取用户在命令行输入的一行数据。
void close()分析这个需求,首先得知需要用输入字符缓冲流读取数据,我们想到了使用刚才学习的BufferedReader这个类。其次,需要获取的是用户在命令行输入的一行数据,通过之前的学习可以知道,System.in是InputStream类(字节输入流)的静态对象,可以从命令行读取数据字节。现在问题出现了,需要把一个字节流转换成一个字符流。在此可以使用InputStreamReader和OutputStreamWriter这两个类来进行转换。
void close()完成上面需求的代码如下(通过该段代码,可以了解如何将字节流转换成字符流):
import java.io.*;
public class TestByteToChar{
public static void main(String[] args) throws IOException {
BufferedReader in = null;
try{
//将字节流System.in通过InputStreamReader转换成字符流
in = new BufferedReader(new InputStreamReader(System.in));
System.out.print("请输入你今天最想说的话:");
String s = in.readLine();
System.out.println("你最想表达的是:" + s);
}catch(IOException e){
System.out.println(e.getMessage());
}finally{
if(in != null){
in.close();
}
}
}
}
void close()刚才提到的将字节流转换为字符流,实际上使用了一种设计模式——适配器模式。适配器模式的意图是将一个类的接口转换成客户希望的另外一个接口,该模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
4.5.3 数据流
void close()数据流,简单来说就是容许字节流直接操作基本数据类型和字符串。
void close()假设程序员使用整型数组types存储车型信息(1代表轿车、2代表卡车),用数组names、oils、losss和others分别存储车名、油量、车损度和品牌(或吨位)的信息。现要求使用数据流将数组信息存到数据文件data中,并从数据文件中读取数据,用来输出车辆信息。
import java.io.*;
public class TestData{
static final String dataFile = "C:\\com\\bd\\zuche\\data";//数据存储文件
//标识车类型:1代表轿车、2代表卡车
static final int[] types = {1,1,2,2};
static final String[] names = { "战神","跑得快","大力士","大力士二代"};
static final int[] oils = {20,40,20,30};
static final int[] losss = {0,20,0,30};
static final String[] others = { "长城","红旗","5吨","10吨"};
static DataOutputStream out = null;
static DataInputStream in = null;
public static void main(String[] args) throws IOException {
try {
//输出数据流,向dataFile输出数据
out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile)));
for (int i = 0; i < types.length; i++) {
out.writeInt(types[i]);
//使用UTF-8编码将一个字符串写入基础输出流
out.writeUTF(names[i]);
out.writeInt(oils[i]);
out.writeInt(losss[i]);
out.writeUTF(others[i]);
}
}finally {
out.close();
}
try{
int type,oil,loss;
String name,other;
//输出数据流,从dataFile读出数据
in = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile)));
while(true)
{
type = in.readInt();
name = in.readUTF();
oil = in.readInt();
loss = in.readInt();
other = in.readUTF();
if(type == 1){
System.out.println("显示车辆信息:\n车型:轿车 车辆名称为:" + name +
" 品牌是:" + other + " 油量是:" + oil + " 车损度为:" + loss);
}else{
System.out.println("显示车辆信息:\n车型:卡车 车辆名称为:" + name +
" 吨位是:" + other + " 油量是:" + oil + " 车损度为:" + loss);
}
}
}catch(EOFException e){
//EOFException作为读取结束的标志
}finally {
in.close();
}
}
}
void close()编译、运行程序,其运行结果如图4.7所示。
图4.7 使用数据流存取车辆信息