乐趣区

关于后端:Java的继承

1 概述

1.1 引入

如果咱们要定义如下类:
学生类, 老师类和工人类,剖析如下。

  1. 学生类
    属性: 姓名, 年龄
    行为: 吃饭, 睡觉
  2. 老师类
    属性: 姓名, 年龄,薪水
    行为: 吃饭, 睡觉,教书
  3. 班主任
    属性: 姓名, 年龄,薪水
    行为: 吃饭, 睡觉,治理

如果咱们定义了这三个类去开发一个零碎,那么这三个类中就存在大量反复的信息(属性: 姓名,年龄。行为:吃饭,睡觉)。这样就导致了雷同代码大量反复,代码显得很臃肿和冗余,那么如何解决呢?

如果多个类中存在雷同属性和行为时,咱们能够将这些内容抽取到独自一个类中,那么多个类无需再定义这些属性和行为,只有 继承 那一个类即可。如图所示:

其中,多个类能够称为 子类 ,独自被继承的那一个类称为 父类 超类(superclass)或者 基类

1.2 继承的含意

继承形容的是事物之间的所属关系,这种关系是:is-a 的关系。例如,兔子属于食草动物,食草动物属于动物。可见,父类更通用,子类更具体。咱们通过继承,能够使多种事物之间造成一种关系体系。

继承 :就是子类继承父类的 属性 行为 ,使得子类对象能够间接具备与父类雷同的属性、雷同的行为。子类能够间接拜访父类中的 非公有 的属性和行为。

1.3 继承的益处

  1. 进步 代码的复用性(缩小代码冗余,雷同代码反复利用)。
  2. 使类与类之间产生了关系。

2 继承的格局

通过 extends 关键字,能够申明一个子类继承另外一个父类,定义格局如下:

class 父类 {...}

class 子类 extends 父类 {...}

须要留神:Java 是单继承的,一个类只能继承一个间接父类,跟事实世界很像,然而 Java 中的子类是更加弱小的。

3 继承案例

3.1 案例

请应用继承定义以下类:

  1. 学生类
    属性: 姓名, 年龄
    行为: 吃饭, 睡觉
  2. 老师类
    属性: 姓名, 年龄,薪水
    行为: 吃饭, 睡觉,教书
  3. 班主任
    属性: 姓名, 年龄,薪水
    行为: 吃饭, 睡觉,治理

3.2 案例图解剖析

老师类,学生类,还有班主任类,实际上都是属于人类的,咱们能够定义一个人类,把他们雷同的属性和行为都定义在人类中,而后继承人类即可,子类特有的属性和行为就定义在子类中了。

如下图所示。

3.3 案例代码实现

1. 父类 Human 类

 public class Human {
  // 正当暗藏
  private String name ;
  private int age ;
  
  // 正当裸露
  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;}
 }

2. 子类 Teacher 类

public class Teacher extends Human {
  // 工资
  private double salary ;
  
  // 特有办法
  public void teach(){System.out.println("老师在认真教技术!");}

  public double getSalary() {return salary;}

  public void setSalary(double salary) {this.salary = salary;}
}

3. 子类 Student 类

public class Student extends Human{}

4. 子类 BanZhuren 类

public class Teacher extends Human {
    // 工资
    private double salary ;
    
       // 特有办法
    public void admin(){System.out.println("班主任强调纪律问题!");}
    
    public double getSalary() {return salary;}

    public void setSalary(double salary) {this.salary = salary;}
}

5. 测试类

public class Test {public static void main(String[] args) {Teacher dlei = new Teacher();
        dlei.setName("播仔");
        dlei.setAge("31");
        dlei.setSalary(1000.99);
        System.out.println(dlei.getName());
        System.out.println(dlei.getAge());
        System.out.println(dlei.getSalary());
        dlei.teach();
        
        BanZhuRen linTao = new BanZhuRen();
        linTao.setName("灵涛");
        linTao.setAge("28");
        linTao.setSalary(1000.99);
        System.out.println(linTao.getName());
        System.out.println(linTao.getAge());
        System.out.println(linTao.getSalary());
        linTao.admin();

        Student xugan = new Student();
        xugan.setName("播仔");
        xugan.setAge("31");
        //xugan.setSalary(1000.99); // xugan 没有薪水属性,报错!System.out.println(xugan.getName());
        System.out.println(xugan.getAge());



    }
}

3.4 小结

1. 继承实际上是子类雷同的属性和行为能够定义在父类中,子类特有的属性和行为由本人定义,这样就实现了雷同属性和行为的反复利用,从而进步了代码复用。

2. 子类继承父类,就能够间接失去父类的成员变量和办法。是否能够继承所有成分呢?请看下节!

4 子类不能继承的内容

4.1 引入

并不是父类的所有内容都能够给子类继承的:

子类不能继承父类的构造方法。

值得注意的是子类能够继承父类的公有成员(成员变量,办法),只是子类无奈间接拜访而已,能够通过 getter/setter 办法拜访父类的 private 成员变量。

4.1 演示代码

public class Demo03 {public static void main(String[] args) {Zi z = new Zi();
        System.out.println(z.num1);
//        System.out.println(z.num2); // 公有的子类无奈应用
        // 通过 getter/setter 办法拜访父类的 private 成员变量
        System.out.println(z.getNum2());

        z.show1();
        // z.show2(); // 公有的子类无奈应用}
}

class Fu {
    public int num1 = 10;
    private int num2 = 20;

    public void show1() {System.out.println("show1");
    }

    private void show2() {System.out.println("show2");
    }

    public int getNum2() {return num2;}

    public void setNum2(int num2) {this.num2 = num2;}
}

class Zi extends Fu {}

5 继承后的特点—成员变量

当类之间产生了继承关系后,其中各类中的成员变量,又产生了哪些影响呢?

5.1 成员变量不重名

如果子类父类中呈现 不重名 的成员变量,这时的拜访是 没有影响的。代码如下:

class Fu {
    // Fu 中的成员变量
    int num = 5;
}
class Zi extends Fu {
    // Zi 中的成员变量
    int num2 = 6;
  
    // Zi 中的成员办法
    public void show() {
        // 拜访父类中的 num
        System.out.println("Fu num="+num); // 继承而来,所以间接拜访。// 拜访子类中的 num2
        System.out.println("Zi num2="+num2);
    }
}
class Demo04 {public static void main(String[] args) {
        // 创立子类对象
        Zi z = new Zi(); 
          // 调用子类中的 show 办法
        z.show();}
}

演示后果:Fu num = 5
Zi num2 = 6

5.2 成员变量重名

如果子类父类中呈现 重名 的成员变量,这时的拜访是 有影响的。代码如下:

class Fu1 {
    // Fu 中的成员变量。int num = 5;
}
class Zi1 extends Fu1 {
    // Zi 中的成员变量
    int num = 6;
  
    public void show() {
        // 拜访父类中的 num
        System.out.println("Fu num=" + num);
        // 拜访子类中的 num
        System.out.println("Zi num=" + num);
    }
}
class Demo04 {public static void main(String[] args) {
          // 创立子类对象
        Zi1 z = new Zi1(); 
          // 调用子类中的 show 办法
        z1.show();}
}
演示后果:Fu num = 6
Zi num = 6

子父类中呈现了同名的成员变量时,子类会优先拜访本人对象中的成员变量。如果此时想拜访父类成员变量如何解决呢?咱们能够应用 super 关键字。

5.3 super 拜访父类成员变量

子父类中呈现了同名的成员变量时,在子类中须要拜访父类中非公有成员变量时,须要应用super 关键字,润饰父类成员变量,相似于之前学过的 this

须要留神的是:super 代表的是父类对象的援用,this 代表的是以后对象的援用。

应用格局:

super. 父类成员变量名

子类办法须要批改,代码如下:

class Fu {
    // Fu 中的成员变量。int num = 5;
}

class Zi extends Fu {
    // Zi 中的成员变量
    int num = 6;
  
