Skip to content

[!quote] 继承 继承 可以使子类具有父类的属性和方法,还可以在子类派生类】中 重新定义 / 追加 属性和方法

public class 父类 {……}

public class 子类 extends 父类 {……}
  • 提高代码的复用性
  • 提高了代码的维护性【修改一处可变多处】

[!hint] 什么时候使用继承 ? 当 A 和 B 两个类有包含关系时

WARNING

  • 在访问子类的构造方法之前,会先访问父类的无参构造方法
  • 父类的私有方法,子类是不能重写的

方法重写,多继承

[!hint] 方法重写

java
// 父类
public class fu{                  
	public void winbee(){
		System.out.println("123");
	}
}
java
// 子类
public class zi extends fu {         
	public void winbee(){   //子类重写了winbee()方法
		super.winbee();    //调用父类的winbee()方法
		System.out.println("456");
	}
}
  • 方法重写里子类方法的访问权限不能比父类低【public > 默认 > private

[!hint] 无法多继承,但可以多层继承【子 extends 父,父 extends 爷】

为什么 Java 不允许多继承 ?

因为会出现”致命方块“问题 400 如果有一个 eat() 方法,从动物类一直继承到了马类,驴类。那当骡子类调用 eat() 方法的时候,那是调用马的还是驴的 ?

关键字

[!quote] thisthis 表示的是当前类

java
public class student {
    private int age;

    public void setAge(int a) {
		this.age = a;  // 当前类的 age
    }
}

[!quote] supersuper 表示的是父类

java
public class student extends fu{
    private int age;

    public void setAge(int a) {
		super.age = a;  // 父类的 age
    }
}

拓展和应用

💛 多态

[!quote] 多态 多态 就是一个对象可以表现出不同的形态

java
animal a = new cat();
  • 使用前提
    • 有 继承/实现 关系
    • 有方法重写
    • 有父类引用指向子类对象: animal a = new cat();

[!hint] 编译器根据引用类型【animal】来判断有哪些方法可以调用,而不是对象实体【cat


  • 多态中,访问引用的变量访问的是父类的变量,访问引用的方法访问的是子类的方法
java
// 父类
public class fu {          
    int age = 1;  
  
    public void winbee() {  
        System.out.println("123");  
    }  
}

// 子类
public class zi extends fu {           
    int age = 2;  
  
    public void winbee() { 
        System.out.println("456");  
    }  
}

// 测试类
public class demo {         
    public static void main(String[] args) {  
        fu fz = new zi();  
        System.out.println(fz.age);   //访问的是父类的age,输出父类的age
        fz.winbee();  //访问的是父类的winbee()方法,输出的是子类的winbee()方法
    }                     
}

---
1
456

向下转型

[!quote] 向下转型 向下转型 可以访问父类中没有,子类中有的方法

java
public static void main(String[] args) {  
    animal a = new cat(); 
    cat c = (cat) a;  // 向下转型
    c.play();
}

作用

[!hint] 让结构更加清晰 我们来写一个俄罗斯方块游戏:

  • 首先我们知道,我们需要一个房子,这个房子里有方块,方块接触到地面或者碰到其他方块会停下来
  • 然后房子上会出现另一个方块,在下落的过程中你可以翻转,如果好的话,可以减少几行
  • 方块有 6 种,它们的形态各异

解决办法 1

我们可以在一个方块类中写上 6 种方块的翻转方法,但是你可能要在一个方块类中写上几千行代码,这明显不好

解决办法 2

我们也可以一个方块类中写上一个空的翻转方法,然后在 6 个子类中重写翻转方法,这样结构清晰,子类长条方块也是方块,子类正方形方块也是方块。而且方块类中可能有各种方块的共有参数

[!hint] 类元素数组

java
Animal animals = new Animal[3];    // 数组里是animal的子类对象
animals[0] = new Dog();
animals[1] = new Cat();
animals[2] = new Wolf();

for(animal a : animals){
	a.eat();
	a.play();
}

💛 抽象类

[!quote] 抽象类 使用 abstract 声明的,没有方法体的方法叫 “抽象方法”,有抽象方法的类叫 “抽象类”

java
public abstract class animal {    //抽象类
	public abstract void eat();     //抽象方法
}
  • 子类继承抽象类时,必须重写该抽象方法
  • 抽象类不能实例化,但是可以通过一个非抽象的子类来间接实例化

[!hint] 作用

  • 抽象方法的意义在于定义一个接口或协议,之后的子类必须实现这些抽象方法,这个协议可以确保所有的子类都具有某种同类型的特征
  • 结构清晰【当别人看到一个类是抽象类时,就会很关心它的抽象方法。也会知道一定会有子类去重写这个抽象方法。而且会去找引用,一定有多态的体现】:根据多态的俄罗斯方块例子我们知道,方块类里的翻转方法并不重要【因为它一定会被重写】。那竟然是空方法的话,我们就可以把它写成抽象方法,把方块类写成抽象类
  • 引起重视:把那些像东西一样差不多的类写成抽象类,而像水杯一样的类就会不抽象,前后形成反差,引起重视
  • 顺应继承的逻辑:如果不用抽象类,继续用普通类的多态。那么我们就可以实例化出一个方块类对象,但是根据继承的逻辑来说,”方块是方块“,这就很奇怪,所以我们让方块类抽象化(让它不能被实例化)

[!hint] 有了接口,为什么还要抽象类 ?https://www.mianshiya.com/bank/1787463103423897602/question/1780933294427238401#heading-2 抽象类 位于接口和普通类之间,用于定义模板性的方法,让其多个子类可以不用写重复逻辑

  • 接口中不能有成员变量,接口中的普通方法也不能有方法体,但是抽象类可以
  • 抽象类虽然不能被实例化,但是可以有构造方法
java
// 抽象类
public abstract class Animal {
    protected String name;

    public Animal(String name) { this.name = name; }

    public abstract void makeSound();

    public void eat() {
        System.out.println(name + " is eating.");
    }
}

// 接口
public interface Swimmable {
    void swim();
}

// 子类
public class Dog extends Animal implements Swimmable {
    public Dog(String name) { super(name); }

    @Override
    public void makeSound() {
        System.out.println("Woof! Woof!");
    }

    @Override
    public void swim() {
        System.out.println(name + " is swimming.");
    }
}

---
在这个例子中,Animal 是一个抽象类,它提供了一个构造函数和一个具体的方法 eat(),同时强制子类实现 makeSound() 方法。而 Swimmable 是一个接口,强制实现类实现 swim() 方法。Dog 类既继承了 Animal 抽象类,也实现了 Swimmable 接口。

好的,这里有一个具体的例子展示了抽象类可以实现的功能,而接口无法实现。

场景描述

假设我们需要设计一个框架来管理数据库连接。我们希望:

  1. 有一个 private 变量来存储数据库连接
  2. 提供一个 protected 方法供子类访问数据库连接
  3. 实现一个公共方法来进行连接操作

抽象类的实现

java
// 抽象类定义
public abstract class Database {
    // 私有成员变量
    private Connection connection;

    // 构造函数
    public Database() {
        this.connection = createConnection();
    }

    // 抽象方法,供子类实现
    protected abstract Connection createConnection();

    // 受保护的方法,供子类使用
    protected Connection getConnection() {
        return connection;
    }

    // 公共方法,所有外部类都可以调用
    public void connect() {
        System.out.println("Connecting to database...");
    }
}

// 具体类 MySQLDatabase 继承自 Database
public class MySQLDatabase extends Database {
    @Override
    protected Connection createConnection() {
        System.out.println("Creating MySQL connection...");
        return new MySQLConnection();
    }

    // 子类可以使用 protected 方法
    public void customMySQLFunction() {
        Connection conn = getConnection();
        // 执行 MySQL 特有的一些操作
    }
}

// 测试类
public class Main {
    public static void main(String[] args) {
        MySQLDatabase mySQLDB = new MySQLDatabase();
        mySQLDB.connect();
        mySQLDB.customMySQLFunction();
    }
}

为什么接口无法实现相同的功能?

接口在 Java 中有以下限制:

  1. 接口中的方法默认是 public,无法定义 protected 方法
  2. 接口不能包含 private 成员变量
  3. 接口不能有构造函数

如果尝试使用接口

java
// 尝试使用接口
public interface Database {
    // 接口不能有构造函数、private 变量或 protected 方法
    Connection createConnection();
    void connect();
}

// MySQLDatabase 实现接口
public class MySQLDatabase implements Database {
    private Connection connection;

    // 构造函数
    public MySQLDatabase() {
        this.connection = createConnection();
    }

    @Override
    public Connection createConnection() {
        System.out.println("Creating MySQL connection...");
        return new MySQLConnection();
    }

    @Override
    public void connect() {
        System.out.println("Connecting to database...");
    }

    // 这个方法不能是 protected 的,因为接口中的所有方法默认是 public
    public void customMySQLFunction() {
        // 无法使用 protected 的 getConnection 方法
        // 因为接口中不允许定义这样的受保护方法
    }
}

在接口版本中,我们无法将 createConnection 和 getConnection 方法设为 protected,也无法包含 private 成员变量。这使得接口在这种情况下不如抽象类灵活和具有封装性。

总结

抽象类可以包含不同访问控制级别的方法和变量(如 privateprotected),并且可以提供部分实现和状态管理。而接口主要用于定义行为规范,所有方法默认都是 public 的,不能包含 private 成员变量。因此,抽象类在需要部分实现和封装时比接口更为合适。