c2 Streams - Filter Streams

Filter Streams被组织在“链”(chain)中,每一个Filter流从上一个流中获取数据,然后传给下一个Filter 流。


Chain Filter Stream Together

一般的使用Filter Stream的方式:

FileInputStream     fin = new FileInputStream("data.txt");
BufferedInputStream bin = new BufferedInputStream(fin);

这种方式可能会导致的问题是从fin、bin都调用了read()方法,这会导致问题,解决办法是可以这么写:

InputStream in = new FileInputStream("data.txt");
in = new BufferedInputStream(in);

利用了多态,面向接口,所以使用的只会是Filter Stream,而不会是FileInputStream。但如果想使用在superclass中没有的方法呢,可以这么写:

DataOutputStream dout = new DataOutputStream(new BufferedOutputStream(
    new FileOutputStream("data.txt")));


BufferedStream


BufferedOutputStream内部有一个缓冲区buff,里面临时存放着要写入的数据,当buff已满或者flush时,一次性写入。这要比多次写入每次一点数据要快。尤其在网络连接中,TCP segment 和 UDP package 都有一个头,大概40字节,假入要写入1k的数据,如果每次1个字节,那光头就要发送40k字节,而如果一次性发送,只需要1k多一点。虽然实际中没有这么夸张,因为网卡和TCP实现者都有不同层次的缓存,但网络写入时用缓冲流会提高很大效率。

BufferedIutputStream内部又有一个缓冲区buf,当调用read()方法时,先从缓冲区中取数据,如果缓冲区中没数据了,就从underlying source中读数据,这时,从BufferedIutputStream中读出的数据和从underlying stream中的读出的数据一样多,不管客户端是否要立即读取完所有数据,暂时不用的保存在buf中。目前在网络连接中BufferedIutputStream的作用不是那么明显,因为网络中的瓶颈在于网络传输速度,而不在网卡向程序传输数据的速度。


public BufferedInputStream(InputStream in)
public BufferedInputStream(InputStream in, int bufferSize)
public BufferedOutputStream(OutputStream out)
public BufferedOutputStream(OutputStream out, int bufferSize)

默认的input stream的buf size是2048字节,output stream 是512字节。理想的buf的大小要看要缓冲的是什么流,对应网络连接,buf一般要比package稍大些,但是,package大小比较难预测,会因网络状况和所使用协议不同而又不同,一般情况下,一个TCP segment 的大小不超过1 kilobyte 。


BufferedIutputStream只重写了InputStream的方法,没有新方法加入,大部分InputStream在返回前只读一次underlying stream 或者 source,但BufferedIutputStream与他们不同,在他的可读mutilbyte的方法中,它会一直读直到他的buf完全读满,或者stream读完了,或者underlying stream被block了。

BufferedIutputStream也没有新的方法,每次write数据都是写在了buf中,记住send时要flush。


PrintStream

最常用的的PrintStream是System.out,out就是个PrintStream。

print()将arg转换成string,以默认编码写入到underlying stream中,println()则是会在行末尾添加一个与平台相关的line-separator,在Unix(Mac Os X)是linefeed  \n, 在Mac Os 9上是 return carrige \r,在Window上是 \r\n .


网络程序员要像避免瘟疫一样避免使用PrintStream!

原因一:println()与平台有关,不同平台有不同的行分隔符。在本地输出到控制台没什么问题,但在网络程序中,想HTTP协议要求换行符是 “\r\n” ,如果在Windows上向client、server发送信息没问题,但在Mac,Unix上就麻烦了,因为那些系统的换行符不是“\r\n”.尽管HTTP协议也接受非法的line teminator,但偶尔还是有异常出现的。.

原因二:PrintStream使用当前平台默认的编码,但这并非是client或者server期望的。PrintStream没有提供任何改变字符集的方法。尽管可以用PrintWriter代替使用,但这个问题仍然存在。

原因三:Printstream吃掉了所有的异常。这适合写些HelloWorld的程序教初学者,让刚开始学习程序的初学者不用为异常处理麻烦。但网络程序比本地控制台要不稳定得多,必须准备好处理意想不到的异常,而这些处理需要异常抛出。

Printstream在内部有异常是设置了一个内部标志,通过checkError()来查看。但简而言之,Printstream提供的错误信息对写网络程序来时完全不够。


Data Streams

DataInputStream、DataOutputStream提供了写和读java基础类型和以二进制表示的String的方法。使用二进制的首要目的是为了在两个使用网络、datafile、管道和其他中间媒介连接起来的java程序间传递数据,output stream写什么,input stream就读什么。

public final void writeChars(String s) throws IOException
public final void writeBytes(String s) throws IOException
public final void writeUTF(String s) throws IOException

writeChars()遍历String,将每个character按顺序,2个字节写入。

writeBytes()遍历String,只写每个character最低位有效字节。对于某些字符集比如Latin-1会丢失某些string的信息。大部分时候要避免使用该方法。

上面2个方法都不包含string的长度,writeUTF()包含string的长度,该方法只能用于和另外一个java程序交换text,另外的那个java程序使用 DataInputstream 读取。要和其他任意程序交换UTF-8的text,应该用适当的编码的InputStreamReader。


DataInputStream提供了著名的readLine()方法,但任何情况下都别使用这个方法。已经被废弃且充满了bug。废弃的原因是在大部分情况下不把non-ASCII转换成bytes,该任务现在由BufferedReader的readLine()代替。但这两个方法都有一个隐蔽的bug:都不把carriage return 当作一行的结尾,只认为一个linefeed(/n)或者 carriage return and linefeed ( /r/n )是行的结尾。当在流中读到一个carriage return时,readLine()继续读看下个是否是linefeed,如果是,/r/n 被thrown away,以string返回该行。如果不是,则throw away /r,返回该行,读到的那个字符作为下一行。可是,如果/r是该流的最后一个字符,那就完了,一直在那等吧!

该问题在读文件时还不明显,因为stream的末尾会有-1在等着。但在网络中,一个持续的connection,当client、server发送外最后一个character后没有关闭连接,等着对方回应,如果幸运,最终会因连接超时而得到一个IOException,尽管会多花一些时间和丢掉流中的最后一行数据,否则就无限期的等待下去吧!




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值