理解抽象的核心思想:隐藏实现,暴露接口
掌握通过 抽象类 和 接口 实现抽象的两种方式
能够设计合理的抽象层次结构(如 Shape → Circle/Rectangle)
避免常见误区:未实现抽象方法、过度抽象等
在实际项目中合理运用抽象提升代码可维护性与安全性
抽象是面向对象编程(OOP)的四大核心原则之一,其核心思想是:
“关注对象能做什么,而不是它如何做。”
生活中的抽象示例
想象一个电视遥控器:
你只需按“开机”按钮,电视就开了
你不需要知道内部电路、红外信号编码、电源管理等复杂细节
这就是抽象:将复杂实现隐藏,只暴露必要操作。
使用 abstract 关键字声明
不能被实例化(new AbstractClass())
可包含:
抽象方法(无方法体,子类必须实现)
具体方法(有实现)
实例变量、静态变量
构造器(用于初始化共享状态)
// 抽象基类
abstract class Shape {
String color;
// 抽象方法:子类必须实现
abstract double area();
public abstract String toString();
// 具体方法:提供通用功能
public String getColor() {
return color;
}
// 构造器:初始化公共属性
public Shape(String color) {
System.out.println("Shape constructor called");
this.color = color;
}
}
// 具体子类:Circle
class Circle extends Shape {
double radius;
public Circle(String color, double radius) {
super(color); // 调用父类构造器
System.out.println("Circle constructor called");
this.radius = radius;
}
@Override
double area() {
return Math.PI * Math.pow(radius, 2);
}
@Override
public String toString() {
return "Circle color is " + super.getColor() +
" and area is: " + area();
}
}
// 具体子类:Rectangle
class Rectangle extends Shape {
double length, width;
public Rectangle(String color, double length, double width) {
super(color);
System.out.println("Rectangle constructor called");
this.length = length;
this.width = width;
}
@Override
double area() {
return length * width;
}
@Override
public String toString() {
return "Rectangle color is " + super.getColor() +
" and area is: " + area();
}
}
// 测试类
public class Test {
public static void main(String[] args) {
Shape s1 = new Circle("Red", 2.2);
Shape s2 = new Rectangle("Yellow", 2, 4);
System.out.println(s1.toString());
System.out.println(s2.toString());
}
}输出:
Shape constructor called
Circle constructor called
Shape constructor called
Rectangle constructor called
Circle color is Red and area is: 15.205308443374602
Rectangle color is Yellow and area is: 8.0
关键点:
通过
Shape引用调用area(),实现多态子类继承公共属性(
color)和方法(getColor())抽象方法强制子类提供具体实现
完全抽象
使用 interface 关键字定义
所有方法默认为 public abstract(除非是 default 或 static)
所有字段默认为 public static final(即常量)
类通过 implements 实现接口
支持多重继承(一个类可实现多个接口)
// 定义行为契约
interface Shape {
double calculateArea(); // 抽象方法
}
class Circle implements Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
class Rectangle implements Shape {
private double length, width;
public Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override
public double calculateArea() {
return length * width;
}
}
public class Main {
public static void main(String[] args) {
Shape cir = new Circle(5.0);
Shape rect = new Rectangle(4.0, 6.0);
System.out.println("Area of Circle: " + cir.calculateArea());
System.out.println("Area of Rectangle: " + rect.calculateArea());
}
}输出:
Area of Circle: 78.53981633974483
Area of Rectangle: 24.0优势:
完全解耦:
Main只依赖Shape接口,不关心具体类型易于扩展:新增
Triangle只需实现Shape支持组合:一个类可同时实现
Shape,Drawable,Serializable等
关系:抽象关注行为层面,封装关注数据层面,二者相辅相成。
简化复杂系统:用户只需关注接口,无需理解底层
提高可维护性:修改实现不影响调用方(只要接口不变)
增强安全性:不暴露敏感实现细节
促进模块化:各组件通过抽象契约协作,降低耦合度
过度抽象:增加不必要的类 / 接口,使代码难以理解
性能开销:方法调用可能涉及动态分派(但现代 JVM 优化良好)
调试困难:对新手而言,追踪抽象链可能较复杂
灵活性受限:一旦接口发布,修改成本高(需考虑向后兼容)
经验法则:
优先考虑接口(更灵活)
当需要共享实现或状态时,再用抽象类
抽象是隐藏实现细节,暴露必要功能的设计思想
Java 通过 抽象类(部分抽象) 和 接口(完全抽象) 实现
抽象类适合有共享状态 / 代码的场景;接口适合定义行为契约
合理使用抽象可提升代码的可维护性、安全性和扩展性
避免过度抽象和未实现抽象方法等常见错误
在什么情况下,你会选择使用抽象类而不是接口?请结合实际项目场景说明。
Java 8 引入了接口中的 default 方法,这是否削弱了“接口代表完全抽象”的概念?为什么?
如果一个系统中大量使用抽象,但几乎没有具体实现变化的需求,这种设计是否合理?为什么?