09 继承、super关键字、this关键字、抽象类

继承、super、this、抽象类

一、继承

1. 继承的概述

  • **面向对象三大特征:**封装性、继承性、多态性。
  • 继承是多态的前提,如果没有继承,就没有多态。
  • 继承主要解决的问题是:共性抽取
  • 继承关系中的特点:
    • 子类可以拥有父类的“内容”。
    • 子类还可以拥有自己专有的内容。
      • 父类:也叫基类、超类。
      • 子类:也叫派生类。

2. 继承的格式

  • 在继承关系中,“子类就是一个父类”。也就是说,子类可以被当做父类看待。 例如,父类是“员工”,子类是“讲师”,那么“讲师就是一个员工”。关系:is-a

  • 定义父类的格式:(一个普通的类定义)

    public class 父类名称{
      //...
    }
    
  • 定义子类的格式:

    public class 子类名称 extends 父类名称{
      //...
    }
    
//定义一个Employee父类
public class Employee {
    public void method() {
        System.out.println("方法执行!");
    }
}

//定义一个Teacher子类,继承父类Employee
public class Teacher extends Employee{
}

//定义一个Assistant子类,继承父类Employee
public class Assistant extends Employee {
}

public class Test {
    public static void main(String[] args) {
        Employee employee = new Employee();
        employee.method();  // 方法执行!

        Teacher teacher = new Teacher();
        teacher.method();  // 方法执行!

        Assistant assistant = new Assistant();
        assistant.method(); // 方法执行!
    }
}

3. 继承中成员变量的访问特点

  • 在父子类的继承关系中,如果成员变量重名,则创建子类对象时,访问有两种方式:

    • **直接通过子类对象访问成员变量:**等号左边是谁,就优先使用谁,没有则向上找。
    • **间接通过成员方法访问成员变量:**该方法属于谁,就优先使用谁,没有则向上找。
    //父类
    public class Fu {
        int numFu = 10;
        int num = 100;
        public void methodFu() {
            System.out.println(num);
        }
    }
    
    //子类
    public class Zi extends Fu{
        int numZi = 20;
        int num = 200;
        public void methodZi() {
            System.out.println(num);
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            Fu fu = new Fu();
            System.out.println(fu.numFu); // 10
            //优先使用等号左边的 Fu fu = new Fu();
            System.out.println(fu.num); // 100
            //方法属于谁就优先使用谁
            fu.methodFu(); // 100
    
            Zi zi = new Zi();
            System.out.println(zi.numFu); // 10
            System.out.println(zi.numZi); // 20
            //优先使用等号左边的 Zi zi = new Zi();
            System.out.println(zi.num); // 200
            //方法属于谁就优先使用谁
            zi.methodZi(); // 200
            zi.methodFu(); // 100
        }
    }
    

4. 区分子类方法中重名的三种变量

  • 局部变量:直接写局部变量名
  • 本类的成员变量this.成员变量名
  • 父类的成员变量super.成员变量名
//定义父类
public class Fu {
    int num = 10;
}
//定义子类
public class Zi extends Fu{
    int num = 20;
    public void method() {
        int num = 30;
        System.out.println(num); // 30
        System.out.println(this.num); // 20
        System.out.println(super.num); // 10
    }
}

5. 继承中成员方法的访问特点

  • 在父子类的继承关系中,创建子类对象,访问成员方法的规则:
    • 创建的对象是谁,就优先用谁,如果没有则向上找。
  • **注意事项:**无论是成员方法还是成员变量,如果没有都是向上找父类,绝对不会向下找子类。
//父类
public class Fu {
    public void methodFu() {
        System.out.println("父类方法执行了!");
    }
    public void method() {
        System.out.println("父类同名方法执行了!");
    }
}

//子类
public class Zi extends Fu{
    public void methodZi() {
        System.out.println("子类方法执行了!");
    }
    public void method() {
        System.out.println("子类同名方法执行了!");
    }
}

public class Test {
    public static void main(String[] args) {
        Zi zi = new Zi();
        zi.methodFu(); // 父类方法执行了!
        zi.methodZi(); // 子类方法执行了!
        //创建的是子类对象 new Zi() ,所以优先使用子类方法。
        zi.method();   // 子类方法执行了!
    }
}

