Java(八)---继承


前言

上一篇我们讲到了面向对象的三大特性的封装,下来我们将继承


1 为什么需要继承

Java中使用类对现实世界中实体来进行描述,类经过实例化之后的产物对象,则可以用来表示现实中的实体,但是
现实世界错综复杂,事物之间可能会存在一些关联,那在设计程序是就需要考虑。
比如:狗和猫,它们都是一个动物。

使用Java语言来进行描述,就会设计出
猫:

public class Cat {
    public String name;
    public int age;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat(){
        System.out.println(this.name+"正在吃饭....");
    }
    public void miaomiao(){
        System.out.println(this.name+"正在喵喵叫....");
    }
}

狗:

public class Dog {
    public String name;
    public int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void eat(){
        System.out.println(this.name+"正在吃饭....");
    }
    public void bark(){
        System.out.println(this.name+"正在汪汪叫....");
    }

}

通过观察上述代码会发现,猫和狗的类中存在大量重复。
那能否将这些共性抽取呢?面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。

2 继承概念

继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
例如:狗和猫都是动物,那么我们就可以将共性的内容进行抽取,然后采用继承的思想来达到共用。
在这里插入图片描述
在这里插入图片描述

Dog和Cat都继承了Animal类,其中:Animal类称为父类/基类或超类,Dog和Cat可以称为Animal的子类/派生类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。
从继承概念中可以看出继承最大的作用就是:实现代码复用,还有就是来实现多态(后序讲)

3.继承的语法

在Java中如果要表示类之间的继承关系,需要借助extends关键字,具体如下:

public class Animal {
    public String name;
    public int age;
    public void eat(){
        System.out.println(this.name+"正在吃饭....");
    }
}

修饰符 class 子类 extends 父类 {
// …
}

Animal类

public class Animal {
    public String name;
    public int age;
    public void eat(){
        System.out.println(this.name+"正在吃饭....");
    }
}

Cat类

public class Cat extends Animal {

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void miaomiao(){
        System.out.println(this.name+"正在喵喵叫....");
    }
}

Dog类

public class Dog extends Animal{
    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void bark(){
        System.out.println(this.name+"正在汪汪叫....");
    }
}

4.父类成员访问

为了解释这个问题,我们在创建一个包,包中有两个类
Base类

public class Base {
    int a=1;
    int b=2;
}

Derived类

public class Derived extends Base {
    int c=10;
    public void method(){
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
    }

    public static void main(String[] args) {
        Derived derived=new Derived();
        derived.method();
    }
}

4.1.子类和父类不存在同名成员变量

就跟上面代码一样,结果是各输入自己的

结果是:各输入自己的

4.2.子类和父类成员变量同名

public class Derived extends Base {
    int a=10;
    public void method(){
        System.out.println(a);
        System.out.println(b);
    }

    public static void main(String[] args) {
        Derived derived=new Derived();
        derived.method();
    }
}

Base类

public class Base {
    int a=1;
    int b=2;
}

结果是
在这里插入图片描述
输出的结果是子类的a,那么怎么可以输入父类的a呢?
使用super关键字
Base类不变
Derived类发生变化

public class Derived extends Base {
    int a=10;

    public void method(){
       // System.out.println(a);
        System.out.println("子类的a:"+this.a);
        System.out.println("父类的a:"+super.a);
        System.out.println(b);
    }

    public static void main(String[] args) {
        Derived derived=new Derived();
        derived.method();
    }
}

在这里插入图片描述

4.3.super关键字

上面我们使用了super关键字,去调用成员变量,现在我们使用super,去调用构造方法使用上面Animal类,Dog类和Cat类的例子。
现在我们在Animal类中创建一个构造方法

public class Animal {
    public String name;
    public int age;

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

    public void eat(){
        System.out.println(this.name+"正在吃饭....");
    }
}

此时Dog和Cat类中的原来的构造方法会报错,为了解决这个问题,我们使用super关键字

public class Cat extends Animal {

    public Cat(String name, int age) {
        super(name,age);
    }
    public void miaomiao(){
        System.out.println(this.name+"正在喵喵叫....");
    }
}
public class Dog extends Animal{
    public Dog(String name, int age) {
        super(name, age);;
    }
    public void bark(){
        System.out.println(this.name+"正在汪汪叫....");
    }
}

【注意事项】

  1. 只能在非静态方法中使用
  2. 在子类方法中,访问父类的成员变量和方法。
    super的其他用法在后文中介绍。

5.this和super

super和this都可以在成员方法中用来访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语句,那他们之间有什么区别呢?
【相同点】

  1. 都是Java中的关键字
  2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
  3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在。
    【不同点】
  4. this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成
    员的引用
    . 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
  5. 在构造方法中:this(…)用于调用本类构造方法,super(…)用于调用父类构造方法,两种调用不能同时在构造
    方法中出现
  6. 构造方法中一定会存在super(…)的调用,用户没有写编译器也会增加,但是this(…)用户不写则没有

