写在前面


  • JAVA冷知识,今天和小伙伴分享的是
  • 通过内部类的方式实现JAVA的多继承
  • 一个DemoJDK源码中的具体场景
  • 部分内容参考
    • 《编写高质量代码(改善Java程序的151个建议)》
    • 《Effective Java》中文版第3版
  • 博文理解有误的地方小伙伴留言私信一起讨论

与亲近之人不要说气话,不要说反话,不要不说话。——烽火戏诸侯 《剑来》


众多周知,对于面向对象语言来讲,JAVA是不支持多继承的,只支持单继承,但是提供了接口来补偿。

在实际的项目中,接口更多的用于行为的委托,把类本身一些是共性但又是特定的行为委托给一个接口的具体实现,当然接口也可以用于属性的委托,对象结构型的设计模式大都采用接口的方式来实现对对象内部组成的注册和操作

如果实现java的多继承,其实很简单,关键是对于内部类的特征的掌握,内部类可以继承一个与外部类无关的类,保证了内部类天然独立性,根据这个特性从而实现一个类可以继承多个类的效果

下面我们看一个Demo,声明父母两个接口,实现父母两个类,看如何通过内部类来继承父母类,而不是通过,接口委托的方式,

一个Demo

父亲接口

package com.liruilong;/*** @Project_name: workspack* @Package: com.liruilong* @Description: 父亲接口* @Author: 1224965096@qq.com* @WeChat_Official_Accounts: 山河已无恙* @blog: https://liruilong.blog.csdn.net/* @Date: 2022/2/12  2:48*/
public interface Father {/*** @return: int* @Description 强壮的行为* @author LiRuilong* @date  2022/2/12  2:49**/int strong();
}

父亲实现类

package com.liruilong;/*** @Project_name: workspack* @Package: com.liruilong* @Description: 父亲类* @Author: 1224965096@qq.com* @WeChat_Official_Accounts: 山河已无恙* @blog: https://liruilong.blog.csdn.net/* @Date: 2022/2/12  2:51*/
public class FatherImpl implements Father {static  public   String height = "身体超高";/*** @return: int* @Description  强壮值* @author LiRuilong* @date  2022/2/12  2:51**/@Overridepublic int strong() {return 8;}
}

母亲接口

package com.liruilong;/*** @Project_name: workspack* @Package: com.liruilong* @Description: 母亲接口* @Author: 1224965096@qq.com* @WeChat_Official_Accounts: 山河已无恙* @blog: https://liruilong.blog.csdn.net/* @Date: 2022/2/12  2:50*/
public interface Mother {/*** @return: int* @Description 温柔的行为* @author LiRuilong* @date  2022/2/12  2:50**/int Kind();
}

母亲实现类

package com.liruilong;/*** @Project_name: workspack* @Package: com.liruilong* @Description: 母亲类* @Author: 1224965096@qq.com* @WeChat_Official_Accounts: 山河已无恙* @blog: https://liruilong.blog.csdn.net/* @Date: 2022/2/12  2:51*/
public class MotherImpl implements Mother{static   public  String pretty = "脸蛋特别漂亮";/*** @return: int* @Description 温柔值* @author LiRuilong* @date  2022/2/12  2:51**/@Overridepublic int Kind() {return 8;}
}

OK,准备工作做好了, 看我们如何实现。

package com.liruilong;import java.util.logging.Logger;/*** @Project_name: workspack* @Package: com.liruilong* @Description: 孩子类* @Author: 1224965096@qq.com* @WeChat_Official_Accounts: 山河已无恙* @blog: https://liruilong.blog.csdn.net/* @Date: 2022/2/12  13:16*/
public class Son extends FatherImpl implements Mother {static Logger logger = Logger.getAnonymousLogger();MotherSpecial motherSpecial = new MotherSpecial();@Overridepublic int strong() {return super.strong() + 1;}@Overridepublic int Kind() {return motherSpecial.Kind();}@Overridepublic String toString() {return "Son{" +"height=" + height +"," +"pretty=" + MotherSpecial.pretty +'}';}public class MotherSpecial extends MotherImpl {@Overridepublic int Kind() {return super.Kind() - 1;}}public static void main(String[] args) {Son son = new Son();logger.info(son.toString());logger.info(son.strong()+"");logger.info(son.Kind()+"");}}

