参考文章地址: http://blog.csdn.net/oracle_microsoft/article/details/2634231
Java IO体系结构看似庞大复杂,其实有规律可循,要弄清楚其结构,需要明白两点:
1. 其对称性质:InputStream 与 OutputStream, Reader 与 Writer,他们分别是一套字节输入-输出,字符输入-输出体系
2. 原始处理器(适配器)与链接流处理器(装饰器)
其结构图如下:
InputStream-OutputStream体系
Reader-Writer体系
1. 基类
InputStream与OutputStream是所有字节型输入输出流的基抽象类,同时也是适配器(原始流处理器)需要适配的对象,也是装饰器(链接流处理器)装饰对象的基类.
2. 原始流处理器
原始流处理器接收Byte数组对象,String对象,FileDescriptor对象将其适配成InputStream,以供其他装饰器使用,他们都继承自InputStream 包括如下几个:
ByteArrayInputStream: 接收Byte数组为流源,为多线程通信提供缓冲区操作功能
FileInputStream: 接收一个File作为流源,用于文件的读取
PipedInputStream: 接收一个PipedOutputStream,与PipedOutputStream配合作为管道使用
StringBufferInputStream: 接收一个String作为流的源(已弃用)
3. 链接流处理器
链接流处理器可以接收另一个流处理器(InputStream,包括链接流处理器和原始流处理器)作为源,并对其功能进行扩展,所以说他们是装饰器.
1) FilterInputStream继承自InputStream,是所有装饰器的父类,FilterInputStream内部也包含一个InputStream,这个InputStream就是被装饰类--一个原始流处理器,它包括如下几个子类:
BufferedInputStream: 用来将数据读入内存缓冲区,并从此缓冲区提供数据
DataInputStream: 提供基于多字节的读取方法,可以读取原始数据类型(Byte, Int, Long, Double等等)
LineNumberInputStream: 提供具有行计数功能的流处理器
PushbackInputStream: 提供已读取字节"推回"输入流的功能
2) ObjectInputStream: 可以将使用ObjectOutputStream写入的基本数据和对象进行反串行化
3) SequenceInputStream: 可以合并多个InputStream原始流,依次读取这些合并的原始流
对于OutputStream, Reader, Writer的体系结构也跟InputStream的结构类似
====================================================================================
Java分为字节流(Stream结尾)和字符流(Reader、Write结尾),再分为输入流(InputStream、Reader)和输出流(OutputStream、Write),输入输出相对于内存而言。在读字符的时候用字符流,如文本文件、XML(我想xml明明是字母字符组成的,属于ASCII文件,为何不用stream读取呢?)等。在读二进制文件时候用字节流,如RAR、EXE等不是文本以外的文件(图片)。Buffered开头的流只是加了缓冲区,为了读写提高效率。字符流不能直接输出,需要转换成字节流才能输出。
二.用法分析
Java IO的一般使用原则(部分来自百度文库):
(1) 按数据来源(去向)分类:
是文件: FileInputStream, FileOutputStream, FileReader, FileWriter
是byte[]:ByteArrayInputStream, ByteArrayOutputStream
是Char[]: CharArrayReader, CharArrayWriter
是String: StringBufferInputStream, StringReader, StringWriter
网络数据流:InputStream, OutputStream, Reader, Writer
(2) 按是否格式化输出分:
要格式化输出:PrintStream, PrintWriter
(3) 按是否要缓冲分:
要缓冲:BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter。
(4) 按数据格式分:
二进制格式(只要不能确定是纯文本的): InputStream, OutputStream及其所有带Stream结束的子类
纯文本格式(含纯英文与汉字或其他编码方式);Reader, Writer及其所有带Reader, Writer的子类
(5) 按输入输出分:
输入:Reader, InputStream类型的子类;输出:Writer, OutputStream类型的子类
(6) 特殊需要:
从Stream到Reader,Writer的转换类:InputStreamReader, OutputStreamWriter
对象输入输出:ObjectInputStream, ObjectOutputStream
进程间通信:PipeInputStream, PipeOutputStream, PipeReader, PipeWriter
合并输入:SequenceInputStream
更特殊的需要:PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader
(7) 决定使用哪个类以及它的构造进程的一般准则如下(不考虑特殊需要):
考虑最原始的数据格式是什么:是否为文本?是输入还是输出?是否需要转换流:InputStreamReader, OutputStreamWriter?数据来源(去向)是什么:文件?内存?网络?是否要缓冲:bufferedReader (特别注明:一定要注意的是readLine()是否有定义,有什么比read, write更特殊的输入或输出方法)是否要格式化输出:print。
三.若干实例
还是寒假时候写的,权当复习了,折叠代码的插件找不到了,先看着吧。
1.System.in
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/*
* System.in是InputStream static final的,包含了多态,叫同步式或者阻塞式
* 读取ASCII和二进制文件(图片),而字母就是ASCII字符(个人理解)。
*/
public class TestSystemIn {
public static void main(String[] args) {
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);//有readline
String s = null;
try {
s = br.readLine();
while(s!=null) {
if(s.equalsIgnoreCase("exit")) {
break;
}
System.out.println(s.toUpperCase());
s = br.readLine();
}
br.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
2.buffer
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class TestBuffer {
public static void main(String[] args) {
try {
//查看修改日就可以判断文件是否是新建的了
BufferedWriter bw = new BufferedWriter(new FileWriter("d:/java.txt"));
BufferedReader br = new BufferedReader(new FileReader("d:/java.txt"));
String s = null;
for(int i=1; i<100; i++) {
s = String.valueOf(Math.random());
bw.write(s);
bw.newLine();//换行
}
//刷新该流的缓冲,br没有该方法
bw.flush();
while((s=br.readLine())!=null) {
System.out.println(s);
}
bw.close();
br.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
3.FileInputStream
import java.io.*;
public class TestFileInputStream {
public static void main(String[] args) {
FileInputStream in = null;
try {
in = new FileInputStream("e:/1.txt");
}catch(FileNotFoundException e) {
System.out.println("找不到文件");
System.exit(-1);
}
//下面表示找到了文件
int tag = 0;
try {
long num = 0;
while((tag = in.read())!=-1) {
//read是字节流,若是有汉字就显示不正常了,使用reader就解决了
System.out.print((char)tag);
num++;
}
in.close();
System.out.println();
System.out.println("共读取了" + num + "字符");
}catch(IOException e1) {//read和close都会抛出IOException
System.out.println("文件读取错误");
System.exit(-1);
}
}
}
4.FileOutputStream实现复制功能
import java.io.*;
/*
* 实现复制功能
*/
public class TestFileOutputStream {
public static void main(String[] args) {
int b = 0;
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("d:/java.txt");
//下面的若是不存在的话会自动建立
out = new FileOutputStream("d:/my_java.txt");
while((b=in.read())!=-1) {
out.write(b);
}
in.close();
out.close();
}catch(FileNotFoundException e) {
System.out.println("找不到指定文件");
System.exit(-1);
}catch(IOException e1) {
System.out.println("文件复制错误");
System.exit(-1);
}
System.out.println("文件已复制");
}
}
5.ObjectOutputStream与Serializable
import java.io.*;
/*
* transient(透明的),可以用来修饰成员变量,
* 当进行序列化时不予考虑,修饰int 的话,不管原来的值是多少
* 输出的就是0
*/
public class TestObjectIO {
public static void main(String[] args) throws Exception {
T t = new T();
t.k = 8;
FileOutputStream fos = new FileOutputStream("d:/1.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(t);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream("d:/1.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
T tRead = (T)ois.readObject();
System.out.println(tRead.i + " " + tRead.j + " " + tRead.k);
}
}
class T implements Serializable {
int i = 10;
int j = 9;
double d = 2.3;
int k = 15;
}
6.转换编码方式
import java.io.*;
/*
* 中文windows默认GBK编码方式
* 追加的内容显示为问号,不知道咋回事
*/
public class TestTransForm {
public static void main(String[] args) {
try {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d:/java.txt"));
osw.write("你好123");//可以直接写入字符串,包括中文,因为外边的是字符流
System.out.println("编码方式:" + osw.getEncoding());//ISO8859_1是西欧语言,又叫latin-1,此时未考虑东方人,国标(ISO)为Unicode
osw.close();
osw = new OutputStreamWriter(new FileOutputStream("d:/java.txt",true),"ISO8859_1");//true表示追加
osw.write("这是追加的内容");
System.out.println("编码方式:" + osw.getEncoding());
osw.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
7.输出重定向
[code]
import java.io.*;
/*
* Print流属于输出流,提供了重载的方法很多,
* PrintWriter和PrintStream不会抛异常,用户通过检测错误状态获取信息,
* 包含自动flush功能,有什么用呢,在jsp里也要输出一些东西,
* 但不必每次抛异常。
*/
public class TestPrintStream {
public static void main(String[] args) {
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream("d:/java.txt");
ps = new PrintStream(fos);
}catch (IOException e) {
e.printStackTrace();
}
if(ps!=null) {
System.setOut(ps);//输出重定向
}
int ln = 0;
for(char c=0; c<65535; c++) {
System.out.print(c + " ");
if(ln++>100) {
System.out.println();
ln = 0;
}
}
}
}
8.DataStream
import java.io.*;
public class TestDataStream {
public static void main(String[] args) {
//先在内存里分配一个字节数组,再有一个 OutputStream,再加上一个数据流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
try {//写出读入
dos.writeDouble(Math.random());
dos.writeBoolean(true);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
System.out.println(bais.available());//共几个字节可用
DataInputStream dis = new DataInputStream(bais);
先写的先读(队列),下面这两个输出不可以调换,否则就先输出了double里的一个字节
System.out.println(dis.readDouble());
System.out.println(dis.readBoolean());
dos.close();
dis.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
===================================================================================================================================
字符流和字节流
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。字节流和字符流的区别:
(1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
(2)处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
(3)字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的;而字符流在操作的时候下后是会用到缓冲区的,是通过缓冲区来操作文件,我们将在下面验证这一点。
结论:优先选用字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。