学习Java41天

抽象类 (Abstract Class)

一、抽象类的基本概念

1. 抽象方法
  • 定义 :一个没有方法体的方法(即只有方法声明,没有具体的实现)。

  • 格式

    java

    复制代码
    public abstract 返回值类型 方法名(参数列表);
  • 特点

    • 使用 abstract 关键字修饰。

    • 没有方法体,直接以分号 ; 结束。

    • 它定义了一个 "必须实现"的规范或契约

2. 抽象类
  • 定义 :包含抽象方法的类必须被声明为抽象类。

  • 格式

    java

    复制代码
    public abstract class 类名 {
        // 类的内容
    }
  • 特点

    • 使用 abstract 关键字修饰类。

    • 抽象类不能直接实例化 (不能 new 抽象类)。

二、抽象类的作用与设计思想

抽象类的核心是一种 "未完成"的类设计模板

  1. 抽取共性,定义规范

    • 当在父类中提取多个子类的共性行为时,如果无法(或不适合)确定该行为的具体实现 ,就将该方法声明为抽象方法

    • 这样,抽象类就定义了一个 "规范"或"契约" ,强制所有非抽象的子类必须按照这个声明来提供具体的实现。

  2. 强制子类重写

    • 子类继承抽象类后,必须处理这些抽象方法。这保证了代码的一致性。

通俗理解 :抽象类就像是制定了一份 "必须完成的任务清单"。清单上只写了任务名称和要求(抽象方法),但没写怎么做。子类就像不同的执行者,它们必须按照清单完成所有任务(实现所有抽象方法),但可以根据自己的情况选择不同的完成方式。

三、继承抽象类的注意事项

当一个普通类继承一个抽象类时,有两种选择:

  1. 重写所有抽象方法(推荐,也是最常见的情况)

    • 将父类中定义的所有抽象方法都提供具体的实现。

    • 这样,子类就成为了一个 "具体类",可以被正常实例化。

  2. 将自己也声明为抽象类

    • 如果子类没有重写没有重写全部 的抽象方法,那么这个子类也必须abstract 关键字修饰,成为一个新的抽象类。

    • 这个新的抽象类可以继续被其他类继承。

简单总结"抽象"具有传递性。一个类只要继承了抽象方法而没有实现它,它自己就必须保持抽象。

四、抽象类的其他特性

  1. 抽象类中可以有构造方法

    • 虽然抽象类不能直接实例化,但它可以有构造方法

    • 构造方法用于子类创建对象时,初始化从父类继承来的成员。

    java

    复制代码
    public abstract class Animal {
        private String name;
        
        // 抽象类可以有构造方法
        public Animal(String name) {
            this.name = name;
        }
        
        public String getName() {
            return name;
        }
        
        public abstract void eat();
    }
  2. 抽象类中不一定有抽象方法

    • 一个类被 abstract 修饰,它就是抽象类。

    • 但抽象类不一定 包含抽象方法。这种设计通常是为了防止这个类被实例化,而只允许它被继承。

    java

    复制代码
    public abstract class MyBaseClass {
        // 没有抽象方法,但类依然是抽象的
        public void normalMethod() {
            System.out.println("这是一个普通方法");
        }
    }
    // 这个类不能 new MyBaseClass(),只能被继承。
  3. 抽象类中可以包含普通成员

    • 抽象类可以有普通的成员变量普通的成员方法(非抽象方法)

    • 这些普通成员可以直接被子类继承和使用。

五、完整示例

java

复制代码
// 1. 定义抽象类
public abstract class Animal {
    private String name;
    private int age;

    // 抽象类可以有构造方法
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 抽象方法:定义了一个必须完成的行为规范
    public abstract void eat();

    // 普通方法:子类可以直接继承使用
    public void sleep() {
        System.out.println(name + "正在睡觉");
    }

    // Getter/Setter
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

// 2. 具体子类:必须重写所有抽象方法
public class Cat extends Animal {
    public Cat(String name, int age) {
        super(name, age); // 调用父类构造方法
    }

    @Override
    public void eat() {
        System.out.println(getName() + "(" + getAge() + "岁)正在吃鱼");
    }
}

// 3. 另一个具体子类
public class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println(getName() + "(" + getAge() + "岁)正在啃骨头");
    }

    // 子类可以有自己的特有方法
    public void watchHouse() {
        System.out.println(getName() + "在看家");
    }
}

// 4. 测试类
public class Test {
    public static void main(String[] args) {
        // Animal a = new Animal(); // 错误!抽象类不能实例化

        Animal cat = new Cat("小花", 2);
        cat.eat();    // 输出:小花(2岁)正在吃鱼
        cat.sleep();  // 输出:小花正在睡觉

        Dog dog = new Dog("大黄", 3);
        dog.eat();        // 输出:大黄(3岁)正在啃骨头
        dog.watchHouse(); // 输出:大黄在看家
    }
}

