杰瑞科技汇

abstract类与普通类的核心区别是什么?

什么是抽象类?

抽象类是一种不能被实例化的类,你不能用 new 关键字来创建一个抽象类的对象,它的主要目的是作为基类(父类),被其他类继承,从而实现代码的重用和定义一种通用的“蓝图”或“模板”。

abstract类与普通类的核心区别是什么?-图1
(图片来源网络,侵删)

你可以把它想象成一个“不完整”的类,它包含了一些已经实现的功能,也包含了一些尚未实现、必须由子类来完成的功能。

为什么需要抽象类?

抽象类主要用于以下场景:

  1. 定义通用行为:当多个类具有一些共同的特征和行为时,可以将这些共同的行为提取到一个父类中,如果这个父类本身不能独立存在(因为它描述的是一个抽象概念),就可以将其声明为抽象类。
  2. 强制子类实现特定功能:当父类中的某个方法,其具体实现依赖于子类的具体特性时,父类可以不提供实现,而是强制要求所有子类必须重写(实现)这个方法,这确保了所有子类都具有这个行为,尽管实现方式可能不同。
  3. 实现代码复用:抽象类可以包含具体的方法(有方法体的方法)和成员变量,子类可以直接继承和使用它们,避免了重复编写代码。

如何定义抽象类?

使用 abstract 关键字来修饰一个类,这个类就成为抽象类。

// 使用 abstract 关键字定义抽象类
public abstract class Animal {
    // ... 类内容
}

抽象类的主要特征

1 抽象方法

这是抽象类最核心的特征。

abstract类与普通类的核心区别是什么?-图2
(图片来源网络,侵删)
  • 定义:使用 abstract 关键字修饰的方法,称为抽象方法。
  • 特点
    • 抽象方法没有方法体(即没有 和具体的实现代码)。
    • 它以分号
    • 抽象方法必须在抽象类中定义。
public abstract class Animal {
    // 抽象方法:没有方法体
    public abstract void makeSound();
}

2 包含具体方法

抽象类不仅可以有抽象方法,也可以有具体的方法(有完整实现的方法),子类会继承这些具体的方法。

public abstract class Animal {
    // 具体方法:有方法体
    public void eat() {
        System.out.println("This animal is eating.");
    }
    // 抽象方法:没有方法体,需要子类实现
    public abstract void makeSound();
}

3 不能被实例化

这是抽象类最基本的规定,你不能创建抽象类的对象。

// 错误!不能创建抽象类的实例
Animal myAnimal = new Animal(); // 编译会报错

4 构造方法

抽象类可以有构造方法,虽然你不能创建抽象类的对象,但抽象类的构造方法会在其子类创建对象时被调用(通过 super() 语句),这通常用于初始化抽象类中定义的成员变量。

public abstract class Animal {
    private String name;
    // 抽象类的构造方法
    public Animal(String name) {
        this.name = name;
        System.out.println("Animal constructor called for " + name);
    }
    // ...
}

5 继承规则

  • 子类必须实现抽象方法:如果一个非抽象的类继承了一个抽象类,那么这个子类必须实现(重写)父类中的所有抽象方法,否则,这个子类也必须被声明为 abstract

    abstract类与普通类的核心区别是什么?-图3
    (图片来源网络,侵删)
    // 正确:子类实现了所有抽象方法
    public class Dog extends Animal {
        public Dog(String name) {
            super(name);
        }
        @Override
        public void makeSound() {
            System.out.println("Woof!");
        }
    }
    // 错误:子类没有实现 makeSound(),所以它也必须是抽象的
    // public class Cat extends Animal {
    //     public Cat(String name) {
    //         super(name);
    //     }
    //     // 编译错误:Cat 不是抽象的,并且未覆盖 Animal 中的抽象方法 makeSound()
    // }
  • 抽象类可以继承抽象类:如果一个抽象类继承自另一个抽象类,它不需要强制实现父类的所有抽象方法,它可以选择实现其中一部分,或者一个也不实现。