6.再谈初始化

Animal类

public class Animal {
    public String name;
    public int age;
    static {
        System.out.println("static()::Animal....");
    }
    {
        System.out.println("Animal实例代码块...");
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Animal构造代码块");
    }

    public void eat(){
        System.out.println(this.name+"正在吃饭....");
    }
}

Cat类

public class Cat extends Animal {
    static {
        System.out.println("static()::Cat....");
    }
    {
        System.out.println("Cat实例代码块...");
    }
    public Cat(String name, int age) {
        super(name,age);
        System.out.println("Cat构造代码块...");
    }
    public void miaomiao(){
        System.out.println(this.name+"正在喵喵叫....");
    }
}

Dog类

public class Dog extends Animal{
    static {
        System.out.println("static()::Dog....");
    }
    {
        System.out.println("Dog实例代码块...");
    }
    public Dog(String name, int age) {
        super(name, age);
        System.out.println("Dog构造代码块");
    }
    public void bark(){
        System.out.println(this.name+"正在汪汪叫....");
    }
}

我们还记得之前讲过的代码块吗?我们简单回顾一下几个重要的代码块:实例代码块和静态代码块。在没有继承关系时的执行顺序。
在这里插入图片描述

7.protected关键字

在这里插入图片描述
在这里插入图片描述
B和D类

public class B {
    private int a;
    protected int b;
    public int c;
    int d;
}
class D extends B{
    public void method(){
        //super.a = 10; // 编译报错,父类private成员在相同包子类中不可见
        super.b = 20; // 父类中protected成员在相同包子类中可以直接访问
        super.c = 30; // 父类中public成员在相同包子类中可以直接访问
        super.d = 40; // 父类中默认访问权限修饰的成员在相同包子类中可以直接访问
    }
}

C类

public class C extends B {
    public void method(){
        // super.a = 10; // 编译报错,父类中private成员在不同包子类中不可见
        super.b = 20; // 父类中protected修饰的成员在不同包子类中可以直接访问
        super.c = 30; // 父类中public修饰的成员在不同包子类中可以直接访问
        //super.d = 40; // 父类中默认访问权限修饰的成员在不同包子类中不能直接访问
    }

}

TestProtect类

public class TestProtect {
    public static void main(String[] args) {
        C c = new C();
        c.method();
        //System.out.println(c.a); // 编译报错,父类中private成员在不同包其他类中不可见
        //System.out.println(c.b); // 父类中protected成员在不同包其他类中不能直接访问
        System.out.println(c.c); // 父类中public成员在不同包其他类中可以直接访问
        //System.out.println(c.d); // 父类中默认访问权限修饰的成员在不同包其他类中不能直接访问
    }
}

注意:父类中private成员变量虽然在子类中不能直接访问,但是也继承到子类中了
什么时候下用哪一种呢?
我们希望类要尽量做到 “封装”, 即隐藏内部实现细节, 只暴露出 必要 的信息给类的调用者.
因此我们在使用的时候应该尽可能的使用 比较严格 的访问权限. 例如如果一个方法能用 private, 就尽量不要用 public.
另外, 还有一种 简单粗暴 的做法: 将所有的字段设为 private, 将所有的方法设为 public. 不过这种方式属于是对访问权限的滥用, 还是更希望同学们能写代码的时候认真思考, 该类提供的字段方法到底给 “谁” 使用(是类内部自己用, 还是类的调用者使用, 还是子类使用。

8.继承方式

在这里插入图片描述
注意:Java中不支持多继承。
时刻牢记, 我们写的类是现实事物的抽象. 而我们真正在公司中所遇到的项目往往业务比较复杂, 可能会涉及到一系列复杂的概念, 都需要我们使用代码来表示, 所以我们真实项目中所写的类也会有很多. 类之间的关系也会更加复杂.
但是即使如此, 我们并不希望类之间的继承层次太复杂. 一般我们不希望出现超过三层的继承关系. 如果继承层次太多, 就需要考虑对代码进行重构了.
如果想从语法上进行限制继承, 就可以使用 final 关键字

9.final 关键字

final关键可以用来修饰变量、成员方法以及类。

9.1.修饰变量

public static void main(String[] args) {
        final int a=10;
        //a=20;
        System.out.println(a);
    }

9.2.修饰类

见下面图

9.3.修饰方法

修饰方法:表示该方法不能被重写(后序介绍)
在这里插入图片描述

10.继承和组合

和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果。组合并没有涉及到特殊的语法
(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段。
继承表示对象之间是is-a的关系,比如:狗是动物,猫是动物
组合表示对象之间是has-a的关系,比如:汽车(也可以是a part of)

组合和继承都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,一般建议:能用组合尽量用组合。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值