Java 抽象类
Java 抽象类
在 Java 继承体系中,父类通常包含子类的共同特征,但部分父类会设计得过于通用,以至于无需创建具体实例(因为某些方法的实现依赖子类的具体类型)。这种“无法实例化、包含抽象方法”的类称为抽象类,它是连接通用父类与具体子类的关键桥梁。所以规定抽象类不能创建实例。
抽象类的核心概念
定义与本质
- 抽象类:用
abstract关键字修饰的类,无法通过new关键字创建实例,仅作为子类的模板。 - 抽象方法:用
abstract关键字修饰的方法,只有方法声明(无方法体),其实现由子类提供。 - 核心本质:抽象类是对“共同特征”的抽象,抽象方法是对“共同行为”的声明——父类明确子类必须具备该行为,但不关心具体实现方式。
为什么需要抽象类?
以几何图形(Geometric)为例:
- 所有几何图形(圆、矩形)都有“面积”和“周长”,但计算方式完全不同。
- 若
Geometric类定义普通方法getArea()和getPerimeter(),无法给出通用实现;若不定义这些方法,工具类无法统一操作所有几何图形(需强制类型转换,降低灵活性)。 - 抽象类的解决方案:将
getArea()和getPerimeter()声明为抽象方法,子类必须实现,既保证了“行为统一”,又允许“实现差异化”。
抽象类与抽象方法的定义
抽象类的语法格式
[访问修饰符] abstract class 类名 {
// 普通属性
// 普通方法(有方法体)
// 抽象方法(无方法体,用 abstract 修饰)
public abstract 返回值类型 方法名(参数列表);
// 构造方法(用于子类初始化,不能创建自身实例)
}
示例:抽象类 Geometric
package com.inherit.inherit;
import java.time.LocalDate;
/**
* 抽象类可以有构造方法,但是不能创建抽象类的实例,交给子类的构造方法进行调用
* @author Jing61
*/
public abstract class Geometric {
private LocalDate createDate;
private String color;
private boolean filled;
public Geometric() {
this.createDate = LocalDate.now();//当前系统日期
}
public Geometric(String color, boolean filled) {
this.createDate = LocalDate.now();//当前系统日期
this.color = color;
this.filled = filled;
}
public LocalDate getCreateDate() {
return createDate;
}
public void setCreateDate(LocalDate createDate) {
this.createDate = createDate;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public boolean isFilled() {
return filled;
}
public void setFilled(boolean filled) {
this.filled = filled;
}
/**
* 抽象方法:所有的子类都具有的共同的行为特征,该行为特征在父类不可描述----抽象方法
* 只有方法的定义没有方法的实现,交由子类去实现
* 包含抽象方法的类必须是一个抽象类
* @return
*/
public abstract double getArea();
public abstract double getPerimeter();
}
抽象类的子类实现
抽象类的子类必须满足以下条件之一:
- 实现父类的所有抽象方法(成为具体类,可实例化);
- 若未自身实现父类的所有抽象方法,也需要声明为抽象类(交由下一级子类实现抽象方法)。
子类 1:Circle(圆,实现所有抽象方法)
package com.inherit.inherit;
/**
* 子类继承抽象类,必选实现抽象类中所有的抽象方法,否则子类也应该是一个抽象类,交由具体的子类去实现
* @author Jing61
*/
public class Circle extends Geometric{
public static final double PI = 3.14;
private double radius;
public Circle() {
}
public Circle(double radius) {
this.radius = radius;
}
public Circle(double radius,String color,boolean filled) {
super(color,filled);
this.radius = radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
/**
* 计算圆几何图形的面积
*/
@Override
public double getArea() {
return PI * radius * radius;
}
@Override
public double getPerimeter() {
return 2 * PI * radius;
}
@Override
public String toString() {
String ret = "circle:(area:" + getArea() + ",perimeter:" + getPerimeter();
if(isFilled())
ret += "filled, color:" + getColor();
return ret + ")";
}
}
子类 2:Rectangle(矩形,实现所有抽象方法)
package com.inherit.inherit;
import java.time.LocalDate;
public class Rectangle extends Geometric{
private double width;
private double height;
public Rectangle() {
}
public Rectangle(double width,double height) {
this.width = width;
this.height = height;
}
public Rectangle(double width,double height,String color,boolean filled) {
super(color,filled);
this.width = width;
this.height = height;
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
@Override
public double getPerimeter() {
return 2 * (width + height);
}
@Override
public double getArea() {
return width * height;
}
}
抽象类的实际应用
可能大家会提出,既然Geometric类不知道如何实现getArea()和getPerimeter()方法,那就干脆不要定义它了,这不是一个好的思路。抽象类的核心价值是“统一接口、差异化实现”,如下面程序所示,现在有一个工具方法用于去比较两个几何图形的面积是否相同及展示一個几何对象,从中就可以看出使用抽象方法的优势(父类更加通用):
测试类 GeometricDemo
package com.inherit.inherit;
/**
* 抽象类的使用
* @author Jing61
*/
public class GeometricDemo {
public static void main(String[] args) {
Geometric geometric1 = new Circle(5);
Geometric geometric2 = new Rectangle(5,4);
System.out.println("两个几何对象的面积是否相等:" + equalArea(geometric1, geometric2));
displayGeometricObject(geometric1);
displayGeometricObject(geometric2);
}
public static boolean equalArea(Geometric object1, Geometric object2) {
//如果父类没有定义getArea(),那么此处就不能调用,必须类型强转为子类类型,这就降低了程序的灵活性
return object1.getArea() == object2.getArea();
}
public static void displayGeometricObject(Geometric object) {
System.out.println();
System.out.println("The area is " + object.getArea());
System.out.println("The perimeter is " + object.getPerimeter());
}
}
应用优势
- 通用性:工具方法
equalArea()和displayGeometricObject()可处理所有Geometric子类对象,无需修改代码即可支持新的几何图形(如三角形Triangle)。 - 规范性:子类必须实现抽象方法,保证所有几何图形都具备“计算面积和周长”的行为,避免遗漏。
抽象类的核心规则
- 修饰符要求:抽象类和抽象方法必须用
abstract修饰;抽象方法无方法体。 - 实例化限制:抽象类不能实例化(实例化是为了调用属性和方法,抽象类本身没有方法实现),抽象类不能通过
new关键字创建实例(即使不含抽象方法,也无法实例化)。 - 成分支持:抽象类可包含普通属性、普通方法、构造器、初始化块、内部类、枚举类;构造器用于子类初始化,不能创建自身实例。
- 子类要求:
- 继承抽象类的子类,必须实现父类所有抽象方法(否则子类需声明为抽象类)。
- 若子类继承抽象父类后未完全实现抽象方法,或实现接口未完全实现抽象方法,子类必须为抽象类。
Java 内置抽象类示例
示例 1:java.lang.Number
Number 是抽象类,定义了数值类型的共同方法(如 intValue()、doubleValue()),子类包括 Integer、Double、Long 等,需实现父类的抽象方法以完成类型转换。
其类结构示意:

示例 2:java.util.Calendar
Calendar 是抽象基类,用于提取日历信息(年、月、日、时等),子类 GregorianCalendar(公历类)是其具体实现,支持不同日历系统的扩展(如农历、犹太历)。
代码示例:使用 GregorianCalendar 打印日历
package com.inherit.inherit;
import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.GregorianCalendar;
/**
* GregorianCalendar是抽象类Calendar的一个具体实现子类
* java.util.Date的实例表示以毫秒为精度的特定时刻,java.util.Calendar是抽象基类,可提取详细日历信息
* Calendar的子类可以实现特定的日历系统(公历、农历、犹太历等)
*
* 核心方法:
* get(int field):提取日期和时间信息(field为Calendar常量)
* set(int field, int value):设置日期和时间信息
*
* Calendar常量说明:
* YEAR:年份
* MONTH:月份(0表示1月,11表示12月)
* DATE/DAY_OF_MONTH:当月天数
* HOUR:小时(12小时制)
* HOUR_OF_DAY:小时(24小时制)
* MINUTE:分钟
* SECOND:秒
* DAY_OF_WEEK:一周的天数(1表示星期日)
* DAY_OF_YEAR:当年的天数
* AM_PM:上午(0)/下午(1)
* @author Jing61
*/
public class PrintCalendarDemo {
public static void main(String[] args) {
GregorianCalendar calendar = new GregorianCalendar();
int today = calendar.get(Calendar.DAY_OF_MONTH);
int month = calendar.get(Calendar.MONTH);//从0开始
//设置日历为这个月的第一天
calendar.set(Calendar.DAY_OF_MONTH, 1);
//得到这一天为星期几
int weekday = calendar.get(Calendar.DAY_OF_WEEK);
//得到星期的起始日
int firstDayOfWeek = calendar.get(Calendar.DAY_OF_WEEK_IN_MONTH);
int indent = 0;
while(weekday != firstDayOfWeek) {
indent++;
calendar.add(Calendar.DAY_OF_MONTH, -1);
weekday = calendar.get(Calendar.DAY_OF_WEEK);
}
String[] weekdayNames = new DateFormatSymbols().getShortWeekdays();
do {
System.out.printf("%4s",weekdayNames[weekday]);
calendar.add(Calendar.DAY_OF_MONTH, 1);
weekday = calendar.get(Calendar.DAY_OF_WEEK);
}while(weekday != firstDayOfWeek);
System.out.println();
for(int i = 1; i <= indent; i++) {
System.out.print(" ");
}
calendar.set(Calendar.DAY_OF_MONTH, 1);
do {
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.printf("%5d", day);
calendar.add(Calendar.DAY_OF_MONTH, 1);
if(day == today)System.out.print("* ");
else System.out.print(" ");
weekday = calendar.get(Calendar.DAY_OF_WEEK);
if(weekday == firstDayOfWeek) System.out.println();
}while(calendar.get(Calendar.MONTH) == month);
}
}

浙公网安备 33010602011771号