    public void show() {
        int num = 1;
      
        // 拜访办法中的 num
        System.out.println("method num=" + num);
        // 拜访子类中的 num
        System.out.println("Zi num=" + this.num);
        // 拜访父类中的 num
        System.out.println("Fu num=" + super.num);
    }
}

class Demo04 {public static void main(String[] args) {
          // 创立子类对象
        Zi1 z = new Zi1(); 
          // 调用子类中的 show 办法
        z1.show();}
}

演示后果:method num=1
Zi num=6
Fu num=5

小贴士:Fu 类中的成员变量是非公有的,子类中能够间接拜访。若 Fu 类中的成员变量公有了,子类是不能间接拜访的。通常编码时,咱们遵循封装的准则,应用 private 润饰成员变量,那么如何拜访父类的公有成员变量呢?对!能够在父类中提供公共的 getXxx 办法和 setXxx 办法。

6 继承后的特点—成员办法

当类之间产生了关系,其中各类中的成员办法,又产生了哪些影响呢?

6.1 成员办法不重名

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

class Fu {public void show() {System.out.println("Fu 类中的 show 办法执行");
    }
}
class Zi extends Fu {public void show2() {System.out.println("Zi 类中的 show2 办法执行");
    }
}
public  class Demo05 {public static void main(String[] args) {Zi z = new Zi();
         // 子类中没有 show 办法,然而能够找到父类办法去执行
        z.show(); 
        z.show2();}
}

6.2 成员办法重名

如果子类父类中呈现 重名 的成员办法,则创立子类对象调用该办法的时候,子类对象会优先调用本人的办法。

代码如下:

class Fu {public void show() {System.out.println("Fu show");
    }
}
class Zi extends Fu {
    // 子类重写了父类的 show 办法
    public void show() {System.out.println("Zi show");
    }
}
public class ExtendsDemo05{public static void main(String[] args) {Zi z = new Zi();
         // 子类中有 show 办法,只执行重写后的 show 办法
        z.show();  // Zi show}
}

7 办法重写

7.1 概念

办法重写 :子类中呈现与父类截然不同的办法时(返回值类型,办法名和参数列表都雷同),会呈现笼罩成果,也称为重写或者复写。 申明不变,从新实现

7.2 应用场景与案例

产生在子父类之间的关系。
子类继承了父类的办法,然而子类感觉父类的这办法不足以满足本人的需要,子类从新写了一个与父类同名的办法,以便笼罩父类的该方 法。

例如:咱们定义了一个动物类代码如下:

public class Animal  {public void run(){System.out.println("动物跑的很快!");
    }
    public void cry(){System.out.println("动物都能够叫~~~");
    }
}

而后定义一个猫类,猫可能认为父类 cry()办法不能满足本人的需要

代码如下:

public class Cat extends Animal {public void cry(){System.out.println("咱们一起学猫叫,喵喵喵!喵的十分好听!");
    }
}

public class Test {public static void main(String[] args) {
          // 创立子类对象
          Cat ddm = new Cat();// 调用父类继承而来的办法
        ddm.run();
          // 调用子类重写的办法
          ddm.cry();}
}

7.2 @Override 重写注解

  • @Override: 注解,重写注解校验!
  • 这个注解标记的办法,就阐明这个办法必须是重写父类的办法,否则编译阶段报错。
  • 倡议重写都加上这个注解,一方面能够进步代码的可读性,一方面能够避免重写出错!

    加上后的子类代码模式如下:

    public class Cat extends Animal {
         // 申明不变,从新实现
        // 办法名称与父类全副一样,只是办法体中的性能重写写了!@Override
        public void cry(){System.out.println("咱们一起学猫叫,喵喵喵!喵的十分好听!");
        }
    }

7.3 注意事项

  1. 办法重写是产生在子父类之间的关系。
  2. 子类办法笼罩父类办法,必须要保障权限大于等于父类权限。
  3. 子类办法笼罩父类办法,返回值类型、函数名和参数列表都要截然不同。

8 继承后的特点—构造方法

8.1 引入

当类之间产生了关系,其中各类中的构造方法,又产生了哪些影响呢?
首先咱们要回顾两个事件,构造方法的定义格局和作用。

