一、抽象类是什么
在Java中,抽象类是一个用来定义通用行为的类,它不能被实例化。抽象类可以包含抽象方法(没有实现的方法)和具体方法(有实现的方法)。抽象类的设计目的是为其他类提供一个基类,从而可以实现代码重用和多态。
二、抽象类详细介绍
1、例子引入
我们如果定义一个普通的 Animal 类,然后在这个类中写了一个动物叫的方法:
class Animal {
public void cry() {
System.out.println("...");
}
}
但是实际上这里只是一个基类,并没有代表什么动物,所以也没有具体的叫声,我们需要一个子类来继承这个基类,然后知道特定的动物后,才知道需要发出什么声音:
class Animal {
public void cry() {
System.out.println("...");
}
}
class Cat extends Animal {
@Override
public void cry() {
System.out.println("喵~");
}
}
从这里我们发现,对于 Animal 类中的 cry() 方法好像没有什么作用,只要有具体的动物类要继承这个 Animal 类,就会重写 cry() 方法,所以 Animal 类中的 cry() 方法就不会被调用,即使被调用,也没有实际意义。
所以说这里将 Animal 的 cry() 方法实现是没有意义的。
所以我们可以将 cry() 方法声明为抽象方法,但是抽象方法只能在抽象类中,所以我们也要将 Animal 声明为抽象类。任何包含一个或多个抽象方法的类必须被声明为抽象类。
这样上面的问题就解决了:
abstract class Animal {
public abstract void cry();// 声明为抽象方法
}
class Cat extends Animal {
@Override
public void cry() {
System.out.println("喵~");
}
}
2、抽象类(Abstract Class)
使用 abstract 关键字来定义一个抽象类。
abstract class AbstractClass {
}
抽象类可以包含抽象方法,也可以包含具体方法。
abstract class AbstractClass {
abstract void abstractMethod();// 抽象方法
// 使用 abstract 作为修饰符可以声明抽象方法。
void concreteMethod() {
// 具体方法
}
}
抽象方法只声明,不实现,子类要给出实现。具体方法就是已经实现的方法。
3、抽象类特点
1)不能实例化:
你不能直接创建抽象类的实例。尝试实例化将导致编译错误。
public class Example {
public static void main(String[] args) {
new AbstractClass();
}
}
abstract class AbstractClass {
}
2)抽象类和抽象方法的关系
- 抽象类可以没有抽象方法。
- 抽象方法只能在抽象类中。
- 只要类中有抽象方法,则类需要被声明为抽象类。
3)抽象类可以有任意成员
抽象类可以有任意成员,包括但不限于非抽象方法,静态方法,构造方法,实例属性,静态属性。因为抽象类的本质还是类。只不过它相对于一般类多了可以包含抽象方法这一个特点。
- 抽象类可以包含成员变量,并且可以有任何访问修饰符(public, protected, private)。
- 尽管不能实例化抽象类,但它可以有构造函数。这些构造函数通常用于子类构造时初始化通用属性。
abstract class AbstractClass {
private int a;// 实例属性
public static int b;// 静态属性
public AbstractClass() {
a = 0;// 构造器
}
abstract void bar();// 抽象方法
public static void foo() {
// 静态方法
}
// ...
}
4)抽象类继承
抽象类被子类继承后,子类必须实现所有抽象类的抽象方法,除非子类也是抽象类。
abstract class A {
public abstract void foo();
public abstract void bar();
}
abstract class B extends A {
// 子类为抽象类
}
class C extends A {
@Override
public void foo() {
}
@Override
public void bar() {
}
// 实现所有抽象方法
}
5)抽象方法不能使用的修饰符
抽象方法不能使用 static、final 和 private 修饰符,这是因为这些修饰符与抽象方法的性质相冲突:
- static:静态态方法是与类相关的,而抽象方法是与实例相关的,需要在子类中实现。
- final:最终方法不能被重写,而抽象方法本质上需要在子类中实现。
- private:私有方法是不能在子类中访问的,而抽象方法需要在子类中实现。
6)抽象类的多态性
抽象类也具有多态性,抽象类的引用可以指向一般子类的对象。
// 定义一个抽象类
abstract class Animal {
// 抽象方法
abstract void makeSound();
// 具体方法
void eat() {
System.out.println("This animal is eating.");
}
}
// 定义一个子类 Dog
class Dog extends Animal {
@Override
void makeSound() {
System.out.println("Woof!");
}
}
// 定义一个子类 Cat
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("Meow!");
}
}
// 测试类
public class Main {
public static void main(String[] args) {
// 使用多态性
Animal myDog = new Dog();
Animal myCat = new Cat();
myDog.makeSound(); // 输出: Woof!
myCat.makeSound(); // 输出: Meow!
myDog.eat(); // 输出: This animal is eating.
myCat.eat(); // 输出: This animal is eating.
}
}
7)补充
abstract 只能修饰类和方法。
三、抽象类的最佳实践——模版设计模式
public class Example {
public static void main(String[] args) {
A a = new A();
a.calculate();
B b = new B();
b.calculate();
}
}
abstract class Template {
public void calculate() {
long startTime = System.currentTimeMillis();
job();
long endTime = System.currentTimeMillis();
System.out.println("执行时间:" + (endTime - startTime) + "ms");
}
public abstract void job();
}
class A extends Template {
@Override
public void job() {
for (int i = 0; i < 1000000; i++) {
System.out.print("");
}
}
}
class B extends Template {
@Override
public void job() {
for (int i = 0; i < 10000000; i++) {
System.out.print("");
}
}
}
运行结果:
这个代码通过定义一个抽象类 Template,然后为这个抽象类定义了一个具体方法 calculate,实现了一个通用的算法框架,用于计算某个任务的执行时间。中间会调用具体的任务方法 job,job 方法是在抽象类 Template 中声明为抽象方法,等待子类实现这个方法,子类可以实现具体的任务方法 job。
在使用自类的实例调用 calculate 方法时,执行到调用 job 方法时,这时就会因为动态绑定而调用子类实现的 job 方法,因为这时的运行时类型是子类。
具体来说:
- 当调用
A a = new A(); a.calculate();
时,calculate
方法会执行job
方法的A
类实现。因为a
的运行时类型是A
。 - 同样地,当调用
B b = new B(); b.calculate();
时,calculate
方法会执行job
方法的B
类实现。因为b
的运行时类型是B
。
这种模式的优点在于可以将通用的行为(如时间测量)封装在一个方法中,并允许子类通过实现特定的任务逻辑来扩展通用行为。这样可以减少代码重复,提高代码的可维护性和可扩展性。