遇到认真的读者是作者的一种幸运,真的,上一篇接口推送后,有好几个读者留言说,“二哥,你有一处内容需要修正,应该是接口中不能有 private 和 protected 修饰的方法。”说实话,看到这样的留言,我内心是非常欣慰的,因为你投出去的一块石头在水面上激起了一串美丽的涟漪。

在 Java 中,一个类可以继承另外一个类或者实现多个接口,我想这一点,大部分的读者应该都知道了。还有一点,我不确定大家是否知道,就是一个接口也可以继承另外一个接口,就像下面这样:

public interface OneInterface extends Cloneable {
}

这样做有什么好处呢?我想有一部分读者应该已经猜出来了,就是实现了 OneInterface 接口的类,也可以使用 Object.clone() 方法了。

public class TestInterface implements OneInterface {
    public static void main(String[] args) throws CloneNotSupportedException {
        TestInterface c1 = new TestInterface();
        TestInterface c2 = (TestInterface) c1.clone();
    }
}

除此之外,我们还可以在 OneInterface 接口中定义其他一些抽象方法(比如说深拷贝),使该接口拥有 Cloneable 所不具有的功能。

public interface OneInterface extends Cloneable {
    void deepClone();
}

看到了吧?这就是继承的好处:子接口拥有了父接口的方法,使得子接口具有了父接口相同的行为;同时,子接口还可以在此基础上自由发挥,添加属于自己的行为

以上,把“接口”换成“类”,结论同样成立。让我们来定义一个普通的父类 Wanger:

public class Wanger {
    int age;
    String name;
    void write() {
        System.out.println("我写了本《基督山伯爵》");
    }
}

然后,我们再来定义一个子类 Wangxiaoer,使用关键字 extends 来继承父类 Wanger:

public class Wangxiaoer extends Wanger{
    @Override
    void write() {
        System.out.println("我写了本《茶花女》");
    }
}

我们可以将通用的方法和成员变量放在父类中,达到代码复用的目的;然后将特殊的方法和成员变量放在子类中,除此之外,子类还可以覆盖父类的方法(比如write() 方法)。这样,子类也就焕发出了新的生命力。

Java 只支持单一继承,这一点,我在上一篇接口的文章中已经提到过了。如果一个类在定义的时候没有使用 extends 关键字,那么它隐式地继承了 java.lang.Object 类——在我看来,这恐怕就是 Java 号称万物皆对象的真正原因了。

那究竟子类继承了父类的什么呢?

子类可以继承父类的非 private 成员变量,为了验证这一点,我们来看下面这个示例。

public class Wanger {
    String defaultName;
    private String privateName;
    public String publicName;
    protected String protectedName;
}

父类 Wanger 定义了四种类型的成员变量,缺省的 defaultName、私有的 privateName、共有的 publicName、受保护的 protectedName。

在子类 Wangxiaoer 中定义一个测试方法 testVariable()


可以确认,除了私有的 privateName,其他三种类型的成员变量都可以继承到。

同理,子类可以继承父类的非 private 方法,为了验证这一点,我们来看下面这个示例。

public class Wanger {
    void write() {
    }

    private void privateWrite() {
    }

    public void publicWrite() {
    }

    protected void protectedWrite() {
    }
}

父类 Wanger 定义了四种类型的方法,缺省的 write、私有的 privateWrite()、共有的 publicWrite()、受保护的 protectedWrite()。

在子类 Wangxiaoer 中定义一个 main 方法,并使用 new 关键字新建一个子类对象:


可以确认,除了私有的 privateWrite(),其他三种类型的方法都可以继承到。

不过,子类无法继承父类的构造方法。如果父类的构造方法是带有参数的,代码如下所示:

public class Wanger {
    int age;
    String name;

    public Wanger(int age, String name) {
        this.age = age;
        this.name = name;
    }
}

则必须在子类的构造器中显式地通过 super 关键字进行调用,否则编译器将提示以下错误:


修复后的代码如下所示:

public class Wangxiaoer extends Wanger{
    public Wangxiaoer(int age, String name) {
        super(age, name);
    }
}