6.继承中方法的覆盖重写

  • 概念与特点

    • 重写(Override):在继承关系当中,方法名称一样,参数列表也一样。也叫,覆盖、覆写。
    • 重载(Overload):方法名称一样,参数列表不一样。
    • **方法的覆盖重写特点:**创建的是子类对象,则优先使用子类方法。
  • 注意事项

    • 必须保证父子类之间方法的名称相同,参数列表也相同。
      • @Override写在方法前面,可以用来检测是不是有效的正确覆盖重写。也可以不使用。
    • 子类方法的返回值必须小于等于父类方法的返回值范围。
      • 扩展:java.lang.Object类是所有类的公共最高父类(祖宗类)。
    • 子类方法的权限必须大于等于父类方法的权限修饰符。
      • 扩展:public > protected > (default) > private
      • (default)不是关键字default ,而是什么都不写留空。
  • 应用场景

    //父类
    public class Phone {
        public void call() {
            System.out.println("打电话");
        }
        public void send() {
            System.out.println("发短信");
        }
        public void show() {
            System.out.println("显示号码");
        }
    }
    
    //子类
    public class NewPhone extends Phone{
        @Override
        public void show() {
            //System.out.println("显示号码");
            super.show(); //调用父类的show方法
            System.out.println("显示姓名");
            System.out.println("显示头像");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            Phone phone = new Phone();
            phone.call(); // 打电话
            phone.send(); // 发短信
            phone.show(); // 显示号码
    
            NewPhone newPhone = new NewPhone();
            newPhone.call(); // 打电话
            newPhone.send(); // 发短信
            newPhone.show(); // 显示号码
                             // 显示姓名
                             // 显示头像
        }
    }
    

7. 继承中构造方法的访问特点

  • 子类构造方法当中有一个默认隐含的super()调用,所以一定是先调用的父类构造,后执行的子类构造。
  • 子类构造可以通过super关键字来调用父类重载构造,如super(参数,...)
  • super的父类构造调用,必须是子类构造方法的第一个语句,并且一个子类构造不能调用多次super构造。
  • 子类构造必须调用父类构造方法,不写则默认有super(),写了则使用所写的指定的super调用,super只能有一个,还必须是第一个。

8. super关键字的三种用法

  • 在子类的成员方法中,访问父类的成员变量 super.
  • 在子类的成员方法中,访问父类的成员方法 super.
  • 在子类的构造方法中,访问父类的构造方法 super(参数)

9.this关键字的三种用法

  • 在本类的成员方法中,访问本类的成员变量 this.
  • 在本类的成员方法中,访问本类的另一个成员方法 this.
  • 在本类的构造方法中,访问本类的另一个构造方法 this(参数)
    • this(参数)调用也必须是构造方法的第一个语句且唯一。
    • superthis两种构造调用,不能同时使用。

10. super与this关键字图解

11. Java继承的三个特点

  • Java语言是单继承的。一个类的直接父类只有一个。

    class A{}
    class B extends A{} //正确
    class C{}
    class D extend A, C{} //错误
    
  • Java语言可以多级继承

    class A{}
    class B extends A{} //正确
    class C extends B{} //正确
    
  • 一个子类的直接父类是唯一的,但一个父类可以有多个子类

    class A{}
    class B extends A{} //正确
    class C extends A{} //正确
    

二、抽象

1. 概念

  • 如果父类中的方法不确定如何进行{}方法体实现,那么这就应该是一个抽象方法

2.抽象方法和抽象类的格式

  • **抽象方法:**在返回值之前加上abstract关键字,然后去掉大括号,直接分号结束。
  • **抽象类:**抽象方法所在的类,必须是抽象类,在class之前加上abstract即可。
  • 抽象类中可以定义普通成员方法。
public abstract class Animal{
  public abstract void eat();
  public void noemalMethod(){}
}

3. 抽象方法和抽象类的使用

  • 不能直接创建抽象类对象。
  • 必须用一个子类来继承抽象父类。
  • 子类必须覆盖重写抽象父类当中所有的抽象方法。
    • 覆盖重写(实现):子类去掉抽象方法的abstract关键字,然后补上方法体大括号。
  • 创建子类对象进行使用。
//抽象类
public abstract class Animal {
    public abstract void eat();
}
//子类
public class Cat extends Animal{
    @Override
    public void eat(){
        System.out.println("猫吃鱼!");
    }
}

public class Test {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.eat();     // 猫吃鱼!
    }
}