// 5. 抽象子类示例(不重写抽象方法)
public abstract class Bird extends Animal {
    // 没有重写 eat() 方法,所以 Bird 也必须是抽象类
    public Bird(String name, int age) {
        super(name, age);
    }

    // 可以定义新的抽象方法
    public abstract void fly();
}

// 6. 最终的具体类,需要实现所有抽象方法(包括Animal的和Bird的)
public class Sparrow extends Bird {
    public Sparrow(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() { // 实现 Animal 的 eat()
        System.out.println(getName() + "正在吃虫子");
    }

    @Override
    public void fly() { // 实现 Bird 的 fly()
        System.out.println(getName() + "正在飞翔");
    }
}

六、抽象类 vs 普通类 vs 接口

特性 普通类 抽象类 接口
实例化 可以直接实例化 不能直接实例化 不能直接实例化
方法 全是具体方法 可以有具体方法和抽象方法 Java 8前全是抽象方法,之后可以有默认/静态方法
构造方法 没有
成员变量 任意类型 任意类型 只能是 public static final 常量
继承/实现 单继承 单继承 多实现
设计目的 具体实现 模板设计,部分实现,部分规范 纯规范,定义行为契约

七、总结

  1. 抽象类是一个"半成品"类,它定义了部分实现,也声明了部分必须由子类完成的规范(抽象方法)。

  2. 核心作用代码复用 + 强制规范。既提取了共性代码,又强制子类必须实现特定行为。

  3. 使用场景:当多个相关类有共同的属性和行为,但某些行为的具体实现各不相同时。

  4. 设计理念 :"面向抽象编程",依赖于抽象(父类或接口)而非具体实现,提高了代码的灵活性和可扩展性。

java 复制代码
package abstract02;

public class Test {
    public static void main(String[] args) {
        Frog f = new Frog(1, "wawa");
        System.out.println(f.getName()+", "+f.getAge());
        f.drink();
        f.eat();
        f.swim();

        Dog d = new Dog(5, "wangwang");
        System.out.println(d.getName()+", "+d.getAge());
        d.drink();
        d.eat();
        d.swim();

        Sheep s = new Sheep(6, "miemie");
        System.out.println(s.getName()+", "+s.getAge());
        s.drink();
        s.eat();
    }
}


package abstract02;

public abstract class Animal {
    private String name;
    private int age;

    public Animal() {
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void drink(){
        System.out.println("动物在喝水");
    }

    public abstract void eat();
}


package abstract02;

public class Dog extends Animal implements Swim{

    public Dog() {
    }

    public Dog(int age, String name) {
        super(age, name);
    }

    @Override
    public void eat() {
        System.out.println("狗在吃骨头");
    }

    @Override
    public void swim() {
        System.out.println("狗在游泳");
    }
}


package abstract02;

public  class Frog extends Animal implements Swim{

    public Frog() {
    }

    public Frog(int age, String name) {
        super(age, name);
    }

    @Override
    public void eat() {
        System.out.println("青蛙在吃虫子");
    }

    @Override
    public void swim(){
        System.out.println("青蛙在游泳");

    }
}


package abstract02;

public class Sheep extends Animal{
    public Sheep() {
    }

    public Sheep(int age, String name) {
        super(age, name);
    }

    @Override
    public void eat() {
        System.out.println("🐏在吃草");
    }

}


package abstract02;

public interface Swim {
    public abstract void swim();

}
相关推荐
aloha_7891 小时前
软考信息系统项目管理师错误归纳总结
java·学习
呉師傅2 小时前
UPS滴滴告警!如何测量UPS电池内阻【UPS学习】
运维·服务器·网络·学习·电脑
lizhihai_992 小时前
股市学习心得-与英伟达核心 PCB 相关的八家关联企业
大数据·人工智能·学习
阳光宅男@李光熠3 小时前
【电子通识】贴片电阻上的丝印332、5R6、1502、01C怎么读出阻值?
笔记·学习
sakiko_5 小时前
Swift学习笔记31-网络请求
网络·笔记·学习·swift
星夜夏空995 小时前
STM32单片机学习(20) —— 利用中断实现串口通信(填前面的坑)
stm32·单片机·学习
神谕的祝福5 小时前
comfyui从0到1开始学习-第一讲安装ComfyUI
学习
名字不相符5 小时前
ctfshow之MISC入门(个人记录与学习)
学习·ctf·misc
创业之路&下一个五年6 小时前
自聚合树形业务:泛型基类+继承 设计思想完整总结(含核心原理与落地案例)
学习·总结
wuxinyan1236 小时前
工业级大模型学习之路023:LangChain零基础入门教程(第六篇):重排序与高级检索策略
人工智能·python·学习·langchain