一个完整的例子

让我们用一个经典的“形状”例子来理解抽象类。

第1步:定义抽象类 Shape

Shape 是一个抽象概念,我们无法直接画一个“形状”,只能画具体的圆形、正方形等。Shape 应该是抽象的。

// Shape.java
public abstract class Shape {
    // 成员变量
    protected String color;
    // 构造方法
    public Shape(String color) {
        this.color = color;
    }
    // 抽象方法:计算面积,具体实现由子类决定
    public abstract double calculateArea();
    // 具体方法:获取颜色,所有子类都可以直接使用
    public String getColor() {
        return color;
    }
    // 具体方法:显示信息
    public void displayInfo() {
        System.out.println("This is a " + color + " shape.");
    }
}

第2步:创建具体的子类

现在我们创建 CircleRectangle,它们都是具体的形状,可以被实例化。

// Circle.java
public class Circle extends Shape {
    private double radius;
    public Circle(String color, double radius) {
        super(color); // 调用父类 Shape 的构造方法
        this.radius = radius;
    }
    // 必须实现父类的抽象方法
    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
    // Circle 可以有自己的特有方法
    public double getRadius() {
        return radius;
    }
}
// Rectangle.java
public class Rectangle extends Shape {
    private double width;
    private double height;
    public Rectangle(String color, double width, double height) {
        super(color);
        this.width = width;
        this.height = height;
    }
    // 必须实现父类的抽象方法
    @Override
    public double calculateArea() {
        return width * height;
    }
}

第3步:测试和使用

// Main.java
public class Main {
    public static void main(String[] args) {
        // Shape shape = new Shape("Red"); // 错误!不能实例化抽象类
        // 创建具体的子类对象
        Circle circle = new Circle("Blue", 5.0);
        Rectangle rectangle = new Rectangle("Green", 4.0, 6.0);
        // 调用方法
        System.out.println("--- Circle Info ---");
        circle.displayInfo(); // 调用继承自 Shape 的具体方法
        System.out.println("Area: " + circle.calculateArea()); // 调用子类实现的抽象方法
        System.out.println("Radius: " + circle.getRadius()); // 调用子类自己的方法
        System.out.println("\n--- Rectangle Info ---");
        rectangle.displayInfo();
        System.out.println("Area: " + rectangle.calculateArea());
    }
}

输出结果:

--- Circle Info ---
This is a Blue shape.
Area: 78.53981633974483
Radius: 5.0
--- Rectangle Info ---
This is a Green shape.
Area: 24.0

抽象类 vs. 接口

这是初学者经常混淆的两个概念,它们都可以用来定义“契约”,但有显著区别。

特性 抽象类 接口
继承 只能继承一个抽象类(单继承) 可以实现多个接口(多实现)
方法 可以包含抽象方法具体方法 从 Java 8 开始,可以包含 defaultstatic 方法,但主要还是用于定义抽象行为
成员变量 可以包含各种类型的成员变量(private, protected, public, static, final 成员变量默认是 public static final 的,即常量
构造方法 有构造方法 没有(构造方法)
目的 提供一个“是什么”的关系(Dog 是一个 Animal),用于代码复用和建立类层次结构 提供一个“能做什么”的能力(Bird 能飞),用于定义行为规范
访问修饰符 方法可以是任何访问修饰符 方法默认是 public

简单总结:

  • 当你想描述一个“is-a”(是一个)的关系,并且希望子类能复用一些代码时,使用抽象类
  • 当你想定义一个类必须具备的“can-do”(能做)的能力,特别是当一个类需要从多个来源继承行为时,使用接口

在现代 Java 开发中,更推荐优先使用接口,因为它提供了更大的灵活性(多实现),而抽象类则更适合用于构建稳固的类层次结构。

希望这个详细的解释能帮助你完全理解 Java 中的抽象类!

分享:
扫描分享到社交APP
上一篇
下一篇