开胃菜
先看一张网上流传的http://java.io包的类结构图:
当你看到这幅图的时候,我相信,你跟我一样内心是崩溃的。
有些人不怕枯燥,不怕寂寞,硬着头皮看源码,但是,能坚持下去全部看完的又有几个呢!
然而,就算源码全部看完看懂,过不了几天,脑子里也会变成一团浆糊。
因为这里的类实在太多了。可能我们反复看,反复记,也很难做到清晰明白。
他就像是一块超级硬的骨头,怎么啃都啃不烂。
面对这样的做法,要坚决对他说,NO。
记不住,怎么办?
我的做法是找出他们的共性,给他们分类,只记典型,触类旁通。
上面的图虽然有分类,但是还不够细,而且没有总结出方便记忆的规律,所以我们要重新整理和归类。
这篇文章中,使用了两种分时给他们分组,目的是更全面的了解共性,帮助记忆。
分类一:按操作方式(类结构)
- 字节流和字符流:
- 字节流:以字节为单位,每次次读入或读出是8位数据。可以读任何类型数据。
- 字符流:以字符为单位,每次次读入或读出是16位数据。其只能读取字符类型数据。
- 输出流和输入流:
- 输出流:从内存读出到文件。只能进行写操作。
- 输入流:从文件读入到内存。只能进行读操作。
注意:这里的出和入,都是相对于系统内存而言的。
- 节点流和处理流:
- 节点流:直接与数据源相连,读入或读出。
- 处理流:与节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流。
为什么要有处理流?直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。
按操作方式分类结构图:
根据以上分类,以及jdk的说明,我们可以画出更详细的类结构图,如下:
分类说明
- 1. 输入字节流InputStream:
输入字节流的继承图可见上图,可以看出:- FileInputStream: 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
- ByteArrayInputStream:
- PipedInputStream: 是从与其它线程共用的管道中读取数据。PipedInputStream的一个实例要和PipedOutputStream的一个实例共同使用,共同完成管道的读取写入操作。主要用于线程操作。
- ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)
- 2. 输出字节流OutputStream:
输出字节流的继承图可见上图,可以看出:- FIleOutputStream:是两种基本的介质流
- ByteArrayOutputStream: 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。
- PipedOutputStream:是向与其它线程共用的管道中写入数据。
- ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。
字节流的输入和输出对照图:
- 3. 字符输入流Reader:
在上面的继承关系图中可以看出:- FileReader:
- PipedReader:是从与其它线程共用的管道中读取数据
- CharArrayReader:
- CharReader、StringReader 是两种基本的介质流,它们分别将Char 数组、String中读取数据。
- BufferedReader 很明显就是一个装饰器,它和其子类负责装饰其它Reader 对象。
- FilterReader 是所有自定义具体装饰流的父类,其子类PushbackReader 对Reader 对象进行装饰,会增加一个行号。
- InputStreamReader: 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader 可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream 转变为Reader 的方法。我们可以从这个类中得到一定的技巧。Reader 中各个类的用途和使用方法基本和InputStream 中的类使用一致。后面会有Reader 与InputStream 的对应关系。
- 4. 字符输出流Writer:
在上面的关系图中可以看出:- FileWriter:
- PipedWriter:是向与其它线程共用的管道中写入数据
- CharArrayWriter:
- CharArrayWriter、StringWriter 是两种基本的介质流,它们分别向Char 数组、String 中写入数据。
- BufferedWriter 是一个装饰器,为Writer 提供缓冲功能。
- PrintWriter 和PrintStream 极其类似,功能和使用也非常相似。
- OutputStreamWriter: 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter 其实就是一个实现此功能的具体类(具体可以研究一SourceCode)。功能和使用和OutputStream 极其类似,后面会有它们的对应图。
字符流的输入和输出对照图:
- 5. 字符流与字节流转换
- 转换流的特点:
- 其是字符流和字节流之间的桥梁;
- 可对读取到的字节数据经过指定编码转换成字符;
- 可对读取到的字符数据经过指定编码转换成字节;
- 何时使用转换流?
- 当字节和字符之间有转换动作时;
- 流操作的数据需要编码或解码时。
- 具体的实现:
InputStreamReader:输入流转到读流;
String fileName= "d:"+File.separator+"hello.txt"; File file=new File(fileName); Writer out=new OutputStreamWriter(new FileOutputStream(file)); out.write("hello"); out.close();
OutputStreamWriter:输出流转到写流;
String fileName= "d:"+File.separator+"hello.txt"; File file=new File(fileName); Reader read=new InputStreamReader(new FileInputStream(file)); char[] b=new char[100]; int len=read.read(b); System.out.println(new String(b,0,len)); read.close();
这两个流对象是字符体系中的成员,它们有转换作用,本身又是字符流,所以在构造的时候需要传入字节流对象进来。
分类二:按操作对象
按操作对象分类结构图:
分类说明:
- 对文件进行操作(节点流):
- FileInputStream(字节输入流),
- FileOutputStream(字节输出流),
- FileReader(字符输入流),
- FileWriter(字符输出流)
- 对管道进行操作(节点流):
- PipedInputStream(字节输入流),
- PipedOutStream(字节输出流),
- PipedReader(字符输入流),
- PipedWriter(字符输出流)。
PipedInputStream的一个实例要和PipedOutputStream的一个实例共同使用,共同完成管道的读取写入操作。主要用于线程操作。
- 字节/字符数组流(节点流):
- ByteArrayInputStream,
- ByteArrayOutputStream,
- CharArrayReader,
- CharArrayWriter;
是在内存中开辟了一个字节或字符数组。
除了上述三种是节点流,其他都是处理流,需要跟节点流配合使用。
- Buffered缓冲流(处理流):
- BufferedInputStream,
- BufferedOutputStream,
- BufferedReader,
- BufferedWriter,
是带缓冲区的处理流,缓冲区的作用的主要目的是:避免每次和硬盘打交道,提高数据访问的效率。
- 转化流(处理流):
- InputStreamReader:把字节转化成字符;
- OutputStreamWriter:把字节转化成字符。
- 基本类型数据流(处理流):用于操作基本数据类型值。
- DataInputStream,
- DataOutputStream。
因为平时若是我们输出一个8个字节的long类型或4个字节的float类型,那怎么办呢?可以一个字节一个字节输出,也可以把转换成字符串输出,但是这样转换费时间,若是直接输出该多好啊,因此这个数据流就解决了我们输出数据类型的困难。数据流可以直接输出float类型或long类型,提高了数据读写的效率。
- 打印流(处理流):
- PrintStream,
- PrintWriter,
一般是打印到控制台,可以进行控制打印的地方。
- 对象流(处理流):
- ObjectInputStream,对象反序列化;
- ObjectOutputStream,对象序列化;
把封装的对象直接输出,而不是一个个在转换成字符串再输出。
- 合并流(处理流):
- SequenceInputStream:可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。
典型使用案例
- 1. 复制文件:
/**
* 复制文件:一边读,一边写
*/
class hello { public static void main(String[] args) throws IOException { if (args.length != 2) { System.out.println("命令行参数输入有误,请检查"); System.exit(1); } File file1 = new File(args[0]); File file2 = new File(args[1]); if (!file1.exists()) { System.out.println("被复制的文件不存在"); System.exit(1); } InputStream input = new FileInputStream(file1); OutputStream output = new FileOutputStream(file2); if ((input != null) && (output != null)) { int temp = 0; while ((temp = input.read()) != (-1)) { output.write(temp); } } input.close(); output.close(); } }
- 说明:
- 流在使用结束后,一定要执行关闭操作,即调用close( )方法。
- FileInputStream.read():
这个方法是对这个流一个一个字节的读,返回的结果就是这个字节的int表示方式;
当已经没有内容时,返回的结果为-1; - FileOutputStream.write():
将内容写到文件。
- 2. 不使用FIle,将流中的字符转换大写小:
public static void main(String[] args) throws IOException { String str = "ROLLENHOLT"; ByteArrayInputStream input = new ByteArrayInputStream(str.getBytes()); ByteArrayOutputStream output = new ByteArrayOutputStream(); int temp = 0; while ((temp = input.read()) != -1) { char ch = (char) temp; output.write(Character.toLowerCase(ch)); } String outStr = output.toString(); input.close(); output.close(); System.out.println(outStr); }
- 说明:
- 流在使用结束后,一定要执行关闭操作,即调用close( )方法。
- 3. 使用管道流在多个线程间通信:
/**
* 消息发送类
* */
class Send implements Runnable { private PipedOutputStream out = null; public Send() { out = new PipedOutputStream(); } public PipedOutputStream getOut() { return this.out; } public void run() { String message = "hello , Rollen"; try { out.write(message.getBytes()); } catch (Exception e) { e.printStackTrace(); } try { out.close(); } catch (Exception e) { e.printStackTrace(); } } } /** * 接受消息类 */ class Recive implements Runnable { private PipedInputStream input = null; public Recive() { this.input = new PipedInputStream(); } public PipedInputStream getInput() { return this.input; } public void run() { byte[] b = new byte[1000]; int len = 0; try { len = this.input.read(b); } catch (Exception e) { e.printStackTrace(); } try { input.close(); } catch (Exception e) { e.printStackTrace(); } System.out.println("接受的内容为 " + (new String(b, 0, len))); } } /** * 测试类 */ class hello { public static void main(String[] args) throws IOException { Send send = new Send(); Recive recive = new Recive(); try { //管道连接 send.getOut().connect