30

看了这篇,我确定你已经彻底搞懂Java的继承了

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzIxNzQwNjM3NA%3D%3D&%3Bmid=2247487596&%3Bidx=1&%3Bsn=87848114b9d1a3236c1205cccad4f340
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

遇到认真的读者是作者的一种幸运,真的,上一篇接口推送后,有好几个读者留言说,“二哥,你有一处内容需要修正,应该是接口中不能有 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()

RBVvayJ.png!web

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

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

public class Wanger {
    void write() {
    }

    private void privateWrite() {
    }

    public void publicWrite() {
    }

    protected void protectedWrite() {
    }
}

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

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

7BVZVv2.png!web

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

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

public class Wanger {
    int age;
    String name;

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

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

fyiqmy7.png!web

修复后的代码如下所示:

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 两个接口,但这时候编译出错了。

AFvam2n.png!web

原本,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();
}

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

------------------

公众号:沉默王二(ID:cmower)
CSDN:沉默王二
这是一个有颜值却靠才华吃饭的程序员,你知道,他的文章风趣幽默,读起来就好像花钱一样爽快。

长按下图二维码关注,你将感受到一个有趣的灵魂, 且每篇文章都有干货。

VFjANrY.jpg!web

-------------- --- -

原创不易,莫要白票,如果觉得有点用的话,请毫不留情地转发朋友圈吧 ,因为这将是我写作更多优质文章的最强动力。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK