1、流的概念
1.1流的概念
Java程序通过流来完成输入/输出。流是生产或消费信息的抽象。流通过Java的输入/输出系统与物理设备链接。
1.2输入/输出流的概念
输入/输出流是相对于内存(程序)来说的,从程序外部流入程序内部的,就叫输入。反之,从程序流到外部,就是输出。
1.3输入/输出类分类
从功能上分:输入流和输出流
从流结构上分:字节流和字符流字节流的输入流和输出流的基础是InputStream和OutputStream这两个抽象类
字符流的输入流和输出流的基础是Reader和Writer这两个抽象类
字符流的底层也是字节流来实现的。
输入流,读数据的逻辑:1)open a stream 2)while more information 3)read information 4)close the stream
输出流,写数据的逻辑:1)open a stream 2)while more information 3)write information 4)close the stream
1.4节点流和过滤流的概念
节点流:从特定的地方读写的流类,例如:磁盘或一块内存区
过滤流:使用节点流作为输入或输出。过滤流是使用一个已经存在的输入流或输出流连接创建的。
1.5 节点流/过滤流的分类
如上图所示,红字表示过滤流及其子类。黑字作为节点流。FilterInputStream是将节点流作为源进行处理的。FilterOutStream包括DataOutputStream、BufferedOutputStream、PrintStream,节点流包括FileoutputStream、ByteArrayOutputStream、ObjectOutputStream、PipedOutputStream,将节点流作为目标输出流。
2 字节流实例
2.1 实例1 文件输入流
从磁盘上的某文件将数据读取到程序中。
从某test.txt中读出数据显示在命令行
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class FileTest
{
public static void main(String[] args) throws Exception
{
File file=new File("D:/test.txt");
InputStream is=new FileInputStream(file);
byte[] b=new byte[10];
int length=0;
while((length=is.read(b,0,10))!=-1)
{
System.out.println("length:"+length);
String str=new String(b,0,length);
System.out.println(str);
}
is.close();
}
}
代码解释:
public int read(byte[] b,int off,int len) throws IOException
参数含义:
b
- 读入数据的缓冲区(预先定义的数组)
off
- 在其处写入数据的数组 b
的初始偏移量。
len
- 要读取的最大字节数。
返回值:
读入缓冲区的总字节数,如果由于已到达流末尾而不再有数据,则返回 -1,例如上题若文件中有23个字符。那么第一次while循环的length值为10,第二次为10,第三次为3,那么第四次是-1,退出循环。
2.2 实例2 输出流
将字符串转换为字节数组输出流,然后再写入test.txt中。
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class OutputStreamTest2
{
public static void main(String[] args) throws IOException
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
String str = "helloworld";
byte[] b =str.getBytes();
os.write(b);
byte[] result=os.toByteArray();
for(byte r:result)
{
System.out.print((char)r);
}
File file=new File("D:/test.txt");
FileOutputStream fos=new FileOutputStream(file);
fos.write(result);
}
}
2.3 实例三 过滤流和节点流的混合应用
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 MutilStreamTest
{
public static void main(String[] args) throws Exception
{
DataInputStream dis=new DataInputStream(new BufferedInputStream(new FileInputStream("D:/test.txt")));
byte b[]=new byte[40];
dis.read(b);
dis.close();
DataOutputStream dos=new DataOutputStream(new BufferedOutputStream(new FileOutputStream("D:/aaa.txt")));
dos.write(b);
dos.close();
}
}
程序的含义,将test.txt文件的内容以基本数据流的缓存形式读入和写出(相对于程序)到文件aaa.txt中
在该程序中,FileInputStream/FileOutputStream节点流,节点流通过过滤流BufferedInputStream/BufferedOutputStream的包装后,再由过滤流DataInputStream/DataOutputStream的包装。这就相当于给FileInputStream进行了两层包装。
看了上面的程序,读者有没有产生如下的疑问呢?
1、既然是过滤流,就可以包装其他的节点流,那BufferedInputStream/BufferedOutputStream和DataInputStream/DataOutputStream的位置是否可以调换呢?如果可以调换,表示什么意思呢?
经过笔者的测试发现:
BufferedInputStream bis=new BufferedInputStream(new DataInputStream(new FileInputStream("D:/test.txt")));
byte b1[]=new byte[40];
bis.read(b1);
bis.close();
for(byte bb:b1)
{
System.out.print((char)bb);
}
BufferedOutputStream bos=new BufferedOutputStream(new DataOutputStream(new FileOutputStream("D:/aaa.txt")));
bos.write(b1);
bos.flush();
bos.close();
BufferedInputStream/BufferedOutputStream和DataInputStream/DataOutputStream的位置调换,仍然可以实现以上的功能。只是在理解的时候有偏差。DataStream放在最外边进行包装,相当于基本数据流先转换为缓冲流再转换为文件输入流,而后者表示缓冲流转换为基本数据流再转换为文件输入流。也就是说二者面向程序提供的接口有所不同。
比如:
DataInputStream dis=new DataInputStream(new BufferedInputStream(new FileInputStream("D:/test.txt")));
dis.readDouble();
dis.readByte()
可以直接对8种基本数据类型进行操作(test.txt中必须是对应类型的数据,方可这样操作),而buffer就不可以。这就像超人内裤外穿是人,内穿也是人,只是同一个人所展现的形式不一样,所表现的能力也就不一样了。
注意:1、过滤流单纯的包装过滤流是没有意义的。
2、节点流也是不可以包装节点流的。
3、根据需求将节点流用过滤流进行适当的包装
2.4 装饰模式
I/O中涉及到的最重要的模式,装饰模式,又称为包装模式。即动态的给一个对象添加一些额外的职责。
可参考链接:http://blog.csdn.net/fumier/article/details/41702715
DataInputStream dis=new DataInputStream(new BufferedInputStream(new FileInputStream("D:/test.txt")))
首先,FileInputStream和FilterInputStream继承于InputStream,DataInputStream与BufferedInputStream继承于FilterInputStream。
FilterInputStream部分代码如下:
protected volatile InputStream in;
/**
* Creates a <code>FilterInputStream</code>
* by assigning the argument <code>in</code>
* to the field <code>this.in</code> so as
* to remember it for later use.
*
* @param in the underlying input stream, or <code>null</code> if
* this instance is to be created without an underlying stream.
*/
protected FilterInputStream(InputStream in) {
this.in = in;
}
FilterInputStream的构造方法接收InputStream类,FilterInputStream也可以接收本身。这样就实现了功能的叠加。3 字符流
以上介绍了字节流,字节流提供了处理任何类型输入/输出操作的功能,但是它不能直接操作Unicode字符。所以接下来来谈谈字符流。
字符流层次结构的顶层是Reader和Writer抽象类。具体实现类如下图所示。
如上所示,红字表示过滤流及它的子类。黑字表示节点流。
3.1常用类介绍
3.1.1InputStreamReader/OutputStreamWriter
InputStreamReader从字节流转换为字符流的桥梁。
OutputStreamWriter从字符流转换为字节流的桥梁。
构造方法:
InputStreamReader(InputStream in)
InputStreamReader(InputStream in,String enc)
OutputStreamWriter(OutputSteam out)
OutputStreamWriter(OutputStream out,String enc)
3.1.2FileReader/FileWriter
FileReader类创建了一个可以读取文件内容的Reader类。FileReader继承于InputStreamReader.
构造方法:
FileReader(String filePath)
FileReader(File fileObj)
每一个都能引发一个FileNotFoundException异常。filePath是一个文件的完整路径。fileObj是描述该文件的File对象。
FileWriter创建一个可以写文件Writer类。
FileWriter继承于OutputStreamWriter。
最常用构造方法:
FileWriter(String filePath)
FileWriter(String filePath,boolean append)
FileWriter(File fileObj)
append如果为true,则将字节写入文件末尾处,而不是写入文件开始出。
3.1.3BufferedReader
BufferedReader通过缓冲输入提高性能。
构造方法:
BufferedReader(Reader inputStream)
BufferedReader(Reader inputStream,int bufSize)
第一种形式创建一个默认缓冲区长度的缓冲字符流。
第二种形式,缓冲区长度由bufSize传入和字节流的情况相同,缓冲一个输入字符流同样提供支持可用缓冲区中流内反向移动的基础。
为了支持这点,BufferedReader实现了mark()和reset()方法,并且BufferedReader.markSupported()返回true.
BufferedWriter是一个增加了flush( )方法的Writer。flush( )方法可以用来确保数据缓冲区确实被写到实际的输出流。用BufferedWriter 可以通过减小数据被实际的写到输出流的次数而提高程序的性能。
3.1.4 RandomAccessFile(随机访问文件类)
RandomAccessFile包装了一个随机访问的文件。它不是派生于InputStream和OutputStream,而是实现了基本定义输入、输出方法的DataInput和DataOutput.它支持定位请求,也就是说可以在文件内部放置文件指针。
构造方法:
RandomAccessFile(File fileObj,String access)
RandomAccessFile(String fileName,String access)
第一种形式fileObj指定了作为File对象打开的文件名称
第二种形式,文件名是file参数传入的。
两种情况下,access 都决定允许访问何种文件类型。如果是“r”,那么文件可读不可写,如果是
“rw”,文件以读写模式打开
3.2实例1 OutputStreamWriter/InputStreamReader
OutputStreamWriter/InputStreamReader实例
//将两行字符写入文本中,并从中读取出来显示在命令行。
public class CharStreamTest
{
public static void main(String[] args) throws Exception
{
FileOutputStream fos = new FileOutputStream("D:/test.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos);
osw.write("Thank you");
osw.write("\n");
osw.write("HelloWorld");
osw.close();
FileInputStream fis=new FileInputStream("D:/test.txt");
InputStreamReader isr=new InputStreamReader(fis);
int length=0;
while((length=isr.read())!=-1)
{
System.out.print((char)length);
}
isr.close();
}
}
3.3实例2 BufferedReader
//键盘输入,存入缓冲区,再显示到命令行
public class CharStreamTest1
{
public static void main(String[] args) throws Exception
{
InputStreamReader isr=new InputStreamReader(System.in);
BufferedReader br=new BufferedReader(isr);
String str;
while((str=br.readLine())!=null)
{
System.out.println(str);
}
br.close();
}
}
3.4 实例3 RandomAccessFile
<span style="font-size:12px;">public class RandomAccessTest1
{
public static void main(String[] args) throws Exception
{
File file = new File("D:/test.txt");
RandomAccessFile asf = new RandomAccessFile(file, "rw");
String str;
while (null != (str = asf.readLine()))
{
System.out.println(str);
}
asf.writeUTF("HappyEvery");
asf.close();
}
}</span>