资讯专栏INFORMATION COLUMN

【从基础学 Java】继承

Dongjie_Liu / 1253人阅读

摘要:快速了解继承在的继承关系里子类可以从获取父类的所有的公共和受保护成员字段方法和内部类。阻止继承有些情况下,我们可能不希望子类覆盖父类的方法,这时候,用关键字修饰方法即可实现该目的。

和现实世界中:子女可以继承父母的一些特征(如:基因)、财产等一样。OOP 中也有提供类似的特性,一个类完全可以从其它类里获得一些属性和方法,而不需要我们自己重新定义。这种特性简单但强大 (Simple and powerful)。

快速了解继承

在 Java 的继承关系里:子类可以从获取父类的所有的公共和受保护成员(字段、方法和内部类)。当然,构造方法不是成员 (members) ,所以不能被继承。同时,在 Java 的继承里,子类可以做如下事情:

直接使用继承来的字段

直接使用继承来的方法

声明和父类同名字段,隐藏 掉父类字段

通过父类提供的公有/受保护的方法访问父类的私有成

创建新的字段

重写父类同方法签名的实例方法,隐藏 掉父类方法

重写父类同方法签名的静态方法,隐藏 掉父类方法

编写父类中不存在的方法

使用 super 关键字,利用父类构造方法

覆盖

当子类拥有和父类(或接口)同样方法签名的方法,这种现象叫做覆盖。如以下代码:

class Father
{
    public void doSomthing(){
        // Father do something
    }
}

class Son extends Father{
    @Override
    public void doSomething(){
        // Son do something
    }
}

覆盖父类实例方法

覆盖父类静态方法

覆盖接口默认方法 (JDK 8+)

对于实例方法的覆盖,实际是子类拥有自己的方法。
对于静态方法的覆盖,要记住:静态方法时属于类的,在多态中,调用的始终是类的方法。接口里的静态方法永远不会被覆盖。

    Father father = new Son();
    Father.staticMethod(); // 这里使用的是父类Father的静态方法

对于接口方法的覆盖,遵循以下原则:
1.实例方法的优先级大于接口方法 (JDK8+)

interface Animal{
    default void saySomething(){
        // Animal say something
    }
}
interface Cow extends Animal{
    default void saySomething(){
        // Cow say something
    }
}

class MyCow implements Cow{
    @Override
    public void saySomething(){
        // MyCow say something
    }
    
    Animal myCow = new MyCow();
    myCow.saySomething(); // MyCow say something
}

2.有共同祖先的接口,先被覆盖的方法(继承深度低)会被后覆盖的方法(继承级别高)覆盖

interface Animal{
    default void saySomething(){
        // Animal say something
    }
}
interface Pig extends Animal{
    default void saySomething(){
        // Pig say something
    }
}
interface BigPig extends Pig{
    default void saySomething(){
        // BigPig say something
    }
}
class MyPig implements Pig, BigPig{
    public static void main(String...args){
        MyPig myPig = new MyPig();
        myPig.saySomething(); // BigPig saySomething()
    }
}

P.S. 如果出现同一继承级别(上例中 BigPig 和 Pig 都继承 Animal 接口)或者子类继承的接口无相关关系,但是接口间有同方法签名的方法,就会出现覆盖冲突。需要用 super 关键字指明具体实现哪个接口的方法或者直接覆盖。

interface Run{
    default void run(){
        // Run run
    }
}

interface Car{
    default void run(){
        // Car run
    }
}

class MyCar implements Run, Car{
    @Override
    public void run(){
        Car.super.run();
    
    }
}

同时,我们需要注意,Java 子类覆盖父类方法,应该:

方法的访问权限大于等于父类

方法的返回值小于等于父类

方法抛出的异常小余等于父类

如果子类定义和父类同方法签名的方法,会有如下结果:

x 父类的实例方法 父类的静态方法
子类的实例方法 覆盖父类方法 编译错误
子类的静态方法 编译错误 隐藏父类方法
多态

我们已经知道,子类可以覆盖父类的方法。如果有一个类继承自另外一个类,我们完全可以用一个父类来引用一个子类,如:

class Person{
    public void saySomething(){
        // Person say something
    }
}
class Father{
    @Override
    public void saySomething(){
        // Father say something
    }
}

class Test{
    pubilc static void main(String...args){
        Father father = new Father();
        Person person = father; // 父类引用子类对象
    }
}

像这种父类引用子类对象的现象,Java 里叫做多态 (Polymorphism)。在 Java 里,只有满足如下三个条件,才能叫多态:

继承关系

覆盖方法

父类引用子类对象

对于多态方法的运行,编译器会列举所有父类和子类符合调用方法签名(方法名+参数列表)的方法,然后以以下原则编译、调用方法:

成员变量(编译和运行都看左边)

成员方法(编译看左边,运行看右边)

静态方法(编译和运行都看左边)

其中,调用 private, final, static 方法的过程叫静态绑定,否则叫动态绑定
在使用多态的过程中,最有效的判断语句能否通过编译的方法是:

右边的类是否是(IS-A)左边的类

如示例代码里的 father(Father) IS-A Person。

阻止继承

有些情况下,我们可能不希望子类覆盖父类的方法,这时候,用 final 关键字修饰方法即可实现该目的。编译器可以对用final的方法进行内联操作优化处理。

强制转换

在多态里,我们知道一个父类可以引用一个子类对象:

Father father = new Son();

但是,反过来就不行了:

Son son = new Father();

如果需要让编译器不报错,我们就得进行强制类型转换操作:

Son son = (Son)new Father();

不过,这么做有风险,我们一般要使用 instanceof关键字先判断,能否将父类“安全”转换成子类:

Father f = ...;
if (f instanceof Son){
    Son son = (Son)f;
}
抽象类与接口

在继承体系里,比较常用的就是抽象类和接口。它们比较相似:

都能被继承

都包含抽象方法

都不能被实例化

然而,它们还是有不同的,例如:

抽象类可以声明非 final&&static 的字段,接口不行(默认 public static final )

抽象类可以有构造方法,接口不行

抽象类可以声明非 public 方法,接口不行(默认方法是 public )

一个类只能继承一个抽象类,可以继承多个接口

然后,抽象类和接口有不同的使用场景:P
抽象类:

在相关类里共享代码

规定了一系列通用的方法和属性

需要定义非静态、非共有的方法

接口:

定义属性,如可比较 (Comparable)、可飞(Flyable)

定义行为,不关心具体实现

希望使用多继承

继承的技巧

在 Java 里,我们一般按照如下规则使用继承:

将公共操作放在超类(父类)中

不要使用受保护的域

继承严格遵循 is-a 原则

不要过多得使用反射

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66789.html

相关文章

  • Java基础(了解继承

    摘要:上学学的完全没印象,基础烂的不行,最近项目主要是改,有时间就看了一下这本书补一下基础在写项目时,老用到继承,但是对其了解不深,会用但是不理解概念继承是面向对象编程技术的一块基石,因为它允许创建分等级层次的类。 上学学的完全没印象,Java基础烂的不行,最近项目主要是改bug,有时间就看了一下Head First Java这本书补一下基础 在写项目时,老用到Java继承,但是对其了解不深...

    MageekChiu 评论0 收藏0
  • 基础 Java】序

    摘要:本人生性愚钝,在大学期间没能好好领略等面向对象编程的魅力。现借助一些较为权威的书籍资料,将基础知识里比较重要的东西整理成文,命名从基础学。如果博文不慎侵犯了您的著作权,请联系我。 和很多大学一样,我的学校也是从 Java 、C++ 入手,教给我们面向对象 (OOP) 的思想。本人生性愚钝,在大学期间没能好好领略 Java 等面向对象编程的魅力。现借助一些较为权威的书籍资料,将 Java...

    JackJiang 评论0 收藏0
  • 先转行基础入门编程可以吗?

    摘要:你只需要相信一句话键盘敲烂,月薪过万就行了,进入正文,零基础入门知识点大纲如下其实到目前为止,的岗位需求还是非常多的,还是大多数企业后台开发的主流编程语言,功能强大,还是很值得学习的。 ...

    desdik 评论0 收藏0
  • JAVA 的几点建议

    摘要:自制力好的人,估计在保存后会翻出来看两眼,过几天又忘得一干二净了。多思考学会思考,养成多思考的习惯。以项目来驱动自己学习,整个过程将会有趣得多。后语以上就是我对自学的几点建议,希望对你们有帮助。 微信公众号:一个优秀的废人如有问题或建议,请后台留言,我会尽力解决你的问题。 showImg(https://segmentfault.com/img/remote/1460000018208...

    klinson 评论0 收藏0
  • 基础 Java】泛型

    摘要:泛型方法泛型类中可以定义静态非静态的泛型方法。上述泛型类会被替换成下面形式一般使用第一个限定类型替换变为原始类型,没有限定类型,使用替换。 引言 在面向对象的世界里,我们如果需要一个容器来盛装对象。举个例子:一个篮子。我们可以用这个篮子装苹果,也可以用这个篮子装香蕉。基于 OOP 的思想,我们不希望为苹果和香蕉分别创建不同的篮子;同时,我们希望放进篮子里的是苹果,拿出来的还是苹果。于是...

    huhud 评论0 收藏0

发表评论

0条评论

Dongjie_Liu

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<