  1. 构造方法的名字是与类名统一的。所以子类是无奈继承父类构造方法的。
  2. 构造方法的作用是初始化对象成员变量数据的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个 super(),示意调用父类的构造方法,父类成员变量初始化后,才能够给子类应用。( 先有爸爸,能力有儿子

继承后子类构办法器特点: 子类所有构造方法的第一行都会默认先调用父类的无参构造方法

8.2 案例演示

按如下需要定义类:

  1. 人类
    成员变量: 姓名, 年龄
    成员办法: 吃饭
  2. 学生类
    成员变量: 姓名, 年龄, 问题
    成员办法: 吃饭

代码如下:

class Person {
    private String name;
    private int age;

    public Person() {System.out.println("父类无参");
    }

    // getter/setter 省略
}

class Student extends Person {
    private double score;

    public Student() {//super(); // 调用父类无参, 默认就存在,能够不写,必须再第一行
        System.out.println("子类无参");
    }
    
     public Student(double score) {//super();  // 调用父类无参, 默认就存在,能够不写,必须再第一行
        this.score = score;    
        System.out.println("子类有参");
     }

}

public class Demo07 {public static void main(String[] args) {Student s1 = new Student();
        System.out.println("----------");
        Student s2 = new Student(99.9);
    }
}

输入后果:父类无参
子类无参
----------
父类无参
子类有参

8.3 小结

  • 子类构造方法执行的时候,都会在第一行默认先调用父类无参数构造方法一次。
  • 子类构造方法的第一行都隐含了一个 super() 去调用父类无参数构造方法,super()能够省略不写。

9 super(…)和 this(…)

9.1 引入

请看上节中的如下案例:

class Person {
    private String name;
    private int age;

    public Person() {System.out.println("父类无参");
    }

    // getter/setter 省略
}

class Student extends Person {
    private double score;

    public Student() {//super(); // 调用父类无参构造方法, 默认就存在,能够不写,必须再第一行
        System.out.println("子类无参");
    }
    
     public Student(double score) {//super();  // 调用父类无参构造方法, 默认就存在,能够不写,必须再第一行
        this.score = score;    
        System.out.println("子类有参");
     }
      // getter/setter 省略
}

public class Demo07 {public static void main(String[] args) {
        // 调用子类有参数构造方法
        Student s2 = new Student(99.9);
        System.out.println(s2.getScore()); // 99.9
        System.out.println(s2.getName()); // 输入 null
        System.out.println(s2.getAge()); // 输入 0
    }
}

咱们发现,子类有参数构造方法只是初始化了本人对象中的成员变量 score,而父类中的成员变量 name 和 age 仍然是没有数据的,怎么解决这个问题呢,咱们能够借助与 super(…)去调用父类构造方法,以便初始化继承自父类对象的 name 和 age.

9.2 super 和 this 的用法格局

super 和 this 残缺的用法如下,其中 this,super 拜访成员咱们曾经接触过了。

this. 成员变量        --    本类的
super. 成员变量        --    父类的

this. 成员办法名()      --    本类的    
super. 成员办法名()   --    父类的

接下来咱们应用调用构造方法格局:

super(...) -- 调用父类的构造方法,依据参数匹配确认
this(...) -- 调用本类的其余构造方法,依据参数匹配确认

9.3 super(….)用法演示

代码如下:

class Person {
    private String name ="凤姐";
    private int age = 20;

    public Person() {System.out.println("父类无参");
    }
    
    public Person(String name , int age){
        this.name = name ;
        this.age = age ;
    }

    // getter/setter 省略
}

class Student extends Person {
    private double score = 100;

    public Student() {//super(); // 调用父类无参构造方法, 默认就存在,能够不写,必须再第一行
        System.out.println("子类无参");
    }
    
     public Student(String name,int age,double score) {super(name ,age);// 调用父类有参构造方法 Person(String name , int age)初始化 name 和 age
        this.score = score;    
        System.out.println("子类有参");
     }
      // getter/setter 省略
}

public class Demo07 {public static void main(String[] args) {
        // 调用子类有参数构造方法
        Student s2 = new Student("张三",20,99);
        System.out.println(s2.getScore()); // 99
        System.out.println(s2.getName()); // 输入 张三
        System.out.println(s2.getAge()); // 输入 20
    }
}

留神:

子类的每个构造方法中均有默认的 super(),调用父类的空参结构。手动调用父类结构会笼罩默认的 super()。

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

super(..)是依据参数去确定调用父类哪个构造方法的。

9.4 super(…)案例图解

父类空间优先于子类对象产生

在每次创立子类对象时,先初始化父类空间,再创立其子类对象自身。目标在于子类对象中蕴含了其对应的父类空间,便能够蕴含其父类的成员,如果父类成员非 private 润饰,则子类能够随便应用父类成员。代码体现在子类的结构七调用时,肯定先调用父类的构造方法。了解图解如下:

9.5 this(…)用法演示

this(…)

  • 默认是去找本类中的其余构造方法,依据参数来确定具体调用哪一个构造方法。
  • 为了借用其余构造方法的性能。
package com.itheima._08this 和 super 调用构造方法;
/**
 * this(...):
 *    默认是去找本类中的其余构造方法,依据参数来确定具体调用哪一个构造方法。*    为了借用其余构造方法的性能。*
 */
public class ThisDemo01 {public static void main(String[] args) {Student xuGan = new Student();
        System.out.println(xuGan.getName()); // 输入: 徐干
        System.out.println(xuGan.getAge());// 输入:21
        System.out.println(xuGan.getSex());// 输入:男
    }
}

class Student{
    private String name ;
    private int age ;
    private char sex ;

    public Student() {// 很弱,我的兄弟很牛逼啊,我能够调用其余构造方法:Student(String name, int age, char sex)
        this("徐干",21,'男');
    }

    public Student(String name, int age, char sex) {
        this.name = name ;
        this.age = age   ;
        this.sex = 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 char getSex() {return sex;}

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

9.6 小结

  • 子类的每个构造方法中均有默认的 super(),调用父类的空参结构。手动调用父类结构会笼罩默认的 super()。
  • super() 和 this() 都必须是在构造方法的第一行,所以不能同时呈现。
  • super(..)和 this(…)是依据参数去确定调用父类哪个构造方法的。
  • super(..)能够调用父类构造方法初始化继承自父类的成员变量的数据。
  • this(..)能够调用本类中的其余构造方法。

10 继承的特点

  1. Java 只反对单继承,不反对多继承。

    // 一个类只能有一个父类,不能够有多个父类。class A {}
    class B {}
    class C1 extends A {} // ok
    // class C2 extends A, B {} // error
  2. 一个类能够有多个子类。

    // A 能够有多个子类
    class A {}
    class C1 extends A {}
    class C2 extends  A {}
  3. 能够多层继承。

    class A {}
    class C1 extends A {}
    class D extends C1 {}

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

退出移动版