# 概述

继承是 Java 三大特性之一,它描述了事务之间的所属关系,是从已有的类中构建出新类,已有的类称为父类,新的类称为子类,子类具有父类公有的属性和方法,并能扩展新的属性和方法(子类不会继承父类私有的属性和方法


# 继承的使用

  • 格式:使用 extends 关键字

    class person{}
    class student extends person{}

# 继承的特点

  • 提高代码的复用性

  • 提高开发效率

  • 让类和类之间产生联系,提供了多态的前提

  • 所有类都是 java.lang.Object 类的子类

  • 在 java 中,只支持单继承,不支持多重继承,一个子类只能有一个直接的父类

  • 支持多层继承,一个类的父类可以去继承其他类,那么这个父类的子类也继承了父类继承类的属性和方法

    例子说明
    class perdon{ // 父类的属性和方法
        private String name;
        public String id;
        private void MakeMoney() {
            System.out.println("I will make money");
        }
        public void SpendMoney() {
            System.out.println("I will spend money");
        }
    }
    class Man extends person{
        
        public void eat() {
            System.out.println("I will eat");
        }
    }
    class students extends Man{
        public void say(){
            System.out.print(" 我是最小的");
        }
    }
    //students 类不仅仅继承了 Man 类的属性和方法(公有的),也继承了 perdon 类的属性和方法
    // 多层继承,还可以通过接口、内部类来实现
  • 子类和父类是相对的概念,一个类是某个类父类的同时,也可以是另一个类的子类。

  • 一个父类可以有多个子类


# 向上转型 | 向下转型

父子对象之间的转换分为了向上转型向下转型,它们区别如下:

  • 向上转型:通过子类对象 (小范围) 实例化父类对象 (大范围), 这种属于自动转换

    Man man = new Student();// 通过子类去实例化父类对象

    这样做的意义:
    当我们需要多个同父的对象调用某个方法时,通过向上转换后,则可以确定参数的统一。方便程序设计

    向上转型后,父类对象只能调用父类方法或者子类覆写后的方法,而子类中的方法则是无法调用的。

    在这就是 Student 的方法 say () 不能调用


  • 向下转型:通过父类对象 (大范围) 实例化子类对象 (小范围), 这种属于强制转换

    Student student = (Student)new Man();// 向下转型 (Student 类是 Man 的子类)

# 继承中的关系

  • 不同变量名子类直接调用

  • 相同变量名调用时,如果子类有,就不调用父类的变量,如果子类没有,父类有,则调用父类的;在继承多层父类中有方法或属性重名,会调用最接近的父类

  • 在子类中,要调用父类的成员,使用关键字 super,调用父类方法也是一样,使用 super 关键字。

  • 在继承关系之中,如果要实例化子类对象,会默认先调用父类构造,为父类之中的属性初始化,之后再调用子类构造,为子类之中的属性初始化,即:默认情况下,子类会找到父类之中的无参构造方法。而如果这个时候父类没有无参构造,则子类必须通过 super () 调用指定参数的构造方法

  • 调用父类构造方法的 super()语句必须写在子类构造方法的第一行,否则编译时将出现错误信息

    class person{
        person(){
            System.out.println("Hi")
        }
        person(String s){
            System.out.println(s)
        }
    }
    // 下面的定义是正确的
    class man{
        man(){
            super("111");
        }
    }
    // 下面的写法是会报错的
     class man_1{
    	man_1(){
            String s;
            super("123");
        }
     }
    // JAVA 规定了 super () 调用必须是构造函数主体中的第一条语句
    // 在子类中,不写 super(),系统是默认会在第一行加上的,创建子类对象时,调用子类构造方法之前会先调用父类构造方法,如果有父类一直追溯到 Object 类,它是祖宗类
  • super()与 this()均在构造方法内的第一行,也就是由于这个原因,它们两个无法同时存在于同一个构造方法。与 this 一样,super 指得也是对象,所以不能在 static 环境中使用,包括静态方法和静态初始化器(static 语块)。

  • 继承的初始化顺序

    1、初始化父类再初始化子类

    2、先执行初始化对象中属性,再执行构造方法中的初始化。

    基于上面两点,我们就知道实例化一个子类,java 程序的执行顺序是:

    父类对象属性初始化 ----> 父类对象构造方法 ----> 子类对象属性初始化 ---> 子类对象构造方法


# 代码实例

先创建 person 父类(在此是爷爷类)

public class person {
    private String age;// 私有属性,不被继承
    public String name;// 公有属性,可以被继承
    public String sex="2";
    public String id;
	
    // 无参构造方法
    person(){
    }
	
    // 有参构造方法
    person(String name,String id){
        this.name=name;
        this.id=id;
    }
    // 私有方法,不被继承
    private void MakeMoney() {
        System.out.println("I will make money");
    }
    // 公有方法,可以被继承
    public void SpendMoney() {
        System.out.println("I will spend money");
    }
    public void eat(){
        System.out.println("I will spend eat");
    }
}

创建 Student 父类

public class Student extends person {
    public String grade;
    public String classroom;
    public String sex = "1";
    Student() {
    }
    Student(String grade, String classroom) {
        this.grade = grade;
        this.classroom = classroom;
    }
    public void sayHi(){
        System.out.println("hi");
    }
}

创建 Child 类

public class Child extends Student{
    public String s;
    Child(){}
    Child(String s){
        super("1","101");
    }
    public  void say() {
        System.out.println();
    }
}

创建调用方法

public class transfer {
    public static void main(String[] args) {
        Child child =new Child();
        child.s="s";
        child.classroom="101";
        child.grade="1";
        child.id="20211226";
        child.sex="boy";
        child.name="愧诗";
        child.say();
        child.eat();
        child.sayHi();
        child.SpendMoney();
        Child child1= new Child("s");
        System.out.println(child1.grade);
        System.out.println(child1.classroom);
    }
}

实例说明:

person类
  • 公有属性和方法:name、sex、id、SpendMoney ()、eat ()
  • 私有属性和方法:age ()、MakeMoney ()
Student类
  • 定义的方法和属性(未定义私有的属性和方法)

    属性:grade、classroom、sex

    方法:sayHi ()

  • 继承了:

    属性和方法:name、sex、id、SpendMoney ()、eat ()

Child类
  • 定义的方法和属性

    属性:s

    方法:say ()

  • 继承的

    属性和方法:name、sex、id、SpendMoney ()、eat () | grade、classroom、sex sayHi ()

  • 在 transfer 类中 new 了 2 个对象:child、child1

  • child 设置和调用了其继承和有的方法与属性

  • child1 调用了父类 Student 的有参构造方法

    // 控制台输出
    我被调用了
    I will spend eat
    hi
    I will spend money
    1
    101

#
继承所需要注意的地方!!

  1. 父类一旦发生属性和方法改变,那么子类拥有的属性和方法也会改变
  2. 继承破坏了封装,对于父类而言,它的实现细节对于子类来说是透明的
  3. 继承是一种强耦合关系

在写继承的时候,好好考虑清楚是否需要从子类向父类进行向上转型。如果必须向上转型,则继承是必要的,但是如果不需要,则应当好好考虑自己是否需要继承。


少用继承,多用组合!