4. 抽象方法和抽象类的注意事项

  • 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

  • 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用。

    //抽象类
    public abstract class Animal {
        public Animal(){
            System.out.println("抽象父类的构造方法执行!");
        }
        public abstract void eat();
    }
    //子类
    public class Cat extends Animal{
        public Cat(){
            System.out.println("子类的构造方法执行!");
        }
        @Override
        public void eat() {
            System.out.println("猫吃鱼!");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            Cat cat = new Cat(); // 抽象父类的构造方法执行!
                                 // 子类的构造方法执行!
            cat.eat();  // 猫吃鱼!
        }
    }
    
  • 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

  • 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。

    //抽象父类
    public abstract class Animal {
        public abstract void eat();
        public abstract void sleep();
    }
    //抽象子类
    public abstract class Dog extends Animal{
        @Override
        public void eat() {
            System.out.println("狗吃骨头!");
        }
    }
    //子类
    public class DogGolden extends Dog {
    
        @Override
        public void sleep() {
            System.out.println("金毛要睡了!");
        }
    }
    //子类
    public class Dog2Ha extends Dog{
        @Override
        public void sleep() {
            System.out.println("二哈要睡了!");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            DogGolden dogGolden = new DogGolden();
            dogGolden.eat(); // 狗吃骨头!
            dogGolden.sleep(); // 金毛要睡了!
    
            Dog2Ha dog2Ha = new Dog2Ha();
            dog2Ha.eat(); // 狗吃骨头!
            dog2Ha.sleep(); // 二哈要睡了!
        }
    }
    

5. 发红包案例

  • 题目描述

    群主发普通红包。某群有多名成员,群主给成员发普通红包。红包规则为:
    1.群主的一笔金额,从群主的余额中扣除,平均分为n等份,让成员领取。
    2.成员领取红包后,保存到成员余额中。
    
  • 分析

  • 实现

//用户类
public class User {

    private String name;  // 用户名
    private float money;  // 用户持有的金额

    //无参构造
    public User() {
    }
    //全参构造
    public User(String name, float money) {
        this.name = name;
        this.money = money;
    }
    //Getter & Setter方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getMoney() {
        return money;
    }

    public void setMoney(float money) {
        this.money = money;
    }
}

//群主类
import java.util.ArrayList;

public class GroupOwner extends User{
    //无参构造
    public GroupOwner() {
    }
    //全参构造
    public GroupOwner(String name, float money){
        this.setName(name);
        this.setMoney(money);
    }
    //群主发红包方法
    public ArrayList<Float> send(float money, int copies) {
        this.setMoney(this.getMoney() - money);
        float singleRedBag = money / copies;
        ArrayList<Float> redBag = new ArrayList<>();
        for (int i = 0; i < copies; i++) {
            redBag.add(singleRedBag);
        }
        return redBag;
    }
}

//群成员类
import java.util.ArrayList;

public class GroupMember extends User{
    //无参构造
    public GroupMember(){
    }
    //全参构造
    public GroupMember(String name, float money){
        this.setName(name);
        this.setMoney(money);
    }
    //收红包
    public void receive(ArrayList<Float> redBag){
        if (redBag.size() != 0){
            this.setMoney(this.getMoney() + redBag.remove(0));
        }
    }
}

import java.util.ArrayList;

public class Test {
    public static void main(String[] args) {
        //创建群主
        GroupOwner groupOwner = new GroupOwner("language", 5);
        //发红包前群主的信息
        System.out.println("发红包前群主的信息");
        System.out.println("姓名:" + groupOwner.getName() + ",金额:" + groupOwner.getMoney());
        //创建群成员
        GroupMember groupMember_01 = new GroupMember("C", 1);
        GroupMember groupMember_02 = new GroupMember("C++", 2);
        GroupMember groupMember_03 = new GroupMember("Java", 3);
        //收红包前群成员的信息
        System.out.println("收红包前群成员的信息");
        System.out.println("姓名:" + groupMember_01.getName() + ",金额:" + groupMember_01.getMoney());
        System.out.println("姓名:" + groupMember_02.getName() + ",金额:" + groupMember_02.getMoney());
        System.out.println("姓名:" + groupMember_03.getName() + ",金额:" + groupMember_03.getMoney());

        System.out.println("=======================================");

        //群主发红包
        ArrayList<Float> redBag = groupOwner.send(3, 3);
        //发红包后群主的信息
        System.out.println("发红包后群主的信息");
        System.out.println("姓名:" + groupOwner.getName() + ",金额:" + groupOwner.getMoney());

        //群成员收红包
        groupMember_01.receive(redBag);
        groupMember_02.receive(redBag);
        groupMember_03.receive(redBag);
        //收红包后群成员的信息
        System.out.println("收红包后群成员的信息");
        System.out.println("姓名:" + groupMember_01.getName() + ",金额:" + groupMember_01.getMoney());
        System.out.println("姓名:" + groupMember_02.getName() + ",金额:" + groupMember_02.getMoney());
        System.out.println("姓名:" + groupMember_03.getName() + ",金额:" + groupMember_03.getMoney());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值