深入解析I/O编程之节点流
转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空)
在上一节我们说到IO流主要分为两个大类:节点流类和过滤流类(也称包装类)。程序用于直接操作目标设备所对应的类叫节点流类;为更加灵活方便地读写各种类型的数据,程序也可用通过一个间接流类去调用节点流类而这个简介流类即为过滤流类(也称为包装类)。
总之,无论是节点流类还是过滤流类,它们都属于字节流或字符流其中的一种。下面我们介绍一下常见的节点流类有哪些。
字****节****流
一、InputStream与OutoutStream
InputStream类为所有字节输入流的父类(是抽象类),通过该类对象实现从目标设备中连续读取字节数据。OutputStream类为所有字节输出流的父类(抽象类),通过该类对象实现向目标设备中连续写入字节数据。由于InputStream和OutputStream对象是两个抽象类,不能表明具体对应哪种IO设备。它们下面有许多子类,包括网络、管道、内存、文件等具体的IO设备,如FileInputStream类对应的是文件输入流,是一个节点流类。我们将这些节点流类所对应的IO源和目标称为流节点(Node)。
1.InputStream类
(1)功能:InputStream定义了Java的输入流模型,通过该类对象(输入流)实现从目标设备中连续读取字节数据。InputStream是一个抽象类,程序中实际使用的是它的各种子类对象。
(2)构造方法
>InputStream()
(3)常用方法
>abstract int read():返回下一个输入字节的整型表示,如果返回-1表示遇到流的末尾;
>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):在输入流的当前位置处放上一个标识,允许最多再读取readlimit个字节
>void reset():把输入指针返回到以前所做的标识处;
>boolean markSupported():如果当前流支持mark/reset操作就返回true;
>void close():在操作完一个流后使用该方法关闭该流,释放流对象和与该流相关的系统资源;
2.OutputStream类
(1)功能:通过该类对象(输入流)实现向目标设备中连续写入字节数据。
(2)构造方法
>OutputStream()
(3)常用方法
>void write(int b):将一个自留写到输出流;
>void write(byte[]
b):将整个字节数组写到输出流中;
>void write(byte[]
b,int off,int len):将字节数组b中的从off开始的len个字节写到输出流;
>void flush():将输出流中的数据写入到目标设备中并清空缓冲区;
>void close():关闭输出流
提升:输入输出流类是相对程序而言的,而不是代表文件的。如果我们想要将A文件中内容写入B文件中,我们可以这样做:创建一个输入流来完成对A文件的读取数据操作,创建一个输出类完成对B文件的写数据操作。
二、FileInputStream与FileOutputStream
这两个流节点用来操作磁盘文件(文本、图片、音频等),在创建一个FIleInputStream对象时通过构造函数指定文件的路径和名字(该文件应是存在且可读的)。在创建一个FileOutputStream对象时指定文件,如果存在则覆盖该文件。即通过FileOutputStream类往其连接(对应)的一个文件中写入数据,通过FileInputStream类从其连接(对应)的文件将内容读取出来。
1.FileInputStream类
(1)功能:为一个磁盘文件创建一个输入流,通过FileInputStream流对象实现从文件读取连续字节数据。
(2)构造方法
>FileInputStream(String name):创建一个磁盘文件输入流并将其连接到指定的文件;
>FileInputStream(File file):创建一个磁盘文件输入流并将其连接到File对象指定的文件;
举例:
//第一种构造方法
FileInputStream stream=new FileInputStream("hello.test");
//第二种构造方法(允许在把文件连接到输入流之前进一步操作文件属性)
File file=new File();
FileInputStream stream = new FileInputStream(file);
(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 close():在操作完一个流后使用该方法关闭该流,释放流对象和与该流相关的系统资源;
2.FileOutputStream类
(1)功能:为一个磁盘文件创建一个输出流,通过FileInputStream流对象实现从文件写入连续字节数据。
(2)构造方法
>FileOutputStream(String name):创建一个磁盘文件输出流并将其连接到指定的文件;
>FileInputStream(File file):创建一个磁盘文件输出流并将其连接到File对象指定的文件;
举例:
//第一种构造方法
FileOutputStream stream=new FileOutputStream("hello.test");
//第二种构造方法(允许在把文件连接到输出流之前进一步操作文件属性)
File file=new File();
FileOutputStream stream = new FileOutputStream(file);
(3)常用方法
>void write(int b):将一个自留写到输出流;
>void write(byte[] b):将整个字节数组写到输出流中;
>void write(byte[] b,int off,int len):将字节数组b中的从off开始的len个字节写到输出流;
>void close():关闭输出流
提升:在创建一个FileOutputStream对象时,可以为其指定还不存在的文件名,但不能是存在的目录名或是已经被其他程序打开了的文件。FileOutputStream先创建输出对象,然后再准备输出。
三、PipedInputStream与PipedOutputStream
一个PipedInputStream对象必须和一个PipedOutputStream对象进行连接而产生一个通信管道,其中PipedOutputStream用于向管道写入数据,PipedInputStream用于从管道中读取PipedOutputStream写入的数据。在流中引入管道的概念,主要用来完成线程之间的通信,即一个线程的PipedInputStream对象能够从另外一个线程的PipedOutputStream对象中读取数据。
1.PipedInputStream类
(1)功能:PipedInputStream用于从管道中读取PipedOutputStream写入的数据;
(2)构造方法
>PipedInputStream() :创建一个管道的输入流对象,但没有与PipedOutputStream进行连接;
>PipedInputStream(int pipeSize):创建一个管道的输入流对象,并指定管道大小作为管道缓存;
>PipedInputStream(PipedOutputStream src) : 创建一个管道的输入流对象,但与指定的PipedOutputStream进行连接;
(3)常用方法
>int available() :判定管道输入流还有多有可读字节数据;
>void close() :关闭管道输入流并释放系统资源
>void connect(PipedOutputStream src) :将该输入流与指定的PipedOutputStream进行连接
>int read() :从该管道输入流读取下一个字节数据;
>int read(byte[] b, int off, int len):将管道输入流中的数据读入到字节数组b中从脚标为off开始的len个数组元素中;
>protected void receive(int b):接收一个字节数据
2.PipedOutputStream类
(1)功能:PipedOutputStream用于向管道写入数据;
(2)构造方法
>PipedOutputStream():创建一个管道输出流,但没有与 PipedInputStream进行连接;
>PipedOutputStream(PipedInputStream snk) :创建一个管道输出流,并与指定的PipedInputStream连接;
(3)常用方法
>void close() :关闭管道输出流并释放相应的系统资源;
>void connect(PipedInputStream snk) :将该管道输出流与指定的PipedInputStream相连接;
>void flush() :彻底写入数据到输出流
>void write(byte[] b, int off, int len) :将指定字节数据的off位置开始的len个字节数据写入到管道输出流中
>void write(int b) :写入一个指定的字节数据到管道输出流
提升:使用管道流类,可以实现各个程序模块之间的松耦通信,我们可以灵活地将多个这样的模块的输出流和输入流相连接,以拼装成满足各种应用的程序,而不用对模块内部进行修改。
3.源码实战
实现:多线程实现管道间通信。
(1)Sender.java
功能:实现一个Thread子类,重写run方法用于向管道输出流写入字节数据
<span style="font-size:18px;">package PipedStream; import java.io.IOException; import java.io.PipedOutputStream; /*PipedOutputStream输入流 * 功能:向管道输出流写入字节数据*/ class Sender extends Thread{ private PipedOutputStream out = new PipedOutputStream(); //声明一个PipedOutputStream实例 //成员方法:返回一个PipedOutputStream对象 public PipedOutputStream getOutputStream() { return out; } //成员方法 public void run() { String s=new String("Hello,receiver.How are you!"); try { out.write(s.getBytes()); out.close(); } catch (IOException e) { e.printStackTrace(); } } }</span>
(2)Receiver.java
功能:实现一个Thread子类,重写run方法用于从管道输入流读取字节数据到字节数组中
<span style="font-size:18px;">package PipedStream; import java.io.IOException; import java.io.PipedInputStream; /* PipedInputStream输出流 * 功能:向管道读取字节数据到字节数组中*/ class Receiver extends Thread{ //成员变量 PipedInputStream in = new PipedInputStream(); //成员方法:返回一个PipedInputStream对象 public PipedInputStream getInputStream() { return in; } //成员方法 public void run() { String s=null; byte[] buf = new byte[1024]; try { int len = in.read(buf); //从管道读取buf.length个字节到byte字节数组中并返回实际读到字节数量 s=new String(buf,0,len);//将字节数组中的数据转换为字符串 System.out.println("The following message comes from sender:\n"+s); } catch (IOException e) { e.printStackTrace(); } } }</span>
(3)PipedStreamTest.java
功能:创建两个线程并使用PipedOutputStream和PipedInputStream类创建一个管道。
<span style="font-size:18px;">package PipedStream; import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; public class PipedStreamTest { public static void main(String[] args) { try { Thread t1=new Sender(); //实例化一个线程1 Thread t2 = new Receiver(); //实例化一个线程2 PipedOutputStream out = ((Sender) t1). getOutputStream(); PipedInputStream in = ((Receiver) t2).getInputStream(); out.connect(in); //连接管道 t1.start(); t2.start(); } catch (IOException e) { e.printStackTrace(); } } }</span>
效果演示:
四、ByteArrayInputStream与ByteArrayOutputStream
一般来说,要在程序分配一个存储数据的内存块,通常都用定义一个字节数组实现。在Java中,提供了ByteArrayInputStream与ByteArrayOutputStream两个流类来实现类似内容虚拟文件的功能。ByteArrayInputStream与ByteArrayOutputStream流作用在于用IO流的方式来完成数组内容的读写,我们以保存数据的数组作为数据源来创建一个ByteArrayInputStream流对象,以数组作为缓冲区来创建一个ByteArrayOutputStream对象,最后从ByteArrayOutputStream对象中的数据存储到数组中。
注:这里使用流而非数组实现内存块,相比数组,我们可以借助IO流的各种方法来操作数组中的内容。
1.ByteArrayInputStream
(1)功能:输入流的一种实现。
(2)构造方法:
>ByteArrayInputStream(byte[] buf):以byte字节数组作为数据源创建一个输入流对象;
>ByteArrayInputStream(byte[] buf,int offset,int
length):使用数组buf中的从offset开始的length个元素作为数据源创建一个输入流对象;
(3)常用方法
>abstract int read():返回下一个输入字节的整型表示,如果返回-1表示遇到流的末尾;
>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):在输入流的当前位置处放上一个标识,允许最多再读取readlimit个字节
>void reset():把输入指针返回到以前所做的标识处;
>void close():在操作完一个流后使用该方法关闭该流,释放流对象和与该流相关的系统资源;
2.ByteArrayOutputStream
(1)功能:输出流的一种实现。
(2)构造方法
>ByteArrayOutputStream():创建一个32字节的缓冲区;
>ByteArrayOutputStream(int size):根据参数指定的大小创建缓冲区,缓冲区的大小在数据过多时能够自动增长;
(3)常用方法
>void write(int b):将一个字节数据写到输出流;
>void write(byte[] b,int off,int len):将字节数组b中的从off开始的len个字节写到输出流;
>void close():关闭输出流
>int size():返回buffer缓冲区当前大小;
>byte[] toByteArray():将数据转换为一个字节数组数据并返回该字节数组
>String toString():将缓冲区中的字节数组内容转换成为一个字符串
注:内存虚拟(映像)文件就是把一块内存虚拟成一个硬盘上的文件,原来该写到硬盘文件上的内容会被写到这个内存中,原来该从一个硬盘文件上读取内容改为直接从内存中读取。如果程序在运行过程中要产生一些临时文件,就可以用虚拟文件的方式实现,我们无需访问硬盘,而是直接访问内存,因此能够提供应用程序的效率。
3.源码实战
实现:模拟内存块功能-将小写字母作为输入流的数据源,然后,将该输入流中的所有英文字母变成对应的大写字母写入到输出流中,最后存储到字节数组中并输出。
ByteArraytest.java
<span style="font-size:18px;">import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; public class ByteArraytest { public static void main(String[] args) { String tmp = "abcdefghijklmnopqrstuvwxyt"; byte[] src=tmp.getBytes(); //src为转换前的数据块 ByteArrayInputStream input = new ByteArrayInputStream(src); //实例化一个ByteArrayInputStream输入流并以字节数据src为数据源 ByteArrayOutputStream out = new ByteArrayOutputStream(); //实例化一个ByteArrayOutputStream输出流 //调用转换字母功能函数转换字母 new ByteArraytest().transForm(input, out); byte[] result = out.toByteArray(); //resulte为转换后的内存块,用于存储输出流中的数据 System.out.println(new String(result));//将字节数组中的字节数据转换为字符串数据打印 } //转换成员方法 public void transForm(ByteArrayInputStream input,ByteArrayOutputStream out) { int c; while((c=input.read())!=-1) //循环从输入流读取一个字节,读到末尾返回-1 { int b=(int)Character.toUpperCase((char)c); //字节-》字符-》字节(一个字符占1个字节) out.write(b); //将转换后的一个字节数据写入到输出流中 } } }</span>
源码分析:
(1)byte[] src=tmp.getBytes()语句中的getBytes()方法作用将tmp对象的字符串内容转换成一系列字节数据并存储到新建的byte字节数组中;
(2)为了实现大小写字母的转换,我们通过使用输入流的read()方法(返回读到的字节数据)依次从输入流中读取一个字节,并将字节强制类型转换为字符,然后调用Character.toUpperCase(char)方法实现将小写字母转换为大写的功能,最后再将转换好的大写字母依次强制类型转换为Int(字节)类型写入到ByteArrayOutputStream输出流中。
效果演示:
字****符****流
五、Reader与Writer
Java中的字符为双字节的Unicode编码,而InputStream与OutputStream是用来处理字节的,在处理字符文本时不太方便,需要编写额外的程序代码。为此,Java为字符文本的输入输出专门提供了一套单独的类,即Reader与Writer抽象类。Reader、Writer下面也有许多子类,对具体IO设备进行字符输入输出,如FileReader就是用来读取文件流中的字符。
1.Reader类
(1)功能:用于从输入流读取字符数据,需要注意的是其所有的子类必须实现read(char[], int, int)和close()方法
(2)构造方法
>protected Reader()
>protected Reader(Object lock)
(3)常用方法
与InputStream方法相似
>int read(char[] cbuf):从输入流读取cbuf.length()个字符存储到char字符数组中;
>int read():从输入流读取一个字符并返回读取字符的个数
>boolean ready():判定输入流是否准备能读
2.Writer类
(1)功能:用于写入字符数据到输出流,需要注意的是其所有的子类必须实现write(char[],
int, int)、flush()和close()方法;
(2)构造方法
>protected Writer()
>protected Writer(Object lock)
(3)常用方法
Writer append(char c) :将指定的一个字符追加写入到输出流中;
abstract void close() :调用flush彻底写入数据,然后关闭流
abstract void flush() :彻底写入数据;
void write(char[] cbuf) :将字符数组中的数据写入到输出流;
void write(int c) :将一个字符写入到输出流;
void write(String str) :将字符串数据写入到输出流;
六、FileReader与FileWriter
1.FileReader类
(1)功能:用来读取文件流中的字符
(2)构造方法
>FileReader(File file) :构造一个FileReader对象,并指定要读取的File包含的文件资源
>FileReader(String fileName) :构造一个FileReader对象,并指定路径文件资源
(3)常用方法
与FileInputStream相似,详情:http://docs.oracle.com/javase/8/docs/api/index.html
2.FileWriter类
(1)功能:用来写字符数据到文件输出流
(2)构造方法
>FileWriter(File file)
>FileWriter(String fileName)
(3)常用方法
与FileOutputStream相似,详情:http://docs.oracle.com/javase/8/docs/api/index.html
3.源码实战
实现:使用FileWriter打开(创建)一个文件并向其写入字符串数据;使用FileReader读取该文件的字符串数据到指定的数组或字符串中。
FileChar.java
<span style="font-size:18px;">import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class FileChar { public static void main(String[] args ) { /*输出流:写入字符串数据到文件file*/ File file=new File("helloWorld.txt"); //实例化一个File对象,该对象可以操作文件的属性 try { FileWriter out = new FileWriter(file); //实例化一个字符输出流 out.write("HelloWorld,I am Lihua!--2015.02.06"); //向输出流写入字符串数据 out.close(); } catch (IOException e) { e.printStackTrace(); } /*输入流:读取文件file中的字符串数据*/ try { FileReader in = new FileReader(file); // char[] buf = new char[1024]; //第一种读取文件方法 // int len=in.read(buf); //从文件读取buf.length()个字符数据到字符数组buf //System.out.println(new String(buf,0,len)); int c; //第二中读取文件方法 String str = ""; while((c=in.read())!=-1) { str=str+(char)c; } System.out.println(str); } catch (IOException e) { e.printStackTrace(); } } }</span>
效果演示:
七、字节流类与字符流类对比图
1.IO输入流对应图
2.IO输入流对应图