一直以来对IO这块的东西认识都不是很清楚。每次涉及这块的东西,一般都找点现成的代码复制粘贴一下。这可不好,于是静下心来好好弄一下。
什么是IO?
一提起IO给人的感觉就比较复杂(不过确实也挺复杂的),可能是学C的时候吓怕了,呵呵。不过,理一下思路还是比较清晰的。在Java里,IO说白了就是对读、写的一种抽象。没有什么其他的复杂的东西。至于读什么、写什么,那就看你想读什么、想写什么,由自己控制了。
读什么?写什么?
看看下面这个图会清楚很多(只用看InputStream就行,OutputStream一样):
大家可以看一下直接继承InputStream的几个类ByteArrayInputStream、FileInputStream、StringBufferInputStream(PipedInputSteram和SequenceInputStream一般不用)。呵呵,看看这几个类的构造函数就明了了。第一个是把byte数字作为IO源读取byte数组里的东西,第二个是把文件作为IO源读取文件里的东西,第三个是把String作为IO源读取String里面的东西。这么看这几个IO实在简单。说白了就是从byte数组、文件、String按字节读取罢了.....
一句简单的话说:IO体系第一级继承的几个类解决了从哪儿读或者写到哪儿的问题。
怎么读?怎么写?
试想一下,现在要读一个几百兆的文件,怎么读呢?是一下子把整个文件都读到内存慢慢分析呢,还是每次读只读一个字节逐个分析呢?显然两个都不好,于是我们想到了缓冲。所以IO体系上有了BufferedInputStream。再想一下,现在你想写一个“3.1415926”到文件里怎么写?总不能自己把这个数的二进制形式写出来,转成byte数组写到文件里吧。即便写进去了读的时候怎么读啊?谁知道你写进去的是数字而不是其他字符呢?
于是针对怎么读Java
IO体系上出现了一个分支FilerInoutStream(FilterOutputStream)。继承了这个类的几个类各自对怎么读、写提供了不同的支持。Buffered提供了缓冲,LineNumber提供了对不同类型数据的读写。
还是一句话:IO体系的第二级继承解决了怎么读、怎么写。
以不变应万变
说IO不能不说他的Decorator(装饰)模式。此模式在这里的应用确实很经典!从上面两段的分析看来Java的IO就是分成两部分从哪儿读(写)和怎么读(写)。想想看,一个数据源可能需要不同的读写方式、而不同的数据源都可能需要同一直读写方式。于是这里的组合方式是一种乘积。n个数据源×n中处理方式×(n-1)处理方式(有时候可能涉及多种处理方式的叠加)=n^3。想想看,要是一个一个写还不写死......
从IO体系的根看,InputStream(OutputStream)提供了最根本的read(write)方法。各子类提供各自实现。而得益于Decorator,各子类还可以把自己的处理方式加到read(write)的前后,形成一种处理方式的叠加。
JDK这部分的代码比较简单。看一下就大概知道Decorator怎么实现的了。
追根溯源
我们见到的IO最大的应用一个应该是数据的持久化,FileinputStream(FileOutputStream)已经实现了。而另一个则是网络传输。之前我就很想知道IO是怎么在网络上实现传输的?还有上面说的几个IO都不存在“阻塞”问题,那么IO的阻塞实在哪儿产生的呢?
找了半天对于找到了下面一句:
class SocketInputStream extends FileInputStream
再追下去就找到了一些native方法,这个就超出我能力之外了。不过可以猜想,底层的东西应该就是C的一些文件读写了。这个就不是Java的问题:)目前暂时这么认为,以后或许能发现一些新的东西。
根深蒂固的痛——国际化
编码问题不仅是Java头痛的问题,也是每一个用Java的人都头痛的问题。IO也不例外。InputStream(OutputStream)体系都是针对字节(byte)进行读写的。这样的问题是字节可能是没有意义的,只有几个字节联合转换才能得到有意义的东西。所以如果是字节还要再次进行编解码才能得到我们想要的东西。
为了避免这个麻烦,字符体系(Read、Write)产生了。他们可以对流进行编解码,这样可以直接得到用户想要的东西。两个体系大大体上都是对应的。再多的东西我也没有深入了解了:)
还有一个要提的是,什么时候用字符、什么时候用字节呢?TIJ里有交代:大部分时候用字符,字符不行的时候再考虑字节。呵呵,挺简练的。
日新月异
nio应该是老掉牙的东西了。不过对于我这个初始IO的人来说还有待进一步了解。目前知道两点:上面的IO体系的底层都已经用nio重写了,所以速度上应该差不多。这样看来,一般情况下用上面的IO是没问题的。再一个是,nio最大的优势在于他的“非阻塞IO”,不过这个东东是用在网络编程的。一般也用不上。
再新的东西就是前两天看到的一个blog,关于aio(异步IO)的大家有兴趣可以自己看看。
posted @
2006-11-25 21:26 KeepRunning 阅读(352) |
评论 (0) |
编辑 收藏
这是我以前的
Blog,现在也还在用。因为技术方面的东西没人关注,呵呵,所以还是把技术的东西放到技术的地方,或许更能找到共同的语音:)
顺便贴一篇,欢迎讨论~
重拾聚集(Collection)
大家先来看段代码吧:
List list = new ArrayList();
list.add("exception");
for (Iterator iter = list.iterator(); iter.hasNext();) {
String element = (String) iter.next();
list.remove(element);
}
这段代码运行时会抛出异常,呵呵,不知道大家发现问题没?
这段代码会抛出:java.util.ConcurrentModificationException。也就是所谓的“Fail Fast”。用了很久Java了,才发现这么一个异常,着实郁闷了一把......啥都不说了,拿起书来,重新来过。
为什么要Iterator?
下面是一个Collection的总体图:
![](http://static.flickr.com/100/296361872_988cc45936.jpg?v=0)
上面代码涉及了一个经典的涉及模式-Iterator模式。既然涉及了这个模式,就要问问为什么这里要用这个模式。查了一下书,有这么几句描述Iterator的:迭代逻辑没有改变,但是需要将一种聚集变换成另一种聚集。因为不同的聚集遍历接口不同,所以需要修改客户端代码。这几句话没错,而且我们用Iterator都有如文章开头示例的标准方式,然而仔细想来却还有点问题。
假设Iterator是为了实现遍历方便。可是,我们几个常用的聚集(如:ArrayList,LinkedList,Set等)都已经间接实现了Collection接口。而大家应该注意到Collection接口中有个size()方法。这样的结果是,对所有实现了Collection的聚集而言,我们都可以通过for (int i = 0; i < list.size(); i++)这样的形式实现遍历。而且,JDK1.5还可以通过foreach的形式大大简化代码量。所以,这样想来Iterator为了实现遍历的方便而引入,显然不成立。
接着找找。我们可以注意到,Set和List两个大类中一个很大的区别是:List提供get方法,而Set没有提供get方法。这样的结果是Set类只能往集合中放,却不能从集合中取(从一般的常识来说,这样做还是合理的)。呵呵,所以这里我只能猜想Iterator是不是是为了访问Set中元素而设计的了。
仅仅猜想,不知道大家有没有什么更好的想法?
讨论:
跟朋友讨论了一下,for(int i = 0; i < list.size(); i++)对应遍历LinkedList来说是致命的。对ArrayList是O(n),而LinkedList则是O(n^2),这样的效率是不能让人接受的。
如上所说,那么foreach的引入应该是在编译层次对两种不同数据结构进行了不同处理。而且成为了Iterator的替代。
不过这里又引出了另一个问题——
Iterator模式如何用在其他地方?作为一种设计模式Iterator应该有更广的使用价值吧~
Iterator的缺点
- 聚集中的元素有些是有顺序的,而有些是没有顺序可言的。使用过度Iterator会产生一种元素顺序的误解。
- Iterator给出的元素没有类型。这点很致命啊。而且也是JDK1.5一大优势。
- Iterator使得某些同步的类在使用了Iterator后,成为不同步(不能说不同步,只是Iterator无法并发)。如Vector。
这里没有贬低Iterator的意思,只是实在看不出他有啥好处......
集合
能力有限,感觉了解还是太少,也没太多好说的,只是有些区别应该注意一下。
- 继承List的几个类中,只有Vector是同步的。当然也包括Vector的子类Stack。
- 如果需要对不同步的List,Set等可以考虑使用Collections类中的方法。
posted @
2006-11-19 21:32 KeepRunning 阅读(294) |
评论 (0) |
编辑 收藏