我们用内部类继承一个外部类无关的类,实现了Son类的多继承

Bad level value for property: .level
Bad level value for property: java.util.logging.ConsoleHandler.level
Can''t set level for java.util.logging.ConsoleHandler
二月 12, 2022 2:02:06 下午 com.liruilong.Son main
信息: Son{height=身体超高,pretty=脸蛋特别漂亮}
二月 12, 2022 2:02:06 下午 com.liruilong.Son main
信息: 9
二月 12, 2022 2:02:06 下午 com.liruilong.Son main
信息: 7Process finished with exit code 0

这里只是讨论这样的写法,我个人认为,这种方法有些鸡肋。这种方式实现的多继承,完全可以通组合的方式来实现,我们简单分析一下优缺点

优缺点分析

优点:

通过内部类的方式,把继承关系控制在类的内部,理论上比通过组合的方式更加安全,代码可读性要好一点。

更符合设计原则中的迪米特法则,又称最少知道原则(Demeter Principle),一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。

缺点:

首先通过继承的方式实现,打破了类的封装性,子类依赖于其超类中特定功能的实现细节。 超类的实现有可能会随着发行版本的不同而有所变化,如果真的发生了变化,即使子类的代码完全没有改变,但是子类可能会遭到破坏因而,子类必须要跟着其超类的更新而演变,除非超类是专门为了扩展而设计的,并且具有很好的文挡说明

其次,通过这样的方式实现的,不符合常态思想,尤其内部类同名的情况,容易被忽略某些特性(见JDK源码)。而且不满足合成复用原则(Composite Reuse Principle),尽量使用合成/聚合的方式,而不是使用继承。

JDK源码中的运用

关于通过内部类来实现java多继承JDK场景,我们简单分析一下

asList

List<Integer> integers = Arrays.asList(1, 2, 3);

这个代码小伙伴们一定不陌生,这里通过Arrays工具类来生成一个List,但是这里的List并不是真正的ArrayList,而是在Arrays工具类内部定义的一个继承了AbstractList的静态内部类ArrayList,这里java通过内部类的方式巧妙的实现了。

  .......@SafeVarargs@SuppressWarnings("varargs")public static <T> List<T> asList(T... a) {return new ArrayList<>(a);}/*** @serial include*/private static class ArrayList<E> extends AbstractList<E>implements RandomAccess, java.io.Serializable{private static final long serialVersionUID = -2764017481108945198L;private final E[] a;ArrayList(E[] array) {a = Objects.requireNonNull(array);}.................

但是这里同样需要注意的是通过内部类实现多继承要考虑其类的特殊性

这样生成的List调用add方法会抛不支持的操作的异常,基于ArraysArrayList是一个静态私有内部类,除了Arrays能访问以外,其他类都不能访问,正常的ArrayList中add方法是ArrayList父类提供,Arrays的内部类ArrayList没有覆写add方法。

下面源码为ArrayList静态内部类实现的个方法。

    /*** @serial include*/private static class ArrayList<E> extends AbstractList<E>implements RandomAccess, java.io.Serializable{private static final long serialVersionUID = -2764017481108945198L;private final E[] a;ArrayList(E[] array) {a = Objects.requireNonNull(array);}@Overridepublic int size() {return a.length;}@Overridepublic Object[] toArray() {return a.clone();}@Override@SuppressWarnings("unchecked")public <T> T[] toArray(T[] a) {int size = size();if (a.length < size)return Arrays.copyOf(this.a, size,(Class<? extends T[]>) a.getClass());System.arraycopy(this.a, 0, a, 0, size);if (a.length > size)a[size] = null;return a;}@Overridepublic E get(int index) {return a[index];}@Overridepublic E set(int index, E element) {E oldValue = a[index];a[index] = element;return oldValue;}@Overridepublic int indexOf(Object o) {E[] a = this.a;if (o == null) {for (int i = 0; i < a.length; i++)if (a[i] == null)return i;} else {for (int i = 0; i < a.length; i++)if (o.equals(a[i]))return i;}return -1;}@Overridepublic boolean contains(Object o) {return indexOf(o) != -1;}@Overridepublic Spliterator<E> spliterator() {return Spliterators.spliterator(a, Spliterator.ORDERED);}@Overridepublic void forEach(Consumer<? super E> action) {Objects.requireNonNull(action);for (E e : a) {action.accept(e);}}@Overridepublic void replaceAll(UnaryOperator<E> operator) {Objects.requireNonNull(operator);E[] a = this.a;for (int i = 0; i < a.length; i++) {a[i] = operator.apply(a[i]);}}@Overridepublic void sort(Comparator<? super E> c) {Arrays.sort(a, c);}}

即没有实现addremove方法,所以asList返回的为一个长度不可变的列表,数组为多长转换为列表为多长,即不在保持列表动态变长的特性

subList

嗯,不多讲,直接上代码

        ArrayList arrayList = new ArrayList();LinkedList linkedList = new LinkedList();Vector vector = new Vector();linkedList.subList(2,3);arrayList.subList(2,3);vector.subList(2,3);

List提供一个subList方法,与StringsubString有点类似,这里的List通过subList生成子list方式也是通过内部类继承方式的多继承实现的。

当然这里,具体需要分析,ArrayList其他List的实现的方式略有不同

ArrayList是自己定义的内部类SubList继承AbstractList实现的

public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{.......public List<E> subList(int fromIndex, int toIndex) {subListRangeCheck(fromIndex, toIndex, size);return new SubList(this, 0, fromIndex, toIndex);}
.....private class SubList extends AbstractList<E> implements RandomAccess {private final AbstractList<E> parent;private final int parentOffset;private final int offset;int size;.........

LinkedListsubList方法是由AbstractList实现的,它会根据是不是随机存储提供不同的实现方法subList返回的类也是AbstractList的子类SubList

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {........public List<E> subList(int fromIndex, int toIndex) {return (this instanceof RandomAccess ?new RandomAccessSubList<>(this, fromIndex, toIndex) :new SubList<>(this, fromIndex, toIndex));}class SubList<E> extends AbstractList<E> {...}class RandomAccessSubList<E> extends SubList<E> implements RandomAccess{.......}........
}

这里需要注意的是,不管是ArrayList还是LinkedList等其他List,通过SubList内部类生成的List,其所有的方法(get,add,set,remove等)都是在原始列表上操作的,它自身并没有生成一个数组或是链表,也就是子列表只是原列表的一个视图(View),所有的修改都反映在原列表上。

[JAVA冷知识]JAVA居然支持多继承?让我们用内部类去实现吧相关推荐

  1. Java基础知识学习06-封装、继承

    封装 概念:将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法实现对隐藏信息的操作和访问. 特点:提高代码的复用性: 隐藏类的实例细节,方便修改和实现: 提高代码的安全性. ...

  2. [JAVA冷知识]动态加载不适合数组类?那如何动态加载一个数组类?

    写在前面 今天和小伙伴分享一些java小知识点,主要围绕下面几点: 既然数组是一个类, 那么编译后类名是什么?类路径呢? 为什么说动态加载不适合数组? 那应该如何动态加载一个数组? 部分内容参考 &l ...

  3. java基础知识(java基础知识刷题软件)

    自学Java 怎么入门 在学习Java前,你有必要思考一下: 你对学习Java是否有兴趣2.您是否能静下心学习Java 大家都知道兴趣很重要,如果您对Java没有兴趣,相信你没法能静下心学习Java ...

  4. Java基础看jvm,JAVA基础知识|java虚拟机(JVM)

    一.JVM简介 java语言是跨平台的,兼容各种操作系统.实现跨平台的基石就是虚拟机(JVM),虚拟机不是跨平台的,所以不同的操作系统需要安装不同的jdk版本(jre=jvm+类库:jdk=jre+开 ...

  5. Java基础知识——Java语言基础

    ---------------------- ASP.Net+Android+IO开发S. .Net培训.期待与您交流! ---------------------- 2.Java语言基础 Java语 ...

  6. Java基础知识回顾之三 ----- 封装、继承和多态

    前言 在上一篇中回顾了java的修饰符和String类,这篇就来回顾下Java的三大特性:封装.继承.多态. 封装 什么是封装 在面向对象程式设计方法中,封装是指一种将抽象性函式接口的实现细节部份包装 ...

  7. Java冷知识之——魔数(Magic Number)

    一. 编程中的魔数 在阿里巴巴Java开发手册编程规约第二节常量定义的第一条中写道: 魔法值(即魔数)指的是未经预先定义的常量,而反例中的 "Id#taobao_" 则叫做魔字符串 ...

  8. Java冷知识(三)编译器的花招之synthetic修饰符

    首先我们应该达成一个共识:很多的Java语法糖都是编译器赋予的,而JVM是一个与Java语言解耦的平台.有了这个共识,再来看今天的主角:synthetic修饰符. 我们在阅读JDK反射包源码时,会遇到 ...

  9. java冷知识:代码行数多少才算大项目?

    注:以下摘自:<软件工程概论> 郑人杰.殷人民编 java项目的大小衡量标准(以代码行作为计量标准的): 微型:只是一个人,甚至是半日工作在几天内完成的软件: 小型:一个人半年内完成的 2 ...

最新文章

  1. Eclipse Theme
  2. JDK8安装及设置环境变量
  3. 关于Docker目录挂载的总结
  4. tty,串口,控制台与驱动程序
  5. MySQL数据库是非关系_MySQL(数据库)基础知识、关系型数据库yu非关系型数据库、连接认证...
  6. (计算机组成原理)第二章数据的表示和运算-第二节6:定点数除法运算(原码/补码一位除法)
  7. [转]最常用的15大Eclipse开发快捷键技巧
  8. Vmware VirtualCenter Server服务无法自动启动
  9. Mapstruct使用介绍
  10. Unity遍历文件夹及其文件
  11. 正大国际琪貨:为什么资深交易者更倾向于裸K?
  12. 随机过程(三):马尔可夫过程、马尔可夫链、转移概率、转移概率矩阵、平稳性、齐次性、时齐性、一步转移概率、一步转移概率矩阵、C-K方程、n步转移概率、n步转移概率矩阵、遍历性、极限分布、平稳分布
  13. erb自动生成html页面一例
  14. 中国城市资本流动问题探索
  15. numpy.insert用法以及内插插0的方法
  16. elasticsearch 单节点状态yellow解决方法
  17. mapbox制作动态地图——storytelling(附html代码)
  18. 攻防世界-baby_web详解
  19. karaf常用命令纪录
  20. 纯文科生学计算机编程难度大不大

热门文章

  1. 打开用友提示运行错误-2147
  2. [照片调色]调出唯美亮丽色调PP的简单方法
  3. Jeff Dean的神话......
  4. 15-多对多做左连接查询(查询老师,并且把关联的学生也查出来)
  5. Sklearn多种算法实现人脸补全
  6. php 生成 sitemap,PHP生成SiteMap文件代码的方法
  7. 如何让培训更加生动化
  8. java 风速 转 风级
  9. Windows 视频直播功能实现流程
  10. 解决Komodo Edit在Windows10 平台上无法运行的问题