Java中继承机制详解

继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称为派生类,继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。

一、继承的引入

1、用例子进行简单的说明。

    首先定义一个Dog类和Cat类

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

    public void init(String name, String gender, int age, String color){
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.color = color;
    }

    public void eat(){
        System.out.println(name + "在吃狗粮");
    }

    public void sleep(){
        System.out.println(name + "在睡觉");
    }

    public void bark(){
        System.out.println(name + "在汪汪汪");
    }

}
public class Cat {
    public String name;
    public String gender;
    public int age;
    public String temper;

    public void init(String name,String gender,int age,String temper){
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.temper = temper;
    }

    public void eat(){
        System.out.println(name + "在吃猫粮");
    }

    public void sleep(){
        System.out.println(name + "在睡觉");
    }

    public void mew(){
        System.out.println(name + "在喵喵叫");
    }

}

  输入一些例子然后输出

public class TestPet {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.init("可乐","公",1,"黑白");//有个叫可乐的狗狗
        dog.eat();
        dog.sleep();
        dog.bark();

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

        Cat cat = new Cat();
        cat.init("辣条","公",1,"温顺");//有个叫辣条的喵咪
        cat.eat();
        cat.sleep();
        cat.mew();
    }
}

  输出结果为:

  由以上代码我们可以发现:Dog和Cat类大部分的实现都是相同的,在Dog和Cat类中出现了重复代码,每次有新的宠物出现,就需要添加对应的类,这些重复的字段和方法又会重复出现,导致做一些重复的事情,代码的可读性也不高

  狗狗猫咪都有名字,性别,年龄,都需要吃、睡,这些属性和方法是所有动物都具有的——大家都有的方法和属性提取到一个类中单独实现。

2、以下代码用到继承使代码可读性提高

public class Animal { //基类、父类、超类
    public String name;
    public String gender;
    public int age;

    public void init(String name,String gender,int age){
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

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

    public void sleep(){
        System.out.println(name + "在睡觉");
    }

}
public class Dog2 extends Animal{//派生类、子类
    public String color;

    public void bark(){
        System.out.println(name + "在汪汪汪");
    }

}
public class Cat2 extends Animal{//派生类、子类
    //是对基类Animal进行扩增
    public String temper;

    public void mew(){
        System.out.println(name + "在喵喵叫");
    }
}

   Dog2和Cat2都继承了Animal类,其中Animal称为父类/基类/超类,Dog2和Cat2被称为Animal的子类/派生类,继承之后,子类可以复用父类中的成员,子类在实现时只需要关心自己新增加的成员即可。

  从继承概念中可以看出继承最大的作用是:实现代码复用,实现多态。

二、继承的语法

  1、在Java中如果想表示类之间的继承关系,需要借助extends关键字。

注意:1)子类会将父类中的成员变量或者成员方法集成到子类中

           2)子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同

  2、父类成员访问

    子类中访问父类的成员变量

 注意:子类和基类可以存在相同名称的成员变量,并且类型和名字都可以完全相同,此时,在子类的方法中优先访问到的是子类的同名成员,从基类中继承下来的同名成员,不能在子类中被直接访问,称为同名隐藏(在方法中无法直接访问到基类相同名称的成员,直接访问的是子类自己新增的成员)

① 在子类方法中永远都无法访问基类中相同名称的成员了吗?

    方法一:可以在基类中提供对应的方法来操作

    方法二:可以借助super关键字来操作

建议:一般情况下,在继承体系中尽量避免设计名字相同的成员变量

②在子类方法中或者通过子类对象访问成员时:

   如果访问的成员变量子类中有,优先访问自己的成员变量

  ·如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错

成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。

③在子类构造方法中并没有写任何关于基类的构造方法的代码,但是在构造子类对象时,先执行基类的构造方法,然后执行子类的构造方法,因为子类对象中的成员是有两部分组成的,基类继承下来的以及子类新增加的部分,所以在构造子类对象的时候,先要调用基类的构造方法,将从基类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整。

注意:

①若父类显示定义无参或者默认的构造方法,在子类构造方法第一行默认含有super()调用,即调用基类构造方法

②如果父类构造方法是带有参数的,此时需要用户为子类显示定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败

③在子类构造方法中,super()调用父类构造时,必须是子类构造中的第一条语句

super()只能在子类构造方法中出现一次,并且不能和this同时出现

   3、super关键字

      由于设计不好,或者因为场景需要,子类和父类中会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,直接访问时无法做到的,这时Java提供了super关键字,它的主要作用是:在子类方法中访问父类的成员

     以下代码加深理解

public class Base {
    public int a;
    public int b;

    public Base(int a, int b) {
        this.a = a;
        this.b = b;
    }
}
public class Derived extends Base{
    public int c;

    //子类构造方法
    public Derived(int a, int b, int c) {
        super(a, b);
        this.c = c;
    }

    public static void main(String[] args) {
        Derived derived = new Derived(1,2,3);
        derived.a = 10;
        derived.b = 20;
        derived.c = 30;
    }
}

 在子类中,如果想要明确访问父类中成员时,可以借用super关键字

注意:1)super只能在非静态方法中使用

           2)在子类方法中,访问父类的成员变量和方法

4、子类构造方法

    子类对象构造时,先调用基类构造方法,然后执行子类的构造方法

public class Base {
    public int a;
    public int b;

    public Base(int a, int b) {
        this.a = a;
        this.b = b;
    }
}
public class Derived extends Base{
    public int c;

    //子类构造方法
    public Derived(int a, int b, int c) {
        super(a, b);
        this.c = c;
    }

    public static void main(String[] args) {
        Derived derived = new Derived(1,2,3);
        derived.a = 10;
        derived.b = 20;
        derived.c = 30;
    }
}

 结论:

1)如果基类没有显式定义任何构造方法,则子类可以定义也可以不定义,子类如果需要构造方法就定义,不需要就不定义,此时编译器会给基类和子类都生成无参的构造方法

  注意:此时创建对象的时候就不能带有参数

2)如果基类显式定义了构造方法,但是基类的构造方法时无参的,此时子类的构造方法可以定义,也可以不用定义,根据自己的需求

3)如果基类显式定义了带有参数的构造方法,此时子类必须显式定义自己的构造方法,否则编译报错

注意:当编译器在编译子类的构造方法的时候,会在子类构造方法中新增加一条语句,调用基类的构造方法,只能是调用基类的无参构造方法,编译器无法调用基类中带有参数的构造方法,因为带有参数的构造方法需要传参,编译器调用的时候是不知道传递什么参数的。

注意:父类中private成员变量虽然在子类中不能直接访问,但是也继承到子类中

三、继承方式

1、单继承

public class A{
   ......
}
public class B extends A{
   ......
}

2、多层继承

public class A{......}
public class B extends A{......}
public class C extends B{......}

3、不同类继承同一个类(两个孩子一个爹)

public class A{......}
public class B extends A{......}
public class C extends A{......}

4、不支持多继承(不支持一个孩子两个爹) 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

薰衣草2333

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

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

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

打赏作者

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

抵扣说明:

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

余额充值