当前位置: 首页 > article >正文

[Java]抽象类

1. 什么是抽象类?

1.1 定义:

抽象类是一个不能实例化的类,它是用来作为其他类的基类的。抽象类可以包含抽象方法和非抽象方法。抽象方法没有方法体,子类必须重写这些方法并提供具体的实现。抽象类可以有构造方法、成员变量、静态方法和默认方法等。

1.2 特点:

  • 抽象类不能实例化,即不能创建抽象类的对象
  • 抽象类可以有抽象方法和非抽象方法
  • 子类必须实现抽象类中的所有抽象方法,除非子类本身是抽象类。

2. 抽象类的声明

在Java中,使用abstract关键字来声明抽象类和抽象方法。

abstract class Animal {
    // 抽象方法
    abstract void sound();

    // 非抽象方法(普通方法)
    void eat() {
        System.out.println("Animal is eating");
    }
}

3. 抽象类与继承

3.1 解释:

子类继承抽象类时,必须实现抽象类中的所有抽象方法,除非子类也是抽象类。如果子类没有实现抽象类的所有抽象方法,子类也必须被声明为抽象类。

3.2 示例:

abstract class Animal {
    abstract void sound();  // 抽象方法
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("Cat meows");
    }
}

在这个例子中,DogCat类分别实现了Animal类中的抽象方法sound

4. 抽象类的构造方法

4.1 解释:

抽象类可以有构造方法,构造方法在子类创建对象时被调用。虽然不能直接实例化抽象类,但可以通过子类的构造方法来调用父类的构造方法。

4.2 示例:

abstract class Animal {
    Animal() {
        System.out.println("Animal constructor");
    }

    abstract void sound();
}

class Dog extends Animal {
    Dog() {
        super();  // 调用父类的构造方法
        System.out.println("Dog constructor");
    }

    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

class abstract_Main{
    public static void main(String args[]){
        Animal dog = new Dog();
        dog.sound();
    }

}

输出:

Animal constructor
Dog constructor
Dog barks

5. 抽象类的成员变量

5.1 解释:

抽象类可以包含成员变量,它们可以是privateprotectedpublic等不同的访问修饰符。成员变量可以在抽象类中进行初始化,也可以在子类中进行修改。

5.2 示例:

abstract class Animal {
    String name;  // 成员变量

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

    abstract void sound();
}

class Dog extends Animal {
    Dog(String name) {
        super(name);  // 调用父类构造方法
    }