is-a 是继承的一个明显特征,就是说子类的对象引用类型可以是一个父类类型。

public class Wangxiaoer extends Wanger{
    public static void main(String[] args) {
        Wanger wangxiaoer = new Wangxiaoer();
    }
}

同理,子接口的实现类的对象引用类型也可以是一个父接口类型。

public interface OneInterface extends Cloneable {
}
public class TestInterface implements OneInterface {
    public static void main(String[] args) {
        Cloneable c1 = new TestInterface();
    }
}

尽管一个类只能继承一个类,但一个类却可以实现多个接口,这一点,我在上一篇文章也提到过了。另外,还有一点我也提到了,就是 Java 8 之后,接口中可以定义 default 方法,这很方便,但也带来了新的问题:

如果一个类实现了多个接口,而这些接口中定义了相同签名的 default 方法,那么这个类就要重写该方法,否则编译无法通过。

FlyInterface 是一个会飞的接口,里面有一个签名为 sleep() 的默认方法:

public interface FlyInterface {
    void fly();
    default void sleep() {
        System.out.println("睡着飞");
    }
}

RunInterface 是一个会跑的接口,里面也有一个签名为 sleep() 的默认方法:

public interface RunInterface {
    void run();
    default void sleep() {
        System.out.println("睡着跑");
    }
}

Pig 类实现了 FlyInterface 和 RunInterface 两个接口,但这时候编译出错了。


原本,default 方法就是为实现该接口而不覆盖该方法的类提供默认实现的,现在,相同方法签名的 sleep() 方法把编译器搞懵逼了,只能重写了。

public class Pig implements FlyInterface, RunInterface {

    @Override
    public void fly() {
        System.out.println("会飞的猪");
    }

    @Override
    public void sleep() {
        System.out.println("只能重写了");
    }

    @Override
    public void run() {
        System.out.println("会跑的猪");
    }
}

类虽然不能继承多个类,但接口却可以继承多个接口,这一点,我不知道有没有触及到一些读者的知识盲区。

public interface WalkInterface extends FlyInterface,RunInterface{
    void walk();
}

学到了吧?学到就是赚到。

我是沉默王二,一枚有趣的程序员。如果觉得文章对你有点帮助,请微信搜索「 沉默王二 」第一时间阅读,回复【666】更有我为你精心准备的 500G 高清教学视频(已分门别类)。

本文 GitHub 已经收录,有大厂面试完整考点,欢迎 Star。

原创不易,莫要白票,请你为本文点个赞吧,这将是我写作更多优质文章的最强动力。

