Java学习-面向对象之继承

面向对象之继承

1. 继承的概述

继承的概念

泛指把前人的作风、文化、知识、财产等接受过来

Java中的继承

让类与类之间产生父子关系
  被继承的类叫做父类(基类、超类)
  被继承的类叫做子类(派生类)
格式(extends)
   class 父类{
    // …
   }
   class 子类 extends 父类{
    // …
   }

子类继承负累之后有什么效果?

子类拥有了父类的非私有成员(成员变量、成员方法)

案例

  • 代码演示
// 定义一个父类:Parent
public class Parent {
    // 成员变量
    private String name;
    private  int age;

    // 快捷键:alt + insert 快速生成构造方法和get set方法
    // 构造方法

    public Parent() {
    }

    public Parent(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // get和set
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
/*
    Child:子类,派生类
    Parent:父类,基类,超类
 */
public class Child extends Parent {

}
public class Test {
    // main函数是程序的主入口,所有的代码都是从这里开始执行的
    public static void main(String[] args) {
        // 创建Child类的对象
        Child c = new Child();
        // 给对象c的姓名设置为:张三
        c.setName("张三");

        // 打印姓名的值
        System.out.println(c.getName());

        /*
            Java中,子类只能继承父类的非私有成员(成员变量,成员方法)
         */

    }
}
  • 运行结果
    在这里插入图片描述

2. 继承的使用场景

继承的使用场景

  • 多个类中存在相同的属性和行为时,可以将这些内容提取出来放到一个新类中,让这些类和新类产生父子关系,实现代码复用。

案例:定义继承关系的动物类并使用

  • 需求:分别定义Dog类、Pig类,它们共有的属性有:name、age、sex,共有的行为有:eat(),两者特有的行为分别是:watch()、snore()

1.传统做法

  • 代码演示
// 定义一个狗类
public class Dog {
    // 成员变量
    private String name;
    private int age;
    private String sex;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    // 成员方法

    // 吃饭
    public void eat() {
        System.out.println("会吃饭");
    }
    // 看家
    public void watch() {
        System.out.println("会看家");
    }
}
// 定义一个猪类
public class Pig {
    // 成员变量
    private String name;
    private int age;
    private String sex;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    // 成员方法

    // 吃饭
    public void eat() {
        System.out.println("会吃饭");
    }
    // 看家
    public void snore() {
        System.out.println("会看家");
    }
}
public class Test {
    public static void main(String[] args) {
        // 测试狗类
        Dog d = new Dog();
        d.eat();
        d.watch();
        System.out.println("-----------------------");

        // 测试猪类
        Pig p = new Pig();
        p.eat();
        p.snore();
    }
}

发现Dog类和Pig类有重复的代码很多,采用继承可以解决这个问题

  • 运行结果
    在这里插入图片描述

2.使用继承后的代码

  • 代码演示
// 定义一个父类(Animal:动物类),里边放的是共性内容
public class Animal {
    /*
        它们共有的属性有:name、age、sex
        共有的行为有:eat()
     */
    // 成员变量
    private String name;
    private int age;
    private String sex;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    // 成员方法

    // 吃饭
    public void eat() {
        System.out.println("会吃饭");
    }
}
// 定义一个狗类
public class Dog extends Animal{
    // 定义看家的方法
    public void watch() {
        System.out.println("会看家");
    }
}
// 定义猪类
public class Pig extends Animal{
    // 打鼾
    public void snore() {
        System.out.println("会打鼾");
    }
}
public class Test {
    public static void main(String[] args) {
        // 测试狗类
        Dog d = new Dog();
        d.eat();
        d.watch();
        System.out.println("---------------");

        // 测试猪类
        Pig p = new Pig();
        p.eat();
        p.snore();
    }
}
  • 运行结果
    在这里插入图片描述

3. 继承的优缺点

优点

  1. 功能复用:直接将已有的属性和行为继承过来,实现了功能的复用,节省了大量的工作
  2. 便于扩展新功能:在已有功能的基础上,更容易建立、扩充新功能
  3. 结构清晰、简化认识:同属于一个继承体系的相关类,他们之间结构层次清晰,简化了人们对代码结构的认识
  4. 易维护性:不同类之间的继承关系,让这些事物之间保持一定程度的一致性,大大降低了维护成本

缺点

  1. 打破了封装性:父类向子类暴露了实现细节,打破了父类对象的封装性
  2. 高耦合性:类与类之间紧密的结合在一起,相互依赖性高

程序而设计的追求

低耦合,高内聚

  • 耦合:两个(或更多)模块相互依赖于对方
  • 内聚:模块内部结构紧密,独立性强

4. 继承关系中类成员的使用

案例1

  • 需求:子父类中定义了同名的成员变量,如何使用
  • 代码演示
// 父类:水果类
public class Fruit {
    // 成员变量
    int price = 20;
}
// 子类:苹果类
public class Apple extends Fruit{
    // 成员变量
    int price = 10;

    public void showPrice() {
        // 局部变量
        int price = 5;

        System.out.println(price);
        System.out.println(this.price);
        System.out.println(super.price);
    }
}
public class Test {
    public static void main(String[] args) {
        // 需求:测试继承关系中,子父类间成员变量的使用
        Apple apple = new Apple();
        apple.showPrice();

        /*
            Java中使用变量的规则:
                遵循“就近原则”,局部位置有就使用,
                没有就去本类的成员位置找,有就使用
                没有就去父类的成员位置找,有就使用,没有就报错
         */
    }
}
  • 运行结果
    在这里插入图片描述

继承关系中成员变量使用图解

在这里插入图片描述
2.
在这里插入图片描述
3.
在这里插入图片描述

继承关系中成员变量使用结论

  • 查找变量的原则:就近原则
  • 查找变量的顺序:局部变量->成员变量->父类->更高的父类…Object
  • 访问父类变量的方式:super.父类变量名;
  • super:当前对象父类的引用(父类内存空间的标识)
  • 对象初始化顺序:先初始化父类内容,再初始化子类内容

this和super的区别

this:
  本质:对象
  用法:从本类开始找
super:
  本质:父类内存空间的标识
  用法:从父类开始找

案例:

  • 代码演示
// 父类
public class Fu {
    int num = 30;
}
// 子类
public class Zi extends Fu{
    int num = 20;

    public void show() {
        int num = 10;
        System.out.println(num);   // 10
        System.out.println(this.num);   // 20
        System.out.println(super.num);   // 30
    }
}
// 测试类
public class Test {
    public static void main(String[] args) {
        /*
            使用变量遵循就近原则,
                先在局部位置找,有就使用
                没有就去本类的成员位置找,有就使用
                没有就去父类的成员位置找,有就使用,没有就报错

            如果局部变量,本类的成员变量,父类的成员变量重名了,如何解决?
                直接写变量名      局部变量
                this.变量名      本类的成员变量
                super.变量名     父类的成员变量
         */
        Zi zi = new Zi();
        zi.show();
    }
}
  • 运行结果
    在这里插入图片描述

案例2

  • 需求:子父类中定义了同名的成员方法,如何使用?
  • 代码演示
/*
    父类:武功类
    四大权限邱师傅的修饰范围从小到大分别是:
        private 默认(什么都不写就是默认) protected public
 */
public class Martial {
    // 练习内功
    public void internalStrength(){
        System.out.println("练习内功");
    }
    // 练习招式
    public void stroke() {
        System.out.println("练习招式");
    }
}
// 子类:九阴真经
public class NineYin extends Martial{
    // 练习内功
    public void internalStrength(){
        // 这里实在调用父类的成员方法
        super.internalStrength();

        System.out.println("以柔克刚");
    }

    public void stroke() {
        System.out.println("九阴白骨爪");
    }
}
public class Test {
    public static void main(String[] args) {
        // 需求:调用NineYin类中的功能
        NineYin ny = new NineYin();
        ny.internalStrength();
        ny.stroke();
    }
}
  • 运行结果
    在这里插入图片描述
  • 图解
    在这里插入图片描述

继承关系中成员方法使用结论

  • 定义重名方法的前提:
     父类不能完全满足现实需求,扩展父类功能
     父类功能已过时,重新实现父类功能

案例3.1

  • 需求:创建对象时,构造方法时如何被调用的?
  • 代码演示
// 父类
public class Person {
    public Person() {
        System.out.println("Person类的 空参构造");
        // System.out.println("Person类的带参构造" + name);
    }
}
public class Worker extends Person{
    public Worker() {
        System.out.println("Worker类的空参构造");
    }
}
/*
    测试类,用来演示:构造方法的调用的
    结论:
        子类所有构造方法的第一行都有一个默认的super();用来访问父类的无参构造方法
        如果父类没有无参构造,可以通过super(参数)的形式访问父类的带参构造
 */
public class Test {
    public static void main(String[] args) {
        // 创建子类对象
        Worker w = new Worker();
    }
}
  • 运行结果
    在这里插入图片描述
  • 结论
     创建子类对象时,优先调用父类构造方法
     子类构造方法的第一行,隐含语句super(),用于调用父类的默认无参构造

案例3.2

  • 需求:父类不存在默认无参构造方法怎么办?
  • 分析:子类创建对象时,,必须先初始化该对象的父类内容,若父类中不存在默认无参构造,须手动调用父类其它构造。
  • 代码演示
// 父类
public class Person {
    public Person(String name) {
        // System.out.println("Person类的 空参构造");
        System.out.println("Person类的带参构造" + name);
    }
}
// 子类
public class Worker extends Person{
    public Worker() {
        super("张三");    // 用于初始化父类成员的
        System.out.println("Worker类的空参构造");
    }
}
public class Test {
    public static void main(String[] args) {
        // 创建子类对象
        Worker w = new Worker();
    }
}
  • 运行结果
    在这里插入图片描述

5. 方法重写

方法重写(Override)

  • 定义
      子类中出现和父类方法定义相同的方法的现象
  • 解释
      方法重写也叫方法的复写、覆盖
      方法名、参数列表、返回值类型都相同
  • 注意事项
      父类私有方法无法重写
      子类方法访问权限不能小于父类方法
      子类不能比父类方法跑出更大的异常(了解)
  • 使用场景
      扩展父类功能
      父类功能过时,重新实现父类功能

Java中的访问权限修饰符

在这里插入图片描述

方法重写和方法重载的区别

重载(Overload)

方法名:相同
参数列表:不同(个数或对应位置类型)
返回值类型:无关
修饰符:无关
定义位置:同一个类

重写(Override)

方法名:相同
参数列表:相同
返回值类型:相同
修饰符:访问权限不小于被重写方法
定义位置:子父类中

6. Java中继承的特点

  • 单继承
      Java只支持类的单继承,但是支持多层(重)继承
      Java支持接口的多继承,语法为:
        接口A extends 接口B,接口C,接口D…
  • 私有成员不能继承
      只能继承父类的非私有成员(成员变量、成员方法)
  • 构造方法不能继承
      构造方法用于初始化本类对象。
      创建子类对象时,需要调用父类构造初始化
      该对象的父类内容,若父类构造可以被继承,
      该操作会造成调用的混乱。
  • 继承体现了“is a”的关系
      子类符合“is a(是一个)”父类的情况下,才能使用继承,其它情况不建议使用
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值