一篇文章带你彻底搞懂Java 抽象类—菜鸟教程学习记录

一篇文章带你彻底搞懂Java 抽象类—菜鸟教程学习记录

基本概念:

​ 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。

语法上,在 Java 语言中使用 abstract 关键字 来定义抽象类。示例如下:

abstract class

抽象类是面向对象编程中的一个重要概念,具有以下几个特点(这是进阶需要掌握的,如果是新手请跳转到下面的简单版):

  1. **不能被实例化:**抽象类本身不能被实例化,也就是不能创建抽象类的对象。只能通过继承抽象类,在子类中实现抽象方法后才能创建子类的对象。
  2. **可以包含抽象方法:**抽象类可以包含抽象方法,抽象方法是没有具体实现的方法,只有方法声明而没有方法体。抽象方法用于定义接口规范,要求子类必须实现这些抽象方法。
  3. **可以包含普通方法:**抽象类除了包含抽象方法,还可以包含普通的方法实现。抽象类中的普通方法可以有具体的实现代码,并且可以被子类直接继承和使用。
  4. **可以包含成员变量:**抽象类可以包含成员变量,这些成员变量可以是任何类型的变量,包括基本数据类型、引用类型等。子类可以直接继承这些成员变量,并在子类中进行访问和使用。
  5. **可以被继承:**抽象类可以作为其他类的父类被继承。子类继承抽象类后,需要实现抽象类中的抽象方法,并可以选择重写或继承抽象类中的普通方法。
  6. **用于定义类的层次结构:**抽象类常用于定义类的层次结构和接口规范。通过将共同的属性和行为放在抽象类中,可以减少代码的重复,并提高代码的可维护性和扩展性。
  7. **可以拥有构造器:**抽象类可以拥有构造器来初始化成员变量或执行其他必要的操作。子类在实例化时会先调用抽象类的构造器来完成对象的初始化工作。
  8. **可以拥有抽象类的实例方法:**除了抽象方法和普通方法外,抽象类还可以拥有实例方法。这些实例方法可以有具体的实现代码,并且可以被子类直接继承和使用。
  9. **子类可以继承多个抽象类:**子类可以继承一个或多个抽象类,但是Java中不支持多重继承,即一个类只能继承一个具体类,但可以实现多个接口。
  10. 用于强制实现接口规范:抽象类常被用于定义接口规范,通过抽象方法的声明,强迫子类实现这些方法以满足接口的要求。
  11. 可以包含静态方法和静态变量:抽象类可以包含静态方法和静态变量。静态方法和静态变量属于类级别,而不是对象级别,可以直接通过抽象类调用或访问。
  12. **抽象类可以具有默认的方法实现:自Java 8起,**抽象类可以在接口中定义的默认方法具有默认的方法实现。这些默认方法可以在抽象类中直接调用或重写。
  13. 抽象类可以实现接口:抽象类可以作为实现接口的一种方式。抽象类可以实现接口中的方法,并在子类中选择性地重写或继承这些方法。
  14. **抽象类可以被其他类继承和重用:**抽象类可以被其他非抽象类继承和重用,这样可以通过抽象类提供的公共成员变量和方法,减少代码的重复和冗余。

抽象类总结规定

  1. **抽象类不能被实例化(初学者很容易犯的错),**如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
  2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
  4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
  5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类

抽象类

在 Java 语言中使用 abstract class 来定义抽象类。如下实例:

/* 文件名 : Employee.java */
// 员工类,抽象类
public abstract class Employee {
    private String name; // 姓名
    private String address; // 地址
    private int number; // 工号

    // 构造方法,初始化员工信息
    public Employee(String name, String address, int number) {
        System.out.println("Constructing an Employee"); // 输出构造员工的信息
        this.name = name;
        this.address = address;
        this.number = number;
    }

    // 计算薪资,抽象方法,具体实现由子类完成
    public abstract double computePay();

    // 邮寄薪资支票
    public void mailCheck() {
        System.out.println("Mailing a check to " + this.name + " " + this.address);
    }

    // 返回员工信息的字符串表示
    public String toString() {
        return name + " " + address + " " + number;
    }

    // 获取员工姓名
    public String getName() {
        return name;
    }

    // 获取员工地址
    public String getAddress() {
        return address;
    }

    // 设置员工地址
    public void setAddress(String newAddress) {
        address = newAddress;
    }

    // 获取员工工号
    public int getNumber() {
        return number;
    }
}