    @Override
    void sound() {
        System.out.println(name + " barks");
    }
}

6. 抽象类的访问修饰符

6.1 解释:

抽象类的访问修饰符(如publicprotected等)与普通类相同。可以根据需要控制访问级别,确保只有适当的类可以访问抽象类。

6.2 示例:

abstract class Animal {
    abstract void sound();
}

public class Main {
    public static void main(String[] args) {
        // Animal animal = new Animal();  // 错误:不能实例化抽象类
        Dog dog = new Dog();
        dog.sound();  // 输出:Dog barks
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

7. 抽象类与接口的区别

7.1 区别:

抽象类和接口都可以用来定义类的行为规范,但它们有一些关键的区别:

特性抽象类接口
是否可以有方法体可以有抽象方法和非抽象方法只能有抽象方法(Java 8之后,可以有默认方法和静态方法)
是否可以有成员变量可以有成员变量不可以有成员变量
是否支持多继承只能继承一个类可以实现多个接口
构造方法可以有构造方法没有构造方法
继承的限制只能继承一个抽象类可以实现多个接口

7.2 选择使用抽象类还是接口:

  • 抽象类:适用于类之间有“是一个”关系且需要共享代码的情况。比如,多个类共享一些通用的属性和方法时,可以使用抽象类。
  • 接口:适用于不同类之间有“行为”上的共性时。接口更强调行为规范的定义,可以在多个不相关的类中实现。

8. 抽象类的多态

8.1 解释:

和其他类一样,抽象类也支持多态。当父类类型的引用指向子类对象时,可以调用子类的重写方法。这使得抽象类可以与子类一起使用,支持多态特性。

8.2 示例:

abstract class Animal {
    abstract void sound();
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

class Cat extends Animal {
    @Override
    void sound() {
        System.out.println("Cat meows");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal1 = new Dog();
        Animal animal2 = new Cat();
        
        animal1.sound();  // 输出:Dog barks
        animal2.sound();  // 输出:Cat meows
    }
}

通过多态,Animal引用可以指向不同的子类对象,并调用子类的sound方法。

9. 抽象类的局限性

尽管抽象类具有许多优势,但它也有一些局限性:

  • 只能继承一个抽象类:Java中不支持多重继承,因此如果一个类继承了某个抽象类,它就不能再继承其他类。这可能会在设计中造成一些限制。
  • 不能实例化抽象类不能直接实例化,因此必须通过子类来实例化。

10. 抽象类的应用场景

10.1 抽象类通常用于以下情况:

  • 提供模板方法:通过在抽象类中定义一个具体的框架方法,然后让具体的子类提供实现细节。比如,常见的模板方法模式就是用抽象类实现的。
  • 共享代码:如果多个类有相似的功能,可以将这些功能放在抽象类中,由子类继承。
  • 多态支持:通过抽象类和继承机制,支持多态,让同一类型的对象能够表现出不同的行为。

10.2 示例:模板方法模式

// 抽象类,定义了模板方法
abstract class Template {
    // 模板方法,定义了执行步骤的顺序
    public final void execute() {
        step1();
        step2();
        step3();
    }

    // 步骤1(抽象方法,由具体子类实现)
    abstract void step1();

    // 步骤2(抽象方法,由具体子类实现)
    abstract void step2();

    // 步骤3(具体方法,子类可以直接使用)
    void step3() {
        System.out.println("Step 3: Common step");
    }
}

// 具体类A,实现了模板方法中定义的步骤
class ConcreteClassA extends Template {
    @Override
    void step1() {
        System.out.println("ConcreteClassA: Step 1");
    }

    @Override
    void step2() {
        System.out.println("ConcreteClassA: Step 2");
    }
}

// 具体类B,实现了模板方法中定义的步骤
class ConcreteClassB extends Template {
    @Override
    void step1() {
        System.out.println("ConcreteClassB: Step 1");
    }

    @Override
    void step2() {
        System.out.println("ConcreteClassB: Step 2");
    }
}

public class Main {
    public static void main(String[] args) {
        // 使用ConcreteClassA来执行模板方法
        Template classA = new ConcreteClassA();
        System.out.println("Executing Template in ConcreteClassA:");
        classA.execute();  // 调用模板方法

        System.out.println("\n-------------------");

        // 使用ConcreteClassB来执行模板方法
        Template classB = new ConcreteClassB();
        System.out.println("Executing Template in ConcreteClassB:");
        classB.execute();  // 调用模板方法
    }
}

输出:

Executing Template in ConcreteClassA:
ConcreteClassA: Step 1
ConcreteClassA: Step 2
Step 3: Common step

-------------------

Executing Template in ConcreteClassB:
ConcreteClassB: Step 1
ConcreteClassB: Step 2
Step 3: Common step

 解释:

  1. Template

    • 这个类定义了一个模板方法 execute(),它规定了执行步骤的顺序:step1()step2()step3()
    • step1()step2() 是抽象方法,要求具体子类去实现它们。
    • step3() 是一个具体方法,所有子类都可以直接使用,子类不需要重写。
  2. ConcreteClassAConcreteClassB

    • 这两个类都继承了 Template 类,并实现了 step1()step2() 方法,但它们的具体实现是不同的。
    • step3() 由父类 Template 提供,不需要在子类中实现。
  3. 模板方法的使用

    • Main 类的 main() 方法中,我们创建了 ConcreteClassAConcreteClassB 的实例,并调用了它们的 execute() 方法。
    • 每次调用 execute() 方法时,都会执行 Template 中定义的步骤顺序,但实际执行的内容是由 ConcreteClassAConcreteClassB 提供的 step1()step2() 方法。

11. 抽象类与接口的结合使用

11.1解释:

在Java中,抽象类和接口可以结合使用,一个类可以同时实现接口并继承抽象类。这可以让你充分利用接口的多继承特性,同时还能通过抽象类共享代码。

11.2 示例:

// 定义Animal接口,要求实现sound方法
interface Animal {
    void sound();  // 声明发出声音的方法
}

// 定义Mammal抽象类,继承自Animal接口
abstract class Mammal implements Animal {
    // 声明抽象方法walk,表示哺乳动物的步态
    abstract void walk();
}

// Dog类继承自Mammal,并实现sound和walk方法
class Dog extends Mammal {
    // 实现Animal接口的sound方法
    @Override
    public void sound() {
        System.out.println("Dog barks");
    }

    // 实现Mammal类的walk方法
    @Override
    void walk() {
        System.out.println("Dog walks on four legs");
    }
}

// 主类,用于测试Dog类的功能
public class Main {
    public static void main(String[] args) {
        // 创建一个Dog对象
        Dog dog = new Dog();
        
        // 调用Dog类的sound方法
        dog.sound();  // 输出:Dog barks
        
        // 调用Dog类的walk方法
        dog.walk();   // 输出:Dog walks on four legs
    }
}

解释:

  1. Animal 接口

    • Animal 接口声明了一个方法 sound(),所有实现了 Animal 接口的类必须提供对该方法的具体实现。
  2. Mammal 抽象类

    • Mammal 是一个抽象类,它实现了 Animal 接口,但并没有提供 sound() 方法的实现,子类 Dog 需要提供该方法的实现。
    • Mammal 类中还声明了一个抽象方法 walk(),表示哺乳动物的行走方式。具体的 walk() 方法由子类实现。
  3. Dog

    • Dog 类继承自 Mammal,并实现了 sound()walk() 方法,具体定义了狗的行为:叫声和走路方式。
  4. Main

    • Main 类创建了 Dog 类的实例,并调用了 sound()walk() 方法,输出狗的行为。

12. 抽象类中的静态方法

12.1 解释:

抽象类也可以包含静态方法,静态方法属于类本身,而不是类的实例。抽象类中的静态方法可以直接通过类名调用。但静态方法不能被子类重写。静态方法属于类本身,不属于实例化的对象,因此它们的调用不受多态的影响。

12.2 示例:

abstract class Animal {
    static void info() {
        System.out.println("This is an animal");
    }
}

class Dog extends Animal {
    // 不能重写静态方法
    // static void info() {}  // 错误,不能重写静态方法
}

public class Main {
    public static void main(String[] args) {
        Animal.info();  // 调用Animal类的静态方法
    }
}

尽管Animal是一个抽象类,但它仍然可以有静态方法。静态方法不能被子类重写,它们可以通过类名直接访问。

13. 抽象类的默认实现

13.1 解释:

抽象类不仅可以声明抽象方法,还可以提供默认实现。子类可以选择继承这个默认实现,也可以重写这些方法。这种机制与接口的默认方法(default)类似。

13.2 示例:

// 定义一个抽象类Animal,包含一个默认实现的方法eat()和一个抽象方法sound()
abstract class Animal {
    // 默认实现:吃东西的方法
    void eat() {
        System.out.println("Animal is eating");
    }

    // 抽象方法:每个子类必须实现它来发出声音
    abstract void sound();
}

// Dog类继承Animal类,只需要实现sound()方法
class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

// Main类,用于执行和测试代码
public class Main {
    public static void main(String[] args) {
        // 创建一个Dog对象
        Dog dog = new Dog();
        
        // 调用Dog类继承的eat()方法(没有被重写,直接使用父类的默认实现)
        dog.eat();  // 输出:Animal is eating
        
        // 调用Dog类自己实现的sound()方法
        dog.sound();  // 输出:Dog barks
    }
}

在这个例子中,eat()方法在Animal类中有默认实现,Dog类继承了eat()方法,但没有重写它。子类只需要实现sound()方法即可。

14. 抽象类中访问父类的方法

14.1 子类通过super访问父类中的成员方法和成员变量:

子类可以通过super关键字来访问父类的成员方法和成员变量。对于抽象类中的方法,子类可以使用super来调用抽象类中已实现的非抽象方法。

示例:
abstract class Animal {
    void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        super.sound();  // 调用父类的sound方法
        System.out.println("Dog barks");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.sound();
    }
}
/*
输出:
Animal makes a sound
Dog barks
*/

在上面的代码中,Dog类通过super.sound()调用了Animal类中已实现的sound()方法。

14.2 子类通过super访问父类中的构造方法:

在子类的构造方法中,super()可以用来调用父类的构造方法。如果父类没有无参构造方法,子类的构造方法必须显式调用父类的构造方法。

示例:
abstract class Animal {
    Animal(String name) {
        System.out.println("Animal constructor with name: " + name);
    }

    abstract void sound();
}

class Dog extends Animal {
    Dog(String name) {
        super(name);  // 调用父类的构造方法
        System.out.println("Dog constructor");
    }

    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("Buddy");
        dog.sound();
    }
}

输出:

Animal constructor with name:Buddy
Dog constructor
Dog barks

15. 抽象类与构造方法的调用

尽管抽象类不能被直接实例化,但它可以有构造方法。当子类创建对象时,会先调用父类的构造方法。子类的构造方法可以使用super()调用父类的构造方法。

示例:
abstract class Animal {
    Animal() {
        System.out.println("Animal constructor");
    }
    
