继承(Java)

1. 继承的概述

1.1 继承的定义

继承机制:简单来说,就是子类继承父类的属性和行为,使得子类对象具有父类相同的属性和行为(除构造方法外,全部继承);(举例说明:相当于徒弟拜师学艺,师傅将自己的武功进行传授,这样,徒弟就继承了与师傅相同的武功,当然徒弟也可以对所学武功进行扩展);它是面向对象程序设计中代码复用采取的重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,以产生新的类,产生的新类又称为派生类,同时继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程;

注意:上述的父类又称为超类或者基类,而子类又称为派生类

1.2 继承的作用

(1)通过继承来抽取类之间的共性部分,以实现代码的复用,提高程序的开发效率;

(2)通过继承实现多态(后面讲);

1.3 继承的格式

通过extends关键字来声明一个类是继承于另一个类中的,形式如下

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

需要注意的是:

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

2. 继承之后的成员变量

2.1 成员变量与父类不重名

当子类与父类中存在不重名的成员变量时,对访问没有任何影响,代码如下所示:

(1)定义一个Base

public class Base {
    int a;
    int b;
}

(2)再定义一个Derived类,用来继承Base

public class Derived extends Base {
    int c;
    int d;

    public void method() {
        a=10; // 访问从父类中继承下来的a
        b=20; // 访问从父类中继承下来的b
        c=30; // 访问子类自己的c
        d=40; // 访问子类自己的d
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
        System.out.println(d);
    }

    public static void main(String[] args) {
      //创建子类对象d
        Derived d=new Derived();
     //调用子类方法
        d.method();
    }
}
输出结果:
10
20
30
40

2.2 成员变量与父类重名

当子类与父类中存在不重名的成员变量时,对访问是有影响的,代码如下所示:

(1)定义一个Base

public class Base {
    int a=10;
    int b=20;

}

(2)再定义一个Derived类,用来继承Base

public class Derived extends Base {
    int a;
    byte b;
    int c;
    int d;

    public void method() {
        a=100; // 优先访问自己的成员变量a
        b=110; // 优先访问自己的成员变量b
        c=120; // 优先访问自己的成员变量c
        d=130; //  优先访问自己的成员变量d
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
        System.out.println(d);
        System.out.println("=======================");
        System.out.println(super.a);
        System.out.println(super.b);
        System.out.println(this.c);
        System.out.println(this.d);
    }

    public static void main(String[] args) {
        Derived d=new Derived();
        d.method();
    }
}
输出结果:
100
110
120
130
=======================
10
20
120
130


由上可以知道,当子类中出现了与父类同名的成员变量时,访问时,子类会优先访问自己的成员变量(与变量的类型无关);换言之就是它会把与父类同名的成员隐藏了,若要访问,有以下两种方式

(1)使用Set()Get()方法

使用方式:在父类中产生Set()Get()方法,然后在基类中直接调用即可;

(2)使用super关键字;

使用格式super.父类成员名

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

3. 继承之后的成员方法

3.1 成员方法不重名

同样的,当子类与父类的成员方法不重名时,这时对调用是没有任何影响的,也就是说,通过子类对象访问父类与子类不同名的方法时,会优先在子类中找,能找到则访问,否则在父类中找,找到就访问,否则编译报错;代码如下:

(1)定义一个Fu

public class Fu {
//定义methodA方法
    public void methodA(){
        System.out.println("Fu----methodA()");
    }
}

(2) 再定义一个Zi类,用来继承Fu

public class Zi extends Fu{
//创建一个子类方法 methodB
    public void methodB(){
        System.out.println("Zi---methodB()");
    }

    public static void main(String[] args) {
    //创建子类对象
        Zi zi=new Zi();
        //调用子类方法
        zi.methodA(); //调用子类继承下来的A方法
        zi.methodB();//调用子类自己的方法B

    }
}
输出结果:
Fu----methodA()
Zi---methodB()

3.2 成员方法重名(重载/重写)

(1)当方法名相同,参数列表不同时,就构成了我们之前所学的重载(即:方法的重载)
(2)当方法名相同,参数列表相同时,则形成了重写

代码如下:
(1)定义一个Base

public class Base {
    public void methodA(){
        System.out.println("我是父类方法A");
    }
    public  void methodB(){
        System.out.println("我是父类方法B");
    }
}

(2)再定义一个Derived类,用来继承Base

public class Derived extends Base {
    //与基类参数列表不同
    public void methodA(int a){
        System.out.println("我是子类带形参的方法A");
    }
      //与基类原形一致
    public void methodB(){
        System.out.println("我是子类方法B");
    }

    //  子类新增方法C
    public void methodC(){
        System.out.println("我是子类方法C");
    }

    public void testMethod(){
       methodA(10);    // 访问子类自己的
       methodA();  //  访问基类继承的

       methodB();  //  访问子类自己的
       super.methodB();  // 访问基类继承的

       methodC();  //  访问子类自己新增的

    }

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

输出结果:
我是子类带形参的方法A
我是父类方法A
我是子类方法B
我是父类方法B
我是子类方法C

上述代码中,基类的方法A与子类的方法A就构成了重载,(1) 当传入参数值时,就会访问带形参的子类中的A方法;(2) 当没有传入参数值时,首先会优先访问子类自己的方法A,但是没有找到,所以访问基类的方法A;

而不同的是,上述的代码中,子类的方法B就与基类中的方法B构成了重写,因为原型一模一样,这样,当访问与基类重名的B方法时,优先访问子类自己的,同时会将基类中重名的方法屏蔽掉,要想访问,就必需要使用super关键字;

4. 继承之后的构造方法

在继承体系中,子类对象构造时:

创建那个类的对象,就会调用那个类的构造方法

仔细来说,就是当创建子类对象时,就会调用子类的构造方法,但在子类构造方法的第一行会默认的有隐含的super()调用,即告诉编译器去调用基类的构造方法;(很重要呢调试代码会看的更清楚哦),所以看到的结果如下,并不是先调用父类的构造方法,再调用子类的构造方法;

代码如下:

(1)定义一个Fu

public class Fu {
    int a;
    int b;
   public  Fu(){
       System.out.println("我是父类构造方法");
   }
}

(2)定义一个Zi

public class Zi extends Fu{
	int c;
	int d;
    public Zi(){
   // super()
        System.out.println("我是子类构造方法");
    }

    public static void main(String[] args) {
        Zi zi=new Zi();
    }
}

输出结果:
我是父类构造方法
我是子类构造方法

可以从对象模型来进行讨论:

在这里插入图片描述

所以说,子类对象其实就是一个父类对象,子类对象和父类对象是is-a的关系,因为,前半部分是一样的;在构造子类对象时,先要将从父类继承下来的成员初始化完成后,然后再初始化子类自己新增加的成员;

需要注意的是

(1) 若父类显式定义无参或者默认的构造方法,此时,子类根据是否需要选择定义构造方法

(2) 如果父类提供的构造方法是带有参数的,此时编译器不会再给子类生成默认的构造方法,此时用户必须要为子类显式定义构造方法
原因:因为子类要在其构造方法中通过super调用基类的构造方法来完成子类对象从基类继承下来的成员的构造;(有点绕,但很重要,如上图所画的)

(3) 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句
(4) super(...)只能在子类构造方法中出现一次,并且不能和this同时出现

5. super 与 this

super  不能在静态方法中使用
      用来访问父类的成员变量和方法

5.1 super 与 this区别

相同点

(1) 都是Java中的关键字;
(2)只能在类的非静态方法中使用,用来访问非静态成员方法和字段;
(3)在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在-----------------就会导致子类对象从父类继承下来的成员被多次构造;

不同点

(1)this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是父类对象的引用

(2) 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性

(3) this是非静态成员方法的一个隐藏参数,super不是隐藏的参数

(4) 成员方法中直接访问本类成员时,编译之后会将this还原,即本类非静态成员都是通过this来访问的;在子类中如果通过super访问父类成员,编译之后在字节码层面super实际是不存在的(通过字节码文件可以验证)

(5) 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有

6. 继承之后初始化问题

接下来,我们来看一下继承关系下的初始化问题,

如下代码所示

(1)定义一个Person

public class Person {
    public Person(){
        System.out.println("我是父类构造方法");
    }

    //实例代码块
    {
        System.out.println("我是父类实例代码块");
    }

    //静态代码块
    static{
        System.out.println("我是父类静态代码块");
    }
}

(2)定义一个Student类,用来继承Person

public class Student extends Person{
    public Student(){
        System.out.println("我是子类构造方法");
}
     //实例代码块
    {
        System.out.println("我是子类实例代码块");
    }

    //静态代码块
    static{
        System.out.println("我是子类静态代码块");
    }

    public static void main(String[] args) {
        Student s1=new Student();
        System.out.println("====================");
        Student s2=new Student();
    }
 }
 
输出结果:

我是父类静态代码块
我是子类静态代码块
我是父类实例代码块
我是父类构造方法
我是子类实例代码块
我是子类构造方法
====================
我是父类实例代码块
我是父类构造方法
我是子类实例代码块
我是子类构造方法

由于静态代码块是在类加载阶段完成的,因此先执行,而继承体系下,子类要依靠父类,所以父类执行完成后就执行子类的静态代码块,而实例代码块是被拷贝到构造方法之前的,所以优于构造方法执行;

7. 继承的方式

在Java中,支持的继承方式有以下三种:

(1)单继承

在这里插入图片描述

(2)多层继承
在这里插入图片描述

(3)不同类继承同一个类
在这里插入图片描述

(4)多继承(不支持)
在这里插入图片描述
注意:这种继承方式是不支持的;同时,一般不希望出现超过三层的继承关系;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值