从基层容器类看万变不离其宗的JAVA继承体系
以容器类为例子,可以观一叶而知秋,看看以前的前辈们是如何处理各种面向对象思想下的继承体系的。
读的源代码越多,就越要总结这个继承关系否则读的多也忘得快。
首先摆上一张图片:

看到这张图很多人就慌了,难道这些东西我都要全部学习?其实是也不是,其中的很多东西都很有学习的必要,但是学习的过程绝对不是一行一行背诵,每一个类有哪些方法。而怎么从大体上掌握这个继承体系呢?
以最基本的ArrayList<E>为例子。(JDK 1.8环境下)
从ArrayList<E>开始往上看继承体系:
ArrayList(C)--->AbstractList(A)--->AbstractCollection(A)--->Collection(I)--->Iterable(I)
|--->List(I)--->Collection(I)
首先这个继承体系有两条线,我们从上往下看
Iterable,也就是迭代器,是个接口。任何类如果实现了这个接口,就必须实现一个函数,这个函数返回一个迭代器对象。
从某种意义上来讲,这个接口不属于这个继承体系,因为这个接口的主要目的是让实现它的类带有一种功能:通过迭代器访问。因为Collection继承了这个接口,可以想到的是肯定每一个容器都能通过迭代器访问,那么为什么要这么设计呢?
一开始接触的容器比较简单,能通过某种方式遍历,比如通过一个Index来遍历。可是对于一些容器,没有明显的某个数值(或者索引或者下标)可以遍历。
这个问题再进一步就是这样:容器的底层实现各不相同,为了简化遍历这个操作,就有了迭代器,实现屏蔽其底层数据结构的遍历。你用别人写的容器的时候不需要知道它怎么实现的,就只需要拿到这个迭代器,然后遍历就可以了。容器的提供者(编写者)负责这个容器能拿出一个正确的迭代器。(通过实现Iterable接口)
看到这里就能明白,迭代器接口不是单单针对容器,假如你写一个让别人能按照你想法遍历的类,都可以用迭代器。
接下来,我们看看Collection类。这个类是一个接口,里面写了很多方法。除去继承自Object类的方法,和容器有关的主要有:add(),remove(),contains(),isEmpty(),iterator(),size(),toArray()。这里没有写完,也不需要写完。主要就是要明白,一般顶层的接口会有一个极其言简意赅的名字(Collection,List,Set,Executor等等),然后在里面提供最最最基本的操作。比如像这些操作,我想只要是个容器就应该提供吧。
接下来是一个中间层:AbstractCollection。这个抽象类是拿来干嘛的呢?其实一句话表述,它能尽可能实现那些可以实现的操作。这就有点不明白了,你说你上面是个接口,可以说啥都没有,你既然不知道底层的数据结构(使用数组实现的?还是链表?还是???),能在这个层面上实现什么呢?
其实不然,虽然现阶段,抽象类只能看到接口,完全不知道具体怎么实现,可是它知道一定会实现。
具体来说吧,这是AbstractCollection里面的一部分源代码:
public boolean contains(Object o) {
Iterator<E> it = iterator();
if (o==null) {
while (it.hasNext())
if (it.next()==null)
return true;
} else {
while (it.hasNext())
if (o.equals(it.next()))
return true;
}
return false;
}
它虽然不知道你到底怎么实现了iterator(),但是它知道你一定会实现,因为是抽象方法。它在这个层面就直接拿来用了。而通过一个Iterator变量一个容器的代码,谁写基本都是这样。
总结来说,就是位置在继承体系中间的抽象类(如AbstractCollection)它主要作用是封装那些这个层面可以基本实现的差不多的代码。
再然后是AbstractList。这个抽象类类继承了AbstractCollection,同时实现了List接口。
既然前面已经通过抽象类封装了基本方法了,那么这个抽象类又是拿来干嘛的呢?很简单,之前的都是从一个Collection型的容器,功能特别单一,从这里开始就要进行分流了,很明显这个抽象类和它再往下的继承体系都是往List方向发展。
这里的List是个接口,继承自Collection接口,最主要是多了如下几个方法:
get(index),indexOf(Object),listIterator(),set(index,Object);
这说明一个什么?也就是说如果Collection是最基本的容器的话,List就是容器之中的线性表。可以类比数组。因为它的线性表特性,它的数据底层是通过首地址和偏移量的形式储存的,在这个层面上可以认为它是通过一个index(下标)对于上容器里面的元素的。所以对这个借口对应的容器来说,就不止是简单的添加删除了,它还可以做到在某个特定位置添加,修改,或者通过下标取得某个位置的值。
这里可以用简单的话来阐述我理解的这个地方的继承关系以及为什么要这么设计。
List继承自Collection说明它是一种容器,不过它不只单单是容器,它还有线性表性质。也就是说它跟高级了,从容器里面分离出来了一些。
而AbstractList为什么要继承List呢?它是在AbstractCollection基础上继承的List。也就是说,它是一个容器,可是它通过继承List和别的容器区分开来(如AbstractSet)。所以从这里看的话,List在继承体系中至少有这两个作用,1,分化继承线路,2,以后拿来给容器向上转型。
同时AbstractList还有一段有趣的代码:
private class Itr implements Iterator<E> {
int cursor = 0;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
这段代码是AbstractList里面的迭代器的代码的节选。问题的核心是:get()方法根本没有:
abstract public E get(int index);
这还是利用了上面提到的思想:在这个层面还是完全不知道底层的真正数据结构和基本操作的真正实现方式。那么你的get(),set()函数肯定是写不出来的。但是在这个层面它为了尽可能地实现一些大同小异的代码,比如这里的Itr的实现,它既然认定这个抽象方法一定会被继承者实现,它就直接拿来用了。直接利用没有实现的get()方法来实现迭代器。这个过程真是和踢皮球一样:上层的AbstractCollection认为下层会实现迭代器,于是直接使用迭代器写出了Contains()方法。这一层认为下一层应该实现get()方法,于是它就直接用get()方法实现了迭代器。最后的结果是好的,最后一层理论上只需要关注自己切切实实的get(),set()等等方法。
AbstractList再往下就是Arraylist了。这就不用说了,底层的最终实现。封装的工具类,一般来说不需要再继承它只需要使用它。它不再是一个抽象类,里面很多方法得到了实现。应为它最终添加了底层的数据结构实现。所以对它来说一切都是可以明白的,到底那些方法怎么实现。
这都只是Arraylist这条路的一路下来的东西,很多别的继承结构都是基于这个体系的。
从基层容器类看万变不离其宗的JAVA继承体系的更多相关文章
- 关于Java继承体系中this的表示关系
Java的继承体系中,因为有重写的概念,所以说this在子父类之间的调用到底是谁的方法,或者成员属性,的问题是一个值得思考的问题; 先说结论:如果在测试类中调用的是子父类同名的成员属性,这个this. ...
- Java继承体系中this的表示关系
在继承关系下,父类中的this关键字并不总是表示父类中的变量和方法.this关键字的四种用法如前文所述,列举如下. 1) this(paras…); 访问其他的构造方法 2) this.xxx; 访问 ...
- RPC 核心,万变不离其宗
微信搜 「yes的练级攻略」干货满满,不然来掐我,回复[123]一份20W字的算法刷题笔记等你来领. 个人文章汇总:https://github.com/yessimida/yes 欢迎 star ! ...
- 万变不离其宗之UART要点总结
[导读] 单片机开发串口是应用最为广泛的通信接口,也是最为简单的通信接口之一,但是其中的一些要点你是否明了呢?来看看本人对串口的一些总结,当然这个总结并不能面面俱到,只是将个人认为具有共性以及相对比较 ...
- 深入super,看Python如何解决钻石继承难题 【转】
原文地址 http://www.cnblogs.com/testview/p/4651198.html 1. Python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通 ...
- java继承内部类问题(java编程思想笔记)
普通内部类默认持有指向所属外部类的引用.如果新定义一个类来继承内部类,那“秘密”的引用该如何初始化? java提供了特殊的语法: class Egg2 { public class Yolk{ pub ...
- Java 继承详解
什么是继承? 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可. 多个类可以称为子类,单独这个类称为父类.超类或者基类. 子类可以直接 ...
- 深入super,看Python如何解决钻石继承难题
1. Python的继承以及调用父类成员 python子类调用父类成员有2种方法,分别是普通方法和super方法 假设Base是基类 class Base(object): def __init_ ...
- JAVA 继承中的this和super
学习java时看了不少尚学堂马士兵的视频,还是挺喜欢马士兵的讲课步骤的,二话不说,先做实例,看到的结果才是最实际的,理论神马的全是浮云.只有在实际操作过程中体会理论,在实际操作过程中升华理论才是最关键 ...
随机推荐
- 初学HTML 常见的标签(三) 插入类标签
第三篇博客, 这次说的是插入链接类标签, 我们平常在网页中经常能看到蓝色的链接类标签, 或者是一张图片, 一个电邮, 这些都是插入链接类的标签起的作用. <a></a>链接标签 ...
- swift实现饭否应用客户端源码
swift 版 iOS 饭否客户端 源码下载:http://code.662p.com/view/13318.html 饭否是中国大陆地区第一家提供微博服务的网站,被称为中国版Twitter.用户可通 ...
- [Erlang 0107] Erlang实现文本截断
抽时间处理一下之前积压的一些笔记.前段时间有网友 @稻草人 问字符串截断的问题"各位大侠 erlang截取字符串一般用哪个函数啊",有人支招用string:substr/3, ...
- Java导入的项目乱码怎么解决?(Ⅰ)
1.项目右键 打开 >> Properties >> Resource >> Text file encoding >> Other 如 ...
- MySQL高可用架构之MHA
简介: MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司youshimaton(现就职于Facebook公司)开发,是 ...
- 从零自学Hadoop(01):认识Hadoop
本文版权归mephisto和博客园共有,欢迎转载,但须保留此段声明,并给出原文链接,谢谢合作. 文章是哥(mephisto)写的,SourceLink 阅读目录 序 Hadoop 项目起源 优点 核心 ...
- 笔记整理之 Bulk Insert
之前2篇日志整理了BCP大致的用法,这次整理一下它的兄弟 Bulk Insert 的写法以及和bcp那边的结合的用法. 首先,Bulk Insert 语句要在连接了Sql Server 服务器之后才执 ...
- allocation size overflow
var cityID="1"; var areaHtml=""; var storeHtml=""; //区域异步 function Get ...
- 利用varnish做Discuz论坛的缓存服务器
实验背景:公司有一台BBS服务器,用的是LNMP的架构搭建的.正好手头有一台空闲的虚拟机,于是想着给BBS前端加一台缓存服务器.于是选定了varnish,搜了很多教程,跌跌撞撞的完成了配置.这其中很多 ...
- [原]经典bootstrap模态框使用文章
1,Bootstrap 模态对话框和简单使用 <div id="myModal" class="modal hide fade"> <div ...