    abstract void sound();
}

class Dog extends Animal {
    Dog() {
        super();  // 调用父类构造方法
        System.out.println("Dog constructor");
    }

    @Override
    void sound() {
        System.out.println("Dog barks");
    }
}

public class abstract_Main2{
    public static void main(String args[]){
        Animal dog = new Dog();
        dog.sound();     
    }

}

输出:

Animal constructor
Dog constructor
Dog barks

16. 抽象类与final关键字的结合

  • final如果一个类被声明为final,它不能被继承。因此,抽象类不能是final类,因为抽象类必须被继承才能实现。
  • final方法如果一个方法被声明为final,它不能被重写。虽然抽象类中的方法是抽象的,不能直接定义为final,但是如果子类实现了该方法,可以将其标记为final,防止进一步重写。
示例:
// 定义一个抽象类Animal,包含一个final方法eat()和一个抽象方法sound()
abstract class Animal {
    // final方法,子类不能重写
    final void eat() {
        System.out.println("Animal is eating");
    }

    // 抽象方法,子类必须实现
    abstract void sound();
}

// Dog类继承自Animal类,并实现sound()方法
class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Dog barks");
    }

    // 不能重写eat()方法,因为它是final方法
    // 如果你尝试重写eat()方法,编译器会报错
    // final void eat() {
    //     System.out.println("Dog is eating");
    // }
}