看了这篇,我确定你已经彻底搞懂Java的继承了的更多相关文章

  1. [转帖]看完这篇文章你还敢说你懂JVM吗?

    看完这篇文章你还敢说你懂JVM吗? 在一些物理内存为8g的服务器上,主要运行一个Java服务,系统内存分配如下:Java服务的JVM堆大小设置为6g,一个监控进程占用大约 600m,Linux自身使用 ...

  2. 一篇搞懂Java的基本数据类型

    byte 基本类型:byte 包装类:java.lang.Byte 大小:8bit 默认值:0 取值范围:-128~127 Byte.MIN_VALUE Byte.MAX_VALUE 二进制补码表示 ...

  3. APP的缓存文件到底应该存在哪?看完这篇文章你应该就自己清楚了

    APP的缓存文件到底应该存在哪?看完这篇文章你应该就自己清楚了 彻底理解android中的内部存储与外部存储 存储在内部还是外部 所有的Android设备均有两个文件存储区域:"intern ...

  4. 关于 Docker 镜像的操作,看完这篇就够啦 !(下)

    紧接着上篇<关于 Docker 镜像的操作,看完这篇就够啦 !(上)>,奉上下篇 !!! 镜像作为 Docker 三大核心概念中最重要的一个关键词,它有很多操作,是您想学习容器技术不得不掌 ...

  5. 【最短路径Floyd算法详解推导过程】看完这篇,你还能不懂Floyd算法?还不会?

    简介 Floyd-Warshall算法(Floyd-Warshall algorithm),是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似.该算法名称以 ...

  6. Visual Studio Code(VS code)你们都在用吗?或许你们需要看一下这篇博文

    写在前面 在前端开发中,有一个非常好用的工具,Visual Studio Code,简称VS code. 都不用我安利VS code,大家就会乖乖的去用,无数个大言不惭的攻城狮,都被VS code比德 ...

  7. 你们都在用IntelliJ IDEA吗?或许你们需要看一下这篇博文

    写在前面 以前一直用的elipce,如今入坑IntelliJ IDEA,没想到啊.深深的爱上了它,强大到无所不能: "工欲善其事必先利其器",IntelliJ IDEA作为一个非常 ...

  8. 当初要是看了这篇,React高阶组件早会了

    当初要是看了这篇,React高阶组件早会了. 概况: 什么是高阶组件? 高阶部件是一种用于复用组件逻辑的高级技术,它并不是 React API的一部分,而是从React 演化而来的一种模式. 具体地说 ...

  9. JVM内存模型你只要看这一篇就够了

    JVM内存模型你只要看这一篇就够了 我是一只孤傲的鱼鹰 让我们不厌其烦的从内存模型开始说起:作为一般人需要了解到的,JVM的内存区域可以被分为:线程栈,堆,静态方法区(实际上还有更多功能的区域,并且这 ...

随机推荐

  1. muduo网络库源码学习————无界队列和有界队列

    muduo库里实现了两个队列模板类:无界队列为BlockingQueue.h,有界队列为BoundedBlockingQueue.h,两个测试程序实现了生产者和消费者模型.(这里以无界队列为例,有界队 ...

  2. 关于C primer plus 的学习计划(暂停)

    最近想学数据结构,但是C的基础不够好,想借着C primer plus补一下基础.怎料第一章看的还挺快,到第二章看了二十多天.现在改改阅读方式:每日在这里添加进度,然后精看例题习题和章总结,其它简略看 ...

  3. C. K-Complete Word(小小的并查集啦~)

    永久打开的传送门 \(\color{Pink}{-------------分割-------------}\) \(n最大有2e5,那么暴力一定不行,找规律\) \(我们发现第i位的字符一定和第i+k ...

  4. P2308 添加括号(dfs记录dp路径)

    传送门 \(一看肯定是区间DP(因为和和合并石子很相似,都要加n-1次)\) \(转移方程为(其中he[i][j]是i到j的和)\) \[dp[i][j]=min(dp[i][j],dp[i][k]+ ...

  5. vue将后台的值赋给前台

    后台传List到前台: 赋值给table 赋值给form(只能一个个的赋值,对应prop属性) 后台传map到前台:(不需要使用下标取值)

  6. spring中bean的常用属性

    一.scop scope用来配置bean对象是否是单例模式.单例模式是java的二十三种设置模式之一,指在这个项目运行过程中一 个类的对象只会实例化一次.一般,工厂类的对象都是单例模式.非单例模式叫多 ...

  7. 【FPGA篇章一】FPGA工作原理:详细介绍FPGA实现编程逻辑的机理

    欢迎大家关注我的微信公众账号,支持程序媛写出更多优秀的文章 FPGA(Field Programmable Gate Array),即现场可编程逻辑门阵列,它是作为专用集成电路(ASIC)领域中一种半 ...

  8. 常用中文分词工具分词&词性标注简单应用(jieba、pyhanlp、pkuseg、foolnltk、thulac、snownlp、nlpir)

    1.jieba分词&词性标注 import jieba import jieba.posseg as posseg txt1 =''' 文本一: 人民网华盛顿3月28日电(记者郑琪)据美国约翰 ...

  9. SwiftUI - 一起来仿写微信APP之一首页列表视图

    简介 最近在学习 SwiftUI ,我一般都是先去学习界面布局,所以就想着仿写一下经常使用的软件的界面,所以先拿微信开刀.因为不想一次性发太多的内容,所以只好将主题分解,一部分一部分地去讲,接下来我们 ...

  10. 设计模式之GOF23迭代器模式

    迭代器模式Iterator /** * 自定义迭代器接口 * @author 小帆敲代码 * */public interface MyIterator {  void first();//游标置于第 ...