文章目录
1.抽象类和接口的引入
通过前一节我们已经了解了继承,知道了通过继承可以从父类继承下来一些东西
如果所有子类从父类那里继承下来的东西(方法 2)都需要重写,那么说明继承下来的具体功能的实现是无用的。这个时候就要使用 Java 为我们提供了一种特殊的父类称作抽象类啦,它只定义功能名称而不去定义具体实现细节,将其留给子类自己去实现。
这样,发现抽象父类相当于一个上帝,我决定了子类必须拥有某方法(方法 2),但是方法内具体有什么东西将由你们你们自己去实现。但是碍于 Java 是单继承的,就会有一定的局限性,所以又出现了一个全是空实现方法(抽象方法)的特殊抽象类,即接口。
使用了接口,那么所有的空实现方法(抽象方法),则都需要子类自己去实现啦。
接下来让我们看看 抽象类 和接口的具体使用吧!
2. 抽象类
抽象类是一种特殊的类,它不能被实例化,只能被用作其他类的基类
抽象类的主要作用是为子类提供一个通用的模板,并规定子类必须实现的抽象方法
示例代码:
// 抽象类
abstract class Shape {
// 普通方法
public void display() {
System.out.println("This is a shape.");
}
// 抽象方法,子类必须实现
abstract double area();
}
在上述例子中,Shape
是一个抽象类,它包含了一个普通方法 display()
和一个抽象方法 area()
。抽象方法没有具体的实现,而是由子类来实现。
1)继承抽象类
子类继承抽象类时,必须实现抽象类中的所有抽象方法,否则子类也必须声明为抽象类
示例代码:
// 子类继承抽象类
class Circle extends Shape {
private double radius;
// 构造方法
public Circle(double r) {
radius = r;
}
// 实现抽象方法
@Override
double area() {
return Math.PI * radius * radius;
}
}
2)使用抽象类的实例
虽然抽象类不能被实例化,但可以通过子类的实例进行引用
示例代码:
public class Main {
public static void main(String[] args) {
Circle circle = new Circle(5.0);
circle.display(); // 调用抽象类中的普通方法
double area = circle.area(); // 调用抽象类中的抽象方法
System.out.println("Area of the circle: " + area);
}
}
在这个例子中,我们通过 Circle
类的实例来调用抽象类 Shape
中的方法
3)抽象类的特性
- 包含一个抽象方法的类必须是抽象类
- 抽象类和抽象方法都要使用
abstract
关键字声明 - 抽象方法只需声明而不需要实现
- 抽象类必须被子类继承,子类必须重写抽象类中的全部抽象方法(子类如果是抽象类除外)
3. 接口
接口(Interface)是一种抽象类型,它定义了一组抽象方法,但是不包含具体的实现
接口可以被类实现,使得类能够遵循接口定义的契约,实现接口的类必须提供接口中定义的所有方法
1)创建接口
使用 interface
关键字可以定义一个接口。接口中可以包含抽象方法、常量和默认方法
示例代码:
// 定义一个接口
interface Shape {
// 抽象方法
double area();
// 常量
double PI = 3.14;
// 默认方法
default void display() {
System.out.println("This is a shape.");
}
}
在上述例子中,Shape
是一个接口,定义了一个抽象方法 area()
、一个常量 PI
和一个默认方法 display()
。
2)实现接口
类通过关键字 implements
实现接口,并提供接口中定义的所有方法的具体实现
示例代码:
// 实现接口
class Circle implements Shape {
private double radius;
// 构造方法
public Circle(double r) {
radius = r;
}
// 实现抽象方法
@Override
public double area() {
return PI * radius * radius;
}
}
在上述例子中,Circle
类通过 implements
实现了 Shape
接口,并提供了 area()
方法的具体实现。
3)使用接口
可以通过接口类型引用实现该接口的对象
示例代码:
public class Main {
public static void main(String[] args) {
Circle circle = new Circle(5.0);
circle.display(); // 调用接口中的默认方法
double area = circle.area(); // 调用接口中的抽象方法
System.out.println("Area of the circle: " + area);
}
}
在这个例子中,我们通过 Shape
接口类型引用了 Circle
类的对象
4)接口的特性
- 一个类可以实现多个接口。
- 接口可以继承(extends)其他接口。
- 接口中的方法默认是公有的(public),不需要使用
public
修饰符。 - 接口中的字段默认是常量,必须使用
public static final
修饰符。
4.抽象类和接口的区别
抽象类 | 接口 | |
---|---|---|
定义 | 抽象类是一种不能被实例化的类,它用于包含一些抽象方法和可以包含普通方法的类。抽象方法是没有具体实现的方法,需要在子类中被具体实现。 | 接口是一种完全抽象的类型,它定义了一组抽象方法,但不包含实现。实现接口的类需要提供具体的实现。 |
关键字 | 使用 abstract 关键字来声明抽象类,可以包含抽象方法和非抽象方法。 | 使用 interface 关键字来声明接口。 |
构造方法 | 抽象类可以有构造方法,它可以被子类调用。 | 接口不能包含构造方法,因为接口不能被实例化。 |
访问修饰符 | 抽象类的方法可以有各种访问修饰符,包括 public、protected、default、private。 | 接口的方法默认为 public 抽象方法,不能有 private、protected、default 访问修饰符。 |
变量 | 抽象类中可以包含实例变量、静态变量、常量等。 | 接口中只能包含常量,即 public static final 修饰的变量。 |
继承 | 一个类只能继承一个抽象类。 | 一个类可以实现多个接口。 |
5.共同点
- 抽象方法: 抽象类和接口都可以包含抽象方法,需要在子类或实现类中提供具体实现
- 多态性: 抽象类和接口都支持多态性,子类或实现类可以替代抽象类或接口类型
- 扩展性: 抽象类和接口都支持代码的扩展,通过子类或实现类的添加,可以扩展系统功能
6.如何选择
- 是否需要构造方法: 如果你需要构造方法或者想要有构造方法的类,那么选择抽象类。接口不能包含构造方法。
- 单继承 vs 多实现: 如果一个类需要继承多个类,那么选择接口。Java支持单继承,但可以实现多个接口。
- 共享代码: 如果多个类之间有一些共享的代码,可以将这些代码放在一个抽象类中。如果只是希望定义一些规范,而不包含任何实现,那么选择接口。
- 设计层次结构: 如果你需要设计一个类层次结构,并且希望子类共享一些通用行为,那么选择抽象类。
- 解耦: 接口有助于实现类之间的解耦。如果你希望将实现类与接口解耦,使得系统更加灵活,那么选择接口。
总的来说,抽象类和接口各有其优势和适用场景。在设计时,根据具体需求选择合适的方式,有时也可以同时使用抽象类和接口来实现更灵活的设计。