// 主类,用于执行和测试代码
public class Main {
    public static void main(String[] args) {
        // 创建一个Dog对象
        Dog dog = new Dog();
        
        // 调用Dog类继承的eat()方法(它来自Animal类,不能被重写)
        dog.eat();  // 输出:Animal is eating
        
        // 调用Dog类自己实现的sound()方法
        dog.sound();  // 输出:Dog barks
    }
}

在这个例子中,eat()方法在Animal类中被声明为final,所以子类Dog不能重写eat()方法。


http://www.kler.cn/a/531869.html

相关文章:

  • [Proteus仿真]基于51单片机的智能温控系统
  • kubernetes(二)
  • 自动化构建-make/Makefile 【Linux基础开发工具】
  • 解决PyG安装中torch-sparse安装失败问题:详细指南
  • 求职刷题力扣DAY34--贪心算法part05
  • HTB:Alert[WriteUP]
  • 【Three.js+React】教程002:添加lil-gui控制器和加载GLTF模型
  • 股票入门知识
  • 文字显示省略号
  • 如何创建折叠式Title
  • 探秘Linux IO虚拟化:virtio的奇幻之旅
  • HTTP异步Client源码解析
  • 01:安装和部署
  • Alibaba grpc Dubbo view
  • AMBA总线学习4--AHB-lite总线
  • 读书笔记 | 《最小阻力之路》:用结构思维重塑人生愿景
  • Deepseek-R1 和 OpenAI o1 这样的推理模型普遍存在“思考不足”的问题
  • 41【语言的编码架构】
  • 单机性能调优中的程序优化
  • Kotlin 使用 Springboot 反射执行方法并自动传参
  • golang命令大全8--跨平台构建
  • git 新项目
  • Unity游戏(Assault空对地打击)开发(4) 碰撞体和刚体的添加
  • Hugging Face GGUF 模型可视化
  • 【Redis_1】初识Redis
  • DeepSeek:人工智能领域的革新者与未来展望