前情回顾:用了两天的时间,终于把Collection和Map的内容都过了一遍,最大的收获应该就是学会了如何通过了解继承架构来学习和使用API。对了,还有算是第一次从JDK源代码中学习到东西,感觉真心高大上。
最近因为一些前辈的意见和毕设的需要,开始接触一些外国的网站,很有新鲜感,有种见识新世界的感觉。一开始还蛮怕的,觉得自己英语不太行。其实看多几眼,发现一般技术网站或IT门户类的网站用词都特别简明,不会像做六级英语阅读那样特别的复杂。顶多就是一些专有名词比较陌生,google一下就好了,轻松加愉快。预告:在下一季的博客里面,我会分享一些自己利用国外网站学习的经验以及如何整合网上分散资料的方法,纯属抛砖引玉,敬请期待哟。
——————————————————————————闲聊结束———————————————————————————
第十章:InputStream与OutputStream
林信良老师曾经说过:“想活用输入/输出API,一定要先了解Java中如何以串流(Stream)抽象化输入/输出概念,以及InputStreamOutputStream继承架构。如此一来,无论标准输入/输出、文档输入/输出、网络输入/输出、数据库输入/输出等都可用一致的操作进行处理。”
其实,就是一句话,了解继承架构!
第一节:串流继承架构
首先,我们来看看InputStream的常用类继承架构(有水印,避免不了啊啊啊)
再来看看OutputStream的常用类继承架构
初步了解了InputStream与OutputStream类继承架构之后,再来逐步说明相关类的情况。
第二节:标准输入/输出
写过一些Java代码的童鞋应该不会对System.in跟System.out陌生,查看API文档的话,我们会发现它们分别是InputStream与PrintStream的实例,分别表示标准输入(Standard input)与标准输出(Standard output)。眼见为实,我们再去看看源代码的情况:
1 /**
2 * The "standard" input stream. This stream is already 3 * open and ready to supply input data. Typically this stream 4 * corresponds to keyboard input or another input source specified by 5 * the host environment or user. 6 */
7 public final static InputStream in = null; 8
9 /**
10 * The "standard" output stream. This stream is already 11 * open and ready to accept output data. Typically this stream 12 * corresponds to display output or another output destination 13 * specified by the host environment or user. 14
15 */
16 public final static PrintStream out = null;
以上代码是在java.lang.System里面看到的,诚不欺我,果然是他们的实例。但是我觉得很奇怪,用final修饰之后,变量是不可以改动的,既然初始值就已经是null了,那还怎么用?有兴趣的小伙伴们看看源代码,然后告诉我哦。
就System.in而言,因为文本模式下通常是取得整行的用户输入,因此很少直接操作InputStream相关方法,而是使用java.util.Scanner打包System.in。我们可以使用System的setIn()方法指定InputStream实例,重新指定标准输入来源。
书中还举了两个例子,暂时没理解透,先放着不管吧。
第三节:FileInputStream与FileOutputStream等
FileInputStream是InputStream的子类,可以指定文件名创建对象,一旦创建文档就可以用来读取数据。FileOutputStream是OutPutStream的子类,可以指定文件名创建实例,一旦创建文档就可以用来输出数据。再强调一次,不使用时一定都要使用close()关闭文档。
FileInputStream、FileOutputStream在读取、写入文档时,都是以字节为单位的,通常会用一些高阶类加以打包。例如Scanner跟PrintStream类什么的。
讲完FileXX,现在讲ByteArrayXX。
ByteArrayInputStream是InputStream的子类,可以指定byte数组创建对象,一旦创建就可以将byte数组当做数据源进行读取。ByteArrayOutputStream是OutputStream的子类,可以指定byte数组创建实例,一旦创建就可以当做目的地写出数据。
第四节:串流处理装饰器
InputStream、OutputStream提供串流基本操作,如果想要为输入/输出的数据做加工处理的呢,可以使用打包器类。常用的打包器有具备缓冲区作用的BufferedInputStream、BufferedOutputStream,具备数据转换处理作用的DataInputStream、DataOutputStream,具备对象串行化能力的ObjectInuputStream、ObjectInputStream、ObjectOutputStream等。
由于这些类本身并没有更改InputStream或者OutputStream的行为,只不过在InputStream取得数据之后,再做一些加工处理,或者是要输出时做一些加工处理,再交由OutputStream进行输出,因此又称它们为装饰器(Decorator)。看以下这个例子:
1 package cc.openhome; 2
3 import java.io.*; 4
5 public class BufferedIO { 6 public static void dump(InputStream src, OutputStream dest) throws IOException { 7 try (InputStream input = new BufferedInputStream(src); 8 OutputStream output = new BufferedOutputStream(dest)) { 9 byte[] data = new byte[1024]; 10 int length = -1; 11 while ((length = input.read(data)) != -1) { 12 output.write(data, 0, length); 13 } 14 } 15 } 16 }
书上还介绍了几个常用的串流装饰器类,容我总结后再补回来。
InputStream、OutputStream是用来读取和输出字节数据的,如果实际上处理的是字符数据,使用InputStream、OutputStream就要对照编码表,在字符与字节之间进行转换。幸好JavaSE API已提供相关输入/输出字符处理类,不用我们进行编码转换的工作。这一部分这里就先不讨论了,以后再补充。
——————————————————————————第二十一天——————————————————————————
听说一件事连续做二十一天,就可以养成习惯。
1.连续二十一天,每天一篇博客。看起来不难,其实很不容易。如果仅仅只是把资料上的东西原封不动地贴上来,是不可能坚持三个星期的,再说就算是这样做了,也没什么意义。
2.我已经养成这个习惯了,那么有相同想法的童鞋们,你们呢?还有九天,我就完成对自己的许诺了,连续写三十天。
3.三十天之后,休息、回顾、反省、总结一段时间,然后再出发。