第八章 面向对象编程(中级) 中

在这里插入图片描述
第八章 面向对象编程(中级) 上

5.继承

extends

为什么需要继承

一个小问题, 还是看个程序com.zzw.extend_包 Extends01.java, 提出代码复用的问题.

我们编写两个类, 一个是Pupil [小学生], 一个是Graduate [大学毕业生].
问题: 两个类的属性和方法有很多是相同的, 怎么办?

⇒ 继承 (代码复用性~)

继承基本介绍和示意图

继承可以解决代码复用, 让我们的编程更加靠近人类思维. 当多个类存在相同的属性(变量)和方法时, 可以从这些类中抽象出父类, 在父类中定义这些相同的属性和方法, 所有的子类不需要重新定义这些属性和方法, 只需要通过extends来声明继承父类即可.

画出继承的示意图
在这里插入图片描述

继承的基本语法

class 子类 extends 父类 {
}
1.子类就会自动拥有父类定义的属性和方法
2.父类又叫 超类, 基类
3.子类又叫派生类.

快速入门案例

我们对Extends01.java进行改进, 使用集成的方法, 请大家注意体会集成的好处 com.zzw.extend_.improve_ 包

继承给编程带来的便利

1.代码的复用性提高了

2.代码的扩展性和维护性提高了

继承的深入讨论

●继承的深入讨论/细节问题 com.zzw.extend_包 Base.java Sub.java ExtendsDetail.java

public class Base { //父类
    //4个属性
    public int n1 = 100;
    protected int n2 = 200;
    int n3 = 300;
    private int n4 = 400;

    public Base() { //无参构造器
        System.out.println("父类Base()构造器被调用....");
    }
    public Base(String name) { //有参构造器
        System.out.println("父类Base(String name)构造器被调用...");
    }
    public Base(String name, int age) { //有参构造器
        System.out.println("父类Base(String name, int age)构造器被调用...");
    }
    public void test100() {
        System.out.println("test100");
    }

    protected void test200() {
        System.out.println("test200");
    }

    void test300() {
        System.out.println("test300");
    }

    private void test400() {
        System.out.println("test400");
    }
}
public class Sub extends Base { //子类
    public Sub() {//子类无参构造器
        System.out.println("子类Sub()构造器被调用...");
    }

    public Sub(String name) { //有参构造器
        System.out.println("子类Sub(String name)构造器被调用...");
    }

    public Sub(String name, int age) { //有参构造器
        System.out.println("子类Sub(String name, int age)构造器被调用...");
    }

    public void sayOk() { //子类方法
        
    }
}

1.子类继承了父类所有的属性和方法, 非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问, 要通过父类提供公共的方法去访问.

debug工具: 验证子类的确是继承了父类所有的属性
在这里插入图片描述
在这里插入图片描述

2.子类必须调用父类的构造器, 完成父类的初始化

3.当创建子类对象时, 不管使用子类的哪个构造器, 默认情况下都会去调用父类的无参构造器, 如果父类没有提供无参构造器, 则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作, 否则, 编译不会通过.
在这里插入图片描述
在这里插入图片描述

4.如果希望指定去调用父类的某个构造器, 则显示地调用一下: super(参数列表)

5.super在使用时, 必须放在构造器第一行(super只能在构造器中使用)

6.super() 和 this() 都只能放在构造器第一行, 因此这两个方法不能共存在一个构造器
在这里插入图片描述

在这里插入图片描述

7.java所有类都是Object类的子类, Object 是所有类的基类.
Ctrl + H 可以查看类的继承关系

在这里插入图片描述

8.父类构造器的调用不限于直接父类! 将一直往上追溯直到Object类(顶级父类)

父类的对象并没有消失或离开,而是在内存中按照类的继承层次结构被创建并初始化。在构造函数调用完成后,对象的状态会保存在内存中,它可以通过引用被访问和操作。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

9.子类最多只能继承一个父类(指直接继承), 即java是单继承机制
思考: 如何让A类继承B类和C类? A 继承 B, B 继承 C

在这里插入图片描述在这里插入图片描述

10.不能滥用继承, 子类和父类之间必须满足 is-a 的逻辑关系

Person is a Music?
Person Music
Music extends Person //不合理

Animal
Cat extends Animal //合理

继承本质分析

√ 案例
我们看一个案例来分析当子类继承父类, 创建子类对象时, 内存中到底发生了什么? 老师提醒: 当子类对象创建好后, 建立查找的关系. com.zzw.extend_包 ExtendsTheory.java

在这里插入图片描述


√ 子类的内存布局

在这里插入图片描述

com.zzw.extend_包 ExtendsTheory.java

//讲解继承的本质
public class ExtendsTheory {
    public static void main(String[] args) {
        son son = new son(); //内存的布局
        //?-> 这时请大家注意, 要按照查找关系来返回信息
        //(1) 首先看子类是否有该属性
        //(2) 如果子类有这个属性, 并且可以访问, 则返回信息
        //(3) 如果子类没有这个属性, 就看父类有没有这个属性(如果父类有该属性, 并且可以访问, 就返回信息..)
        //(4) 如果父类没有就按照(3)的规则, 继续找上级父类, 直到Object.
        System.out.println(son.name);//返回的就是大头儿子
        //System.out.println(son.age);//返回的就是39
        System.out.println(son.getAge());//返回的就是39
        System.out.println(son.hobby);//返回的就是旅游
    }
}
class GrandPa { //爷爷类
    String name = "大头爷爷";
    String hobby = "旅游";
}
class Father extends GrandPa{ //父类
    String name = "大头爸爸";
    private int age = 39;

    public int getAge() {
        return age;
    }
}
class son extends Father { //子类
    String name = "大头儿子";
}

课堂练习

com.zzw.extend_exercise 包

案例1 ExtendsExercise01.java
class A {
        A() {
                System.out.println(“a”);
        }
        A(String name) {
                System.out.println(“a name”);
        }
}
class B extends A {
        B() {
                this(“abc”);
                System.out.println(“b”);
        }
        B(String name) {
                System.out.println(“b name”);
        }
}
main中: B b = new B(); 会输出什么?

在这里插入图片描述

案例2 ExtendsExercise02.java
class AA { //AA类
        public AA() {
                System.out.println(“我是AA类”);
        }
}
class BB extends AA{ //BB类, 继承AA类
        public BB() {
                System.out.println(“我是BB类的无参构造”);
        }
        public BB(String name) {
                System.out.println(name + “我是BB类的有参构造”);
        }
}
class CC extends BB { //CC类, 继承 BB类
        public CC() {
                this(“hello”);
                System.out.println(“我是CC类的无参构造”);
        }
        public CC(String name) {
                super(“hahah”);
                System.out.println(“我是CC类的有参构造”);
        }
}
main中: CC cc = new CC(); 会输出什么?

在这里插入图片描述

案例3 ExtendsExercise03.java
编写Computer类, 包含CPU, 内存, 硬盘等属性, getDetails方法用于返回Computer的详细信息.
编写PC子类, 继承Computer类, 添加特有属性 【品牌brand】
编写NotePad子类, 继承Computer类, 添加特有属性【颜色color】
编写ExtendsExercise03类, 在main方法中创建PC和NotePad对象, 分别给对象中特有的属性赋值, 以及从Computer类继承的属性赋值, 并使用方法打印输出信息.

6.super关键字

基本介绍

super代表父类的引用, 用于访问父类的属性, 方法, 构造器

基本语法

1.访问父类的属性, 但不能访问父类的private属性
   super.属性名;
2.访问父类的方法, 但不能访问父类的private方法
   super.方法名(参数列表);
3. 访问父类的构造器(这点前面讲过)
   super(参数列表); 只能放在构造器的第一句, 只能出现一句!

super给编程带来的便利/细节

com.zzw.super_包 Super01.java
1.调用父类的构造器的好处 (分工明确, 父类属性由父类初始化, 子类的属性由子类初始化)

2.当子类中有和父类中的成员 (属性和方法) 重名时, 为了访问父类的成员, 必须通过super. 如果没有重名, 使用 super, this 和 直接访问 是一样的效果!
在这里插入图片描述


3.super的访问不限于直接父类, 如果爷爷类和本类中有同名的成员, 也可以使用super去访问爷爷类的成员; 如果多个基类(上级类)中都有同名的成员, 使用super访问遵循就近原则. A -> B -> C, 当然也需要遵守访问权限的相关规则

super和this比较

No.区别点thissuper
1访问属性访问本类的属性, 如果本类没有此属性则从父类继续查找从父类开始查找属性
2调用方法访问本类中的方法, 如果本类没有此方法则从父类继续查找从父类开始查找方法
3调用构造器调用本类构造器, 必须放在构造器的首行调用父类构造器, 必须放在子类构造器的首行
4特殊表示当前对象子类中访问父类对象 (父类的一个引用)

●学习完继承后, 类定义进一步完善

在这里插入图片描述

7.方法重写/覆盖

基本介绍

简单地说: 方法覆盖(重写)就是子类有一个方法, 和父类的某个方法的名称, 返回类型, 形参列表一样, 那么我们就说子类的这个方法覆盖了父类的那个方法.

快速入门

com.zzw.override_包 Override01.java
在这里插入图片描述

注意事项和使用细节

方法重写也叫方法覆盖, 需要满足下面的条件 OverrideDetail.java[看Animal和Dog]

1.子类方法的形参列表, 方法名称, 要和父类方法的形参列表, 方法名称完全一样.

2.子类方法的返回类型和父类方法返回类型一样, 或者是父类返回类型的子类. 比如 父类 返回类型是 Object, 子类方法返回类型是String. 否则, attempting to use incompatible return type

在这里插入图片描述

3.子类方法不能缩小父类方法的访问权限. 否则, attempting to assign weaker access privileges

在这里插入图片描述

课堂练习

√ 题1

请对方法的重写和重载做一个比较

名称发生范围方法名形参列表返回类型修饰符
重载(overload)本类必须一样类型, 个数或者顺序至少有一个不同没有要求没有要求
重写(override)父子类必须一样必须一样子类重写的方法, 返回的类型和父类方法返回的类型一致 或是其子类子类不能缩小父类方法的访问权限

√ 题2
OverrideExercise.java

1.编写一个Person类, 包括属性/private (name, age), 构造器, 方法say(返回自我介绍的字符串)
2.编写一个Student类, 继承Person类, 增加id, score属性/private, 以及构造器, 定义say方法(返回自我介绍的信息)
3.在main中, 分别创建Person 和 Student对象, 调用say方法输出自我介绍.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

~ 小团子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值