Java集合为什么设计为:实现类继承了抽象类,同时实现抽象类实现的接口

更好阅读体验:Java集合为什么设计为:实现类继承了抽象类,同时实现抽象类实现的接口

问题

Java集合源码为什么设计为:「实现类继承了抽象类,同时实现抽象类实现的接口?」

看着List 集合的UML图来分析

如图:接口+抽象类都是成对出现,Collection 和 AbstractCollection;List 和 AbstractList。ArrayList 继承了AbstractList,同时实现了List 接口。

img

再看下其他集合的UML 图,看看是不是也是这样设计的

img

img

img

这样设计的意义

有的人说接口只能使用抽象方法,为了实现公共的方法抽出来用抽象类来实现,提高代码复用性,提高代码质量。

其实不是,第一句就错了,接口同样是可以实现方法,用的关键字是 default。如果真的是为了公共的实现方法抽出来用抽象类来实现,那还不如直接在接口上实现不更好,而且具体的实现类(如:ArrayList)还不用继承抽象类,毕竟继承还是单一的。

List 接口继承了Collection 接口绝大部分方法,并新定义了List 特有的抽象方法,而这些特有的抽象方法都交给了AbstractList 来实现。

抽两个方法来看看

    // AbstractList
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }
​
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }

在AbstractList中,这两个方法只是抛了个“不支持的操作异常”,并没有具体的内容。要明白抽象类是多种类的再一次抽象,也就是说具体的实现方法还是要针对不同的类来编写,所以还要看看ArrayList 和 LinkedList 对add(int index, E element)的具体实现

 // ArrayList 
 public void add(int index, E element) {
        rangeCheckForAdd(index);
​
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        elementData[index] = element;
        size++;
    }
​
 // LinkedList 
 public void add(int index, E element) {
        checkPositionIndex(index);
​
        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }

可见,这两个方法的实现完全不同(底层数据结构不同),到此应该明白一点了:「List 接口定义个List 特有的规范,AbstractList 对这个规范做了通用实现,而具体实现还是要在具体类里面落实」。但并不是只是为了高复用性、减少代码重复度。

比如:public int indexOf(Object o); 虽然抽象类实现了,但具体类还是重写了这方法,显然不是为了解决代码复用的问题,而是提供了通用实现,具体的还有看具体类使用了什么类型的数据结构,然后在具体类中重写方法定制化的实现。

    // AbstractList
   public int indexOf(Object o) {
        ListIterator<E> it = listIterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return it.previousIndex();
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return it.previousIndex();
        }
        return -1;
    }
    // ArrayList
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }
    // LinkedList
    public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

意义

1.接口实现的解耦:接口新增或删除方法时,只需在抽象类中做个方法初始实现(对于删除方法可以不做处理),即可避免一个接口变动导致所有实现类都需要更新代码的问题。

2.多态特性的运用,向上转型:ArrayList 、Vector、LinkedList 都可以实现对数据的存储,都可以用List 作为引用(面向接口编程)。

「总结」

  • 提高接口的灵活性:接口只需要按需重写方法;

  • 提高接口的易扩展:接口变动不影响实现类,只需要更改相应的抽象类实现即可;

  • 抽象类和接口的互补:接口规定行为规范,抽象类补充属性和接口实现的解耦。

    个人理解,如有不对,还望纠正。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
接口抽象类的区别 抽象方法是必须实现的方法。就象动物都要呼吸。但是鱼用鳃呼吸,猪用肺呼吸。 动物要有呼吸方法。怎么呼吸就是子的事了。 现在有很多讨论和建议提倡用interface代替abstract,两者从理论上可以做一般性的混用,但是在实际应用中,他们还是有一定区别的。抽象类一般作为公共的父为子的扩展提供基础,这里的扩展包括了属性上和行为上的。而接口一般来说不考虑属性,只考虑方法,使得子可以自由的填补或者扩展接口所定义的方法,就像JAVA王子所说的事件中的适配器就是一个很好的应用。 用一个简单的例子,比如说一个教师,我们把它作为一个抽象类,有自己的属性,比如说年龄,教育程度,教师编号等等,而教师也是分很多种的,我们就可以继承教师而扩展特有的种属性,而普遍属性已经直接继承了下来。 而接口呢~还是拿教师做例子,教师的行为很多,除了和普通人相同的以外,还有职业相关的行为,比如改考卷,讲课等等,我们把这些行为定义成无body的方法,作为一个集合,它是一个interface。而教师张三李四的各自行为特点又有不同,那么他们就可以扩展自己的行为body。从这点意义上来说,interface偏重于行为。 总之,在许多情况下,接口确实可以代替抽象类,如果你不需要刻意表达属性上的继承的话。
简单性 Java看起来设计得很像C++,但是为了使语言小和容易熟悉,设计者们把C++语言中许多可用的特征去掉了,这些特征是一般程序员很少使用的。例如,Java不支持goto语句,代之以提供break和continue语句以及异常处理。Java还剔除了C++的操作符过载(overload)和多继承特征,并且不使用主文件,免去了预处理程序。因为Java没有结构,数组和串都是对象,所以不需要指针。Java能够自动处理对象的引用和间接引用,实现自动的无用单元收集,使用户不必为存储管理问题烦恼,能更多的时间和精力花在研发上。 面向对象 Java是一个面向对象的语言。对程序员来说,这意味着要注意其中的数据和操纵数据的方法(method),而不是严格地用过程来思考。在一个面向对象的系统中,(class)是数据和操作数据的方法的集合。数据和方法一起描述对象(object)的状态和行为。每一对象是其状态和行为的封装。是按一定体系和层次安排的,使得子可以从超继承行为。在这个层次体系中有一个根,它是具有一般行为的Java程序是用来组织的。 Java还包括一个的扩展集合,分别组成各种程序包(Package),用户可以在自己的程序中使用。例如,Java提供产生图形用户接口部件的java.awt包),这里awt是抽象窗口工具集(abstract windowing toolkit)的缩写,处理输入输出的java.io包)和支持网络功能的java.net包)。 分布性 Java设计成支持在网络上应用,它是分布式语言。Java既支持各种层次的网络连接,又以Socket支持可靠的流(stream)网络连接,所以用户可以产生分布式的客户机和服务器。 网络变成软件应用的分布运载工具。Java程序只要编写一次,就可到处运行。 编译和解释性 Java编译程序生成字节码(byte-code),而不是通常的机器码。Java字节码提供对体系结构中性的目标文件格式,代码设计成可有效地传送程序到多个平台。Java程序可以在任何实现Java解释程序和运行系统(run-time system)的系统上运行。 在一个解释性的环境中,程序开发的标准“链接”阶段大大消失了。如果说Java还有一个链接阶段,它只是把新装进环境的过程,它是增量式的、轻量级的过程。因此,Java支持快速原型和容易试验,它将导致快速程序开发。这是一个与传统的、耗时的“编译、链接和测试”形成鲜明对比的精巧的开发过程。 稳健性 Java原来是用作编写消费家用电子产品软件的语言,所以它是被设计成写高可靠和稳健软件的。Java消除了某些编程错误,使得用它写可靠软件相当容易。 Java是一个强型语言,它允许扩展编译时检查潜在型不匹配问题的功能。Java要求显式的方法声明,它不支持C风格的隐式声明。这些严格的要求保证编译程序能捕捉调用错误,这就导致更可靠的程序。 可靠性方面最重要的增强之一是Java的存储模型。Java不支持指针,它消除重写存储和讹误数据的可能性。似地,Java自动的“无用单元收集”预防存储漏泄和其它有关动态存储分配和解除分配的有害错误。Java解释程序也执行许多运行时的检查,诸如验证所有数组和串访问是否在界限之内。 异常处理是Java中使得程序更稳健的另一个特征。异常是某种似于错误的异常条件出现的信号。使用try/catch/finally语句,程序员可以找到出错的处理代码,这就简化了出错处理和恢复的任务。
实现一个基于内存的英文全文检索搜索引擎,需要完成以下功能: 功能 1:将指定目录下的一批.txt 格式的文本文件扫描并在内存里建立倒排索引,这里面包含必须的子功能包括: 读取文本文件的内容; 将内容切分成一个个的单词; 过滤掉其中一些不需要的单词,例如数字、停用词(the, is and 这样的单词)、过短或过长的单词(例如长度小于 3 或长度大于 20 的单词); 利用 Java集合在内存里建立过滤后剩下单词的倒排索引; 内存里建立好的索引对象可以序列化到文件,同时可以从文件里反序列化成内存里的索引对象; 可以在控制台输出索引的内容。 功能 2:基于构建好的索引,实现单个搜索关键词的全文检索,包含的子功能包括: 根据搜索关键词得到命中的结果集合; 可以计算每个命中的文档的得分,并根据文档得分对结果集排序; 在控制台显示命中的文档的详细信息,如文档的路径、文档内容、命中的关键词信息(如在文档里出现次数)、文档得分; 功能 3:基于构建好的索引,实现二个搜索关键词的全文检索。包含的子功能包括: 支持这二个关键词的与或查询。与关系必须返回同时包含这二个单词的文档集合,或关系返回包含这二个单词中的任何一个的文档集合; 可以计算每个命中的文档的得分,并根据文档得分对结果集排序; 在控制台显示命中的文档的详细信息,如文档的路径、文档内容、命中的关键词信息(如在文档里出现次数)、文档得分; 功能 4:基于构建好的索引,实现包含二个单词的短语检索,即这二个单词必须在作为短语文档里出现,它们的位置必须是相邻的。这个功能为进阶功能。 除了以上功能上的要求外,其他要求包括: 针对搜索引擎的倒排索引结构,已经定义好了创建索引和全文检索所需要的抽象类接口。学生必须继承这些预定义的抽象类和和实现预定义接口来完成实验的功能,不能修改抽象类接口里规定好的数据成员、抽象方法;也不能在预定义抽象类接口里添加自己新的数据成员和方法。但是实现自己的子接口实现则不作任何限定。 自己实现抽象类接口实现里的关键代码必须加上注释,其中每个、每个里的公有方法要加上 Javadoc 注释,并自动生成 Java API 文档作为实验报告附件提交。 使用统一的测试文档集合、统一的搜索测试案例对代码进行功能测试,构建好的索引和基于统一的搜索测试案例的检索结果最后输出到文本文件里作为实验报告附件提交。 本实验只需要基于控制台实现,实验报告里需要提供运行时控制台输出截屏。 关于搜索引擎的倒排索引结构、相关的抽象类接口定义、还有相关已经实现好的工具会在单独的 PPT 文档里详细说明。同时也为学生提供了预定义抽象类接口Java API 文档和 UML 模型图。
简单性 Java看起来设计得很像C++,但是为了使语言小和容易熟悉,设计者们把C++语言中许多可用的特征去掉了,这些特征是一般程序员很少使用的。例如,Java不支持goto语句,代之以提供break和continue语句以及异常处理。Java还剔除了C++的操作符过载(overload)和多继承特征,并且不使用主文件,免去了预处理程序。因为Java没有结构,数组和串都是对象,所以不需要指针。Java能够自动处理对象的引用和间接引用,实现自动的无用单元收集,使用户不必为存储管理问题烦恼,能更多的时间和精力花在研发上。 面向对象 Java是一个面向对象的语言。对程序员来说,这意味着要注意其中的数据和操纵数据的方法(method),而不是严格地用过程来思考。在一个面向对象的系统中,(class)是数据和操作数据的方法的集合。数据和方法一起描述对象(object)的状态和行为。每一对象是其状态和行为的封装。是按一定体系和层次安排的,使得子可以从超继承行为。在这个层次体系中有一个根,它是具有一般行为的Java程序是用来组织的。 Java还包括一个的扩展集合,分别组成各种程序包(Package),用户可以在自己的程序中使用。例如,Java提供产生图形用户接口部件的java.awt包),这里awt是抽象窗口工具集(abstract windowing toolkit)的缩写,处理输入输出的java.io包)和支持网络功能的java.net包)。 分布性 Java设计成支持在网络上应用,它是分布式语言。Java既支持各种层次的网络连接,又以Socket支持可靠的流(stream)网络连接,所以用户可以产生分布式的客户机和服务器。 网络变成软件应用的分布运载工具。Java程序只要编写一次,就可到处运行。 编译和解释性 Java编译程序生成字节码(byte-code),而不是通常的机器码。Java字节码提供对体系结构中性的目标文件格式,代码设计成可有效地传送程序到多个平台。Java程序可以在任何实现Java解释程序和运行系统(run-time system)的系统上运行。 在一个解释性的环境中,程序开发的标准“链接”阶段大大消失了。如果说Java还有一个链接阶段,它只是把新装进环境的过程,它是增量式的、轻量级的过程。因此,Java支持快速原型和容易试验,它将导致快速程序开发。这是一个与传统的、耗时的“编译、链接和测试”形成鲜明对比的精巧的开发过程。 稳健性 Java原来是用作编写消费家用电子产品软件的语言,所以它是被设计成写高可靠和稳健软件的。Java消除了某些编程错误,使得用它写可靠软件相当容易。 Java是一个强型语言,它允许扩展编译时检查潜在型不匹配问题的功能。Java要求显式的方法声明,它不支持C风格的隐式声明。这些严格的要求保证编译程序能捕捉调用错误,这就导致更可靠的程序。 可靠性方面最重要的增强之一是Java的存储模型。Java不支持指针,它消除重写存储和讹误数据的可能性。似地,Java自动的“无用单元收集”预防存储漏泄和其它有关动态存储分配和解除分配的有害错误。Java解释程序也执行许多运行时的检查,诸如验证所有数组和串访问是否在界限之内。 异常处理是Java中使得程序更稳健的另一个特征。异常是某种似于错误的异常条件出现的信号。使用try/catch/finally语句,程序员可以找到出错的处理代码,这就简化了出错处理和恢复的任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java全栈布道师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值