• Java 面向对象(四)继承


    一、继承的概述(Inherited)

      1、由来

        多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。

        其中,多个类可以称为 子类(派生类),单独的那一个类称为 父类超类super class)或 基类

       继承描述的是事物之间的所属关系,这种关系是 : is - a 的关系。(子类就是一个父类:子类 is a 父类)

       如上图:兔子属于食草动物,食草动物属于动物。可见,父类是通用,子类更具体。通过继承,可以使多种事物之间形成一种关系体系。

      2、定义

        继承:就是子类继承父类的属性行为。使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。

      3、好处

          a、提高代码的复用性

        b、扩展了新的功能。

        c、类与类之间产生了关系,是多态的前提

    二、继承的格式

        通过 extends 关键字,可以声明一个子类继承另外一个父类。

       定义格式:

    // 定义父类
    class 父类名称 {
    ...
    }
    // 定义子类 [修饰符] class 子类名称 extends 父类名称 { ... }

    三、继承后的特点——成员变量

       当类之间产生了关系后,其中各类中的成员变量会产生影响,分为下面两类来讨论:

      1、成员变量不重名

        如果子类父类中出现不重名的成员变量,这时候的访问是没有影响的。

       Demo:

     1 // 父类
     2 class Fu {
     3     // Fu中的成员变量。
     4     int num = 5;
     5 }
     6 // 子类
     7 class Zi extends Fu {
     8     // Zi中的成员变量
     9     int num2 = 6;
    10     // Zi中的成员方法
    11     public void show() {
    12         // 访问父类中的num,
    13         System.out.println("Fu num="+num); // 本类中没有,继承而来,所以直接访问。
    14         // 访问子类中的num2
    15         System.out.println("Zi num2="+num2);   // 本类中有,访问本类中num2
    16     }
    17 }
    18 // 测试类
    19 class Test{
    20 public static void main(String[] args) {
    21     // 创建子类对象
    22     Zi z = new Zi();
    23     // 调用子类中的show方法
    24      z.show();
    25     }
    26 }
    27 
    28 演示结果:
    29 Fu num = 5
    30 Zi num2 = 6

      2、成员变量重名

        如果子类父类中出现重名的成员变量,这时候的访问是有影响的。

         Demo:

     1 class Fu {
     2     // Fu中的成员变量。
     3     int num = 5;
     4 } 
     5 class Zi extends Fu {
     6     // Zi中的成员变量
     7     int num = 6;
     8     public void show() {
     9         // 访问父类中的num
    10         System.out.println("Fu num=" + num);
    11         // 访问子类中的num
    12         System.out.println("Zi num=" + num);
    13     }
    14 } 
    15 class ExtendsDemo03 {
    16     public static void main(String[] args) {
    17         // 创建子类对象
    18         Zi z = new Zi();
    19         // 调用子类中的show方法
    20         z.show();
    21     }
    22 } 
    23 演示结果:
    24 Fu num = 6
    25 Zi num = 6    

       通过以上Demo可以看出,子父类中出现了同名的成员变量时,在子类中访问父类是访问不到的。

      super 关键字

      在子类中需要访问父类中非私有成员变量时,需要使用 super 关键字,修饰父类成员变量,类似于之前的 this。

      使用格式:

    super.父类成员变量名
    

       将上面的 Demo 中子类方法进行修改:

     1 class Zi extends Fu {
     2   // Zi中的成员变量
     3   int num = 6;
     4   public void show() {
     5     //访问父类中的num
     6     System.out.println("Fu num=" + super.num);
     7     //访问子类中的num
     8     System.out.println("Zi num=" + this.num);
     9   }
    10 } 
    11 演示结果:
    12 Fu num = 5
    13 Zi num = 6

      Tips:父类中的成员变量是非私有的,子类中可以直接访问。若父类中的成员变量私有了,子类是不能直接访问的。通常,遵循封装的原则,使用 private 修饰成员变量 ,那么访问父类的成员变量就需要使用公开的 setter 和 getter 方法。

    四、继承后的特点——成员方法

      当类之间产生了关系,其中各类中的成员方法又会产生影响,下面分两种情况来讨论:

      1、成员方法不重名

        如果子类父类中出现不重名的成员方法,这时调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。

       Demo:

     1 class Fu{
     2   public void show(){
     3     System.out.println("Fu类中的show方法执行");
     4   }
     5 } 
     6 class Zi extends Fu{
     7   public void show2(){
     8     System.out.println("Zi类中的show2方法执行");
     9   }
    10 } 
    11 public class ExtendsDemo04{
    12   public static void main(String[] args) {
    13     Zi z = new Zi();
    14     //子类中没有show方法,但是可以找到父类方法去执行
    15     z.show();
    16     z.show2();
    17   }
    18 }

      2、成员方法重名 —— 重写(Override)

         如果子类父类中出现 重名 的成员方法,这时的访问是一种特殊情况,叫做方法重写(Override)

        方法重写:子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现

        Demo:

     1 class Fu {
     2   public void show() {
     3     System.out.println("Fu show");
     4   }
     5 } 
     6 class Zi extends Fu {
     7   //子类重写了父类的show方法
     8   public void show() {
     9     System.out.println("Zi show");
    10   }
    11 }
    12 public class ExtendsDemo05{
    13   public static void main(String[] args) {
    14     Zi z = new Zi();
    15     // 子类中有show方法,只执行重写后的show方法
    16     z.show(); // Zi show
    17   }
    18 }

         a、重写的应用

            子类可以根据需要,定义特定与自己的行为。既沿袭了父类的功能名称,又根据子类的需要重新实现父类方法,从而进行扩展增强。

          Demo:新手机增加来点显示头像的功能。

     1 class Phone {
     2     public void sendMessage(){
     3         System.out.println("发短信");
     4     }
     5     public void call(){
     6         System.out.println("打电话");
     7     } 
     8     public void showNum(){
     9         System.out.println("来电显示号码");
    10     }
    11 }
    12 //智能手机类
    13 class NewPhone extends Phone {
    14     //重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能
    15     public void showNum(){
    16         //调用父类已经存在的功能使用super
    17         super.showNum();
    18         //增加自己特有显示姓名和图片功能
    19         System.out.println("显示来电姓名");
    20         System.out.println("显示头像");
    21     }
    22 }
    23 
    24 public class ExtendsDemo06 {
    25     public static void main(String[] args) {
    26         // 创建子类对象
    27         NewPhone np = new NewPhone();
    28         // 调用父类继承而来的方法
    29         np.call();
    30         // 调用子类重写的方法
    31         np.showNum();
    32     }
    33 }        
    View Code 

             Tips:这里进行重写时,用到 super.父类成员方法,表示调用父类的成员方法。

            b、注意事项

          (1)子类覆盖父类方法,方法名形参列表必须和父类的一模一样。

          (2)返回值类型:

              基本数据类型和void:要求与父类被重写的方法的返回值类型“相同”。

              引用数据类型:要求子类重写的方法的返回值类型 <= 父类被重写的方法的返回值类型

          (3)修饰符:

              ① 权限修饰符:子类重写的方法的权限修饰符的可见性范围 >= 父类被重写方法的权限修饰符的可见性范围

              (public > protected > (default) > private)

              ② 其他修饰符:不能是 final,private,static

          (4)异常类型:子类重写的方法抛出的异常类型必须<=父类被重写的方法抛出的异常类型。

              父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出。

    五、继承后的特点——构造方法

      当类之间产生了关系,现在来讨论各类中的构造方法的影响。

        构造方法的定义格式和作用:

        1、构造方法的名字和类名一致的,所以子类是无法继承父类构造方法的

        2、构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化工作,父类成员变量初始化后,才可以给子类使用。

        3、子类的构造方法中默认有一个 super() ,默认情况下,表示调用父类的无参构造方法;如果父类没有无参构造,那么在子类的构造方法的首行,必须手动调用父类的有参构造

      Demo:

     1 class Fu {
     2     private int n;
     3     Fu(){
     4         System.out.println("Fu()");
     5     }
     6 }
     7 class Zi extends Fu {
     8     Zi(){
     9         // super(),调用父类构造方法,默认就给提供一个。
    10         super();
    11         System.out.println("Zi()");
    12     }
    13 }
    14 public class ExtendsDemo07{
    15     public static void main (String args[]){
    16         Zi zi = new Zi();
    17     }
    18 }
    19 输出结果:
    20 Fu()
    21 Zi()

        小结:

      •  子类构造方法当中有一个默认隐含的 “super()” 调用,所有一定是先调用的父类构造,后执行的子类构造;
      •     子类构造可以通过super关键字来调用父类重载构造;
      •     super的父类构造调用,必须是子类构造方法的第一个语句。不能一个子类构造调用多次super构造,只能有一个。

    六、super 关键字

      1、super 含义

        表示从父类中去查找,引用父类的属性或方法。

        注意:super只能访问在子类中可见的属性、方法、构造器(非private修饰)

      2、用法

        (1)super.属性

          当子类声明了和父类同名的属性时,可以使用super.属性来访问父类的属性 

        (2)super.方法

          当在子类中需要调用父类被重写的方法时,可以使用super.方法

        (3)super() 或 super(实参列表)

          super():调用父类的无参构造

          super(实参列表):调用父类的有参构造

             注意:

          ① super() 或 super(实参列表) 必须在子类构造器的首行

          ② 如果子类的构造器中,没有写 super(),它也存在;但是如果子类构造器中写super(实参列表),那么super()就不会存在的

    六、super 和 this

      1、父类空间优于子类对象产生

        在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。

       目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。

          代码体现在子类的构造方法调用时,一定先调用父类的构造方法。

       图示:

      2、super 和 this 的含义

      •  super:代表父类的存储空间标识(可以理解为父亲的引用)。
      •    this:代表当前对象的引用(谁调用就代表谁)。

      3、super 和 this 的用法

        a、访问成员

    this.成员变量      ‐‐ 本类的
    super.成员变量     ‐‐ 父类的
    
    this.成员方法名()  ‐‐ 本类的
    super.成员方法名() ‐‐ 父类的
    

          Demo:

     1 class Animal {
     2     public void eat() {
     3         System.out.println("animal : eat");
     4     }
     5 }
     6 
     7 class Cat extends Animal {
     8     public void eat() {
     9         System.out.println("cat : eat");
    10     } 
    11     public void eatTest() {
    12         this.eat(); // this 调用本类的方法
    13         super.eat(); // super 调用父类的方法
    14     }
    15 }
    16 public class ExtendsDemo08 {
    17     public static void main(String[] args) {
    18         Animal a = new Animal();
    19         a.eat();
    20         Cat c = new Cat();
    21         c.eatTest();
    22     }
    23 } 
    24 输出结果为:
    25 animal : eat
    26 cat : eat
    27 animal : eat
    View Code

        b、访问构造方法

    this(...)  ‐‐ 本类的构造方法
    super(...) ‐‐ 父类的构造方法
    

        注意:子类的每个构造方法中均有默认的 super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()

          super() this() 都必须是在构造方法的第一行,所以不能同时出现

    七、继承的特点

      1、子类继承了父类,子类不能直接访问父类中私有的属性、方法。可以通过 setter/getter 方法来调用。

      2、子类继承父类时,构造器是不能被继承的。

      3、子类继承父类时,在子类的构造器中一定要去调用父类的构造器。

         默认情况下,调用的是父类的无参构造;如果父类没有无参构造,那么在子类的构造器的首行,必须手动调用父类的有参构造。

      4、Java 只支持单继承,即一个 Java 类只能有一个直接父类。

        Demo:

    1 //一个类只能有一个父类,不可以有多个父类。
    2 class C extends A{}         //ok
    3 class C extends A,B...     //error

      5、Java 支持多层继承(继承体系)。

        Demo:

    1 class A{}
    2 class B extends A{}
    3 class C extends B{}

       扩展:顶层父类是 Object 类,所有的类默认继承 Object,作为父类。

      6、子类和父类是一种相对的概念。

    图示:

  • 相关阅读:
    java mybatis
    java influx DB工具类
    java redisUtils工具类很全
    java 任务定时调度(定时器)
    java 线程并发(生产者、消费者模式)
    java 线程同步、死锁
    Redis 集群版
    Redis 单机版
    linux下配置zookeeper注册中心及运行dubbo服务
    vue搭建前端相关命令
  • 原文地址:https://www.cnblogs.com/niujifei/p/11354812.html
Copyright © 2020-2023  润新知