深入解析I/O编程之包装类
从上节学到的节点流类我们知道,如Java中提供的FileOutputStream和FileInputStream类实现往文件中写入字节或从文件中读取字节数据。在实际应用中,我们需要往文件中写入或读取各种类型的数据,一般的做法是先将其他类型的数据转换成字节数组后写入文件或是将从文件中读取到的字节数组转换成其他类型。然而,上述方法会给我们的程序增加了代码量和带来一些困难和麻烦。为了解决这个问题,Java提供了一些中间类,这些中间类提供了读写各种类型的数据的各种方法,当我们需要写入其他类型的数据时,可以调用这些中间类(包装类)的对应方法即可实现。
包装类
包装类提供了读写各种类型数据的各种方法,其作用是将其他数据类型转换成字节数组,然后调用底层的节点流类将这个字节数组写入到目标设备。
以下为包装类的调用过程:
一、DataInputStream与DataOutputStream
DataInputStream与DataOutputStream提供了可以读写各种基本数据类型数据的各种方法。
1.DataInputStream类
(1)功能:包装输入流,提供将读取的字节数组数据转换为各种Java基本数据类型数据的方法;
(2)构造方法
>DataInputStream(InputStream in):传递一个指定输入流对象创建一个包装输入流对象;
注意:在创建包装类对象时,需指定它要调用的哪个底层流对象。
(3)常用方法
>int read(byte[] b) :从输入流中读取一些字节数据并存储到字节数组b中,返回值为读到字节的个数
>int read(byte[] b, int off, int len):从输入流中读取一些字节数据存放到字节数组从off位置后的len数据元素;
>boolean/char/double/float/int/byte/long/short readXxx():从输入流读取一个Xxxx类型数据并返回该数据;
>String readUTF() :返回字符串
>int skipBytes(int n) :跳过n个字节
2.DataOutputStream类
(1)功能:包装输出流,提供各种Java基本数据类型数据的方法将字节数组数据转换写入目标设备;
(2)构造方法
>DataOutputStream(OutputStream out)
(3)常用方法
>void flush() :彻底写入到输出流
>int size() :返回目前写入输出流中字节数据的个数
>void write(byte[] b, int off, int len) :将字符数组b中从off位置开始的len个字节数据写入到输出流;
>void write(int b) :将指定字节b的低八位写入到输入到输出流中;
>void writeBoolean(boolean v) :将boolean类型数据v作为一个字节写入到底层输出流
在Java中字符的是Unicode编码,为双字节。
>void writeByte(int v) :将一个字节的内容写入到目标设备中;
>void writeBytes(String s) :将字符串中的每一个字符的低字节的内容写入到目标设备中
>void writeChar(int v) :将一个字符的两个字节的内容都写入到目标设备中;
>void writeChars(String s) :将字符串中的每一字符两个字节的内容都写入到目标设备中;
>void writeDouble/Float/Int/Long/(xxxx
v) :如int,为将一个整型数据的四个字节的内容写入到目标设备中;
>void writeUTF(String str):将字符串按照UTF格式写入目标设备中;
注释:UTF是带有长度头的,最开始的两个字节是对字符串进行UTF编码的字节长度,然后才是每个字符的UTF编码。
二、BufferedInputStream与BufferedOutputStream
对I/O进行缓冲是一种常见的性能优化。缓冲流为I/O流增加了内存缓冲区,增加缓冲区主要有两个目的:
(1)运行Java的I/O便于一次可以操作多个字节的数据,以提高整个系统的性能;
(2)由于有缓冲区,使得在流上执行skip、mark和reset方法都成为可能。
所谓字符回流,即通过mark方法子啊流的当前位置作一个标记,该方法接收的一个整数参数用来指定从标记处开始,当read方法读取的字节数时,reset方法可以让以后的read方法重新回到mark方法所作的标记处开始读取数据。
1.BufferedInputStream类
(1)功能:Java的BufferedInputStream类可以对任何的InputStream进行带缓冲区的封装以达到性能改善。
(2)构造方法
>BufferedInputStream(InputStream in) :创建一个带有32字节缓冲区的缓冲流
>BufferedInputStream(InputStream in, int size):按指定的大小来创建缓冲区的缓冲流。
注释:通常缓冲区的大小是内存、磁盘扇区或其他系统容量的整数倍,以便充分提高I/O的性能。
(3)常用方法
>int read(byte[] b):从输入流读取b.length个字节存放到字节数组b中并返回实际读入的字节数;
>int read(byte[] b,int off,int len):从输入流的数据读入到字节数组b中从脚标为off开始的len个数组元素中;
>int skip(long n):跳过输入流上的n个字节并返回实际跳过的字节数;
>int available():返回当前输入流中可读的字节数
>void mark(int readlimit):在流的当前位置作一个标记
>void reset():把输入指针返回到以前所做的标识处;
>boolean markSupported():如果当前流支持mark/reset操作就返回true;
>void close():在操作完一个流后使用该方法关闭该流,释放流对象和与该流相关的系统资源;
2.BufferedOutputStream类
(1)功能:
(2)构造方法
>BufferedOutputStream(OutputStream out) :创建一个32字节的缓冲区
>BufferedOutputStream(OutputStream out, int size):以指定的大小来创建缓冲区
(3)常用方法
>void write(int b):将一个字节数组写到输出流中;
>void write(byte[] b,int off,int len):将字节数组b中的从off开始的len个字节写到输出流;
>void flush():用于将缓冲区的数据强制输出完
三、PrintStream
PrintStream类提供了一系列的print和println方法,可以实现将基本数据类型的格式化成字符串输出。如System.out就是printStream类的一个实例对象,Java的PrrintStream对象具有多个重载的print和println方法,它们可输出各种类型的数据。对于基本数据类型的数据,print和pintln方法会先将它们转换成字符串的形式后再输出,而不是输出原始的字节内容。注释:格式化输出是指将一个数据用其字符串格式输出。
(1)功能:可以实现将基本数据类型的格式化成字符串输出
(2)构造方法
>PrintStream(OutputStream out):创建一个print输出流并规定遇到换行符(\n)时是否自动清空缓冲区
>PrintStream(OutputStream out,boolean autoflush):创建一个print输出流
>PrintStream(OutputStream out,boolean auto flush,String encoding):创建一个print输出流并规定遇到换行符(\n)时是否自动清空缓冲区,并指定编码方式
>PrintStream(File file):创建一个print输出流并指定对应的文件
注释:autoflush参数用于在Java中遇到换行符(\n)时是否自动清空缓冲区,endcoding是指定编码方式。
(3)常用方法
>PrintStream append(char c):追加一个指定字符到该输出流;
>void close() :关闭输出流;
>void flush():将缓存区的数据写入到目标设备中;
>void print/println(xxx value):打印指定数据
>void write(int b):将一个字节数组写到输出流中;
>void write(byte[] b,int off,int len):将字节数组b中的从off开始的len个字节写到输出流;
四、综合实例
实现:使用多个流对象对文件进行读写操作。
<span style="font-size:18px;">import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class PactageStreamTest {
public static void main(String[] args)
{
//写数据到Hello.txt文件对应的输出流
try
{
FileOutputStream fos=new FileOutputStream("C:\\Hello.txt"); //实例化一个文件输出流对象
BufferedOutputStream bos=new BufferedOutputStream(fos); //实例化一个缓冲输出流
DataOutputStream dos=new DataOutputStream(bos); //实例化一个包装输出流
dos.writeUTF("hi广州"); //将不同类型的数据转换为字节数据写入到缓冲输出流中
dos.writeBytes("hi广州");
dos.writeChars("hi广州");
dos.close(); //将字节数据从缓冲输出流写入到文件输入流中,释放资源
}
catch (Exception e)
{
e.printStackTrace();
}
//从Hello.txt对应的文件输入流读取数据
try
{
FileInputStream fis=new FileInputStream("C:\\Hello.txt");
BufferedInputStream bis=new BufferedInputStream(fis);
DataInputStream dis=new DataInputStream(fis);
//System.out.println(dis.readUTF());
byte[] buf=new byte[1024];
int len=dis.read(buf); //从输入流读取buf.length长度到buf字节数组,并返回实际读到的字节数
System.out.println(new String(buf,0,len)); //打印buf字节数组的len个字节
}
catch (Exception e)
{
e.printStackTrace();
}
}
}</span>
效果演示:
源码分析:
(1)该程序使用了多个流对象进行文件的读写,这些多个流对象形成了一个链即为流栈。当我们使用流栈时,关闭最上面的一个流也就自动关闭了栈中的所有底层流,所以程序中调用了DataInputStream与DataOutputStream两个流对象的close方法。
FileOutputStream fos=new FileOutputStream("C:\\Hello.txt"); //实例化一个文件输出流对象
BufferedOutputStream bos=new BufferedOutputStream(fos); //实例化一个缓冲输出流
DataOutputStream dos=new DataOutputStream(bos); //实例化一个包装输出流
(2)程序出现乱码,这是由于编码不同造成的。为了解决字符问题,Java.io包中专门提供了各种Reader和Writer类来操作字符串。