注意到该 Employee 类没有什么不同,尽管该类是抽象类,但是它仍然有 3 个成员变量,7 个成员方法和 1 个构造方法。

现在如果你尝试如下的例子:
//AbstractDemo.java 文件代码:
/* 文件名 : AbstractDemo.java */
public class AbstractDemo
{
   public static void main(String [] args)
   {
      /* 以下是不允许的,会引发错误 */
      Employee e = new Employee("George W.", "Houston, TX", 43);
 
      System.out.println("\n Call mailCheck using Employee reference--");
      e.mailCheck();
    }
}

当你尝试编译 AbstractDemo 类时,会产生如下错误:

Employee.java:46: Employee is abstract; cannot be instantiated
      Employee e = new Employee("George W.", "Houston, TX", 43);
                   ^
1 error
原因是:

抽象类不能实例化对象是指不能使用抽象类本身来创建对象。因为抽象类是一种不完整的类,其中定义了抽象方法但没有提供具体的实现。抽象方法是没有方法体的方法声明,只有方法的签名。由于抽象方法没有具体的实现,因此不能直接创建抽象类的对象。抽象类不能被实例化,也就是说不能通过new关键字来创建对象


那么思考为什么抽象类可以有构造器呢?

原因:

​ 抽象类可以具有构造器的原因是,构造器在创建对象时负责初始化对象的状态。尽管抽象类本身不能被实例化,但它的子类可以被实例化,而构造器的目的是初始化子类的对象。

抽象类中的构造器在子类实例化时起到了重要的作用。当我们创建一个子类的对象时,首先会调用子类的构造器,然后再调用父类的构造器。这是因为子类继承了父类的成员,需要保证父类的状态正确初始化。

**抽象类的构造器与普通类的构造器一样,可以用于为实例变量赋初值、执行其他初始化操作等。**在抽象类的构造器中可以调用抽象类中的非抽象方法,这样可以确保子类在实例化时得到正确的初始状态。

当我们在子类中实现抽象方法时,也可以在子类的构造器中执行一些额外的初始化操作。这样做的好处是,在创建子类对象时,我们可以确保对象在使用之前已经完成了必要的初始化步骤。

举个例子,假设我们有一个抽象类Animal,其中定义了一个抽象方法makeSound()和一个非抽象方法eat()

abstract class Animal {
    public abstract void makeSound();
    
    public void eat() {
        System.out.println("The animal is eating.");
    }
}

我们希望子类在实例化时能够正确初始化Animal对象的状态。因此,在子类的构造器中可以调用父类的构造器来完成必要的初始化操作,比如给实例变量赋初值。

class Cat extends Animal {
    public Cat() {
        super();  // 调用父类的构造器
    }
    
    public void makeSound() {
        System.out.println("The cat meows.");
    }
}

在上面的例子中,子类Cat继承了抽象类Animal,并实现了抽象方法makeSound()。在子类的构造器中,我们调用了父类的构造器super()来进行初始化。这样在创建Cat对象时,可以确保Animal对象的初始化步骤被正确执行。

总结

抽象类可以具有构造器,用来初始化子类对象的状态。在子类的构造器中,可以调用父类的构造器来实现必要的初始化操作。这样可以确保在子类实例化时,对象能够得到正确的初始状态。


抽象方法

如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法。

Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。

抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。

public abstract class Employee
{
   private String name;
   private String address;
   private int number;
   
   public abstract double computePay();
   
   //其余代码
}

声明抽象方法会造成以下两个结果:

  • 如果一个类包含抽象方法,那么该类必须是抽象类。
  • 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。

继承抽象方法的子类必须重写该方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该抽象方法,否则,从最初的父类到最终的子类都不能用来实例化对象。

如果Salary类继承了Employee类,那么它必须实现computePay()方法:

/* 文件名 : Salary.java */
public class Salary extends Employee
{
   private double salary; // Annual salary
  
   public double computePay()
   {
      System.out.println("Computing salary pay for " + getName());
      return salary/52;
   }
 
   //其余代码
}

结尾语:记录于2023年8月2号17时33分,以上仅为个人在[Java抽象类—菜鸟教程](Java 抽象类 | 菜鸟教程 (runoob.com))的学习过程中遇到的问题,还有记录的个人想法,有错误欢迎指出,希望对您有帮助,感谢观看!如果可以的话,点点赞,点点关注

你可能感兴趣的:(Java面向对象—菜鸟教程,java,学习,开发语言)