日期:2026-07-23
项目:c260723
作者:WanJL
文件:Demo01.java
在父子类继承时,提取子类的共性功能时,有些方法在父类中并没有具体的实现。
例如员工体系:
员工类
|- 开发工程师
|- Java开发工程师
|- 前端开发工程师
|- 测试工程师
|- 后端测试工程师
|- IOS测试工程师
每一个子类的工作内容都是不一样的,所以都需要重写父类的 work() 方法,导致父类的 work() 方法代码没有实际作用。
public class 员工 {
public void work() {
// 每一个子类的工作内容都不一样,此方法体没有意义
}
}
在 Java 中,一个没有方法体的方法,应该定义为 抽象方法;如果类中包含抽象方法,那么这个类必须是 抽象类。
文件:Demo01.java
使用 abstract 关键字来定义抽象类和抽象方法。
定义抽象类:
public abstract class 类名 {
// 成员
}
定义抽象方法:
public abstract 返回值类型 方法名(参数列表);
注意:抽象方法没有方法体(没有
{},直接以分号结尾)。
文件:Demo01.java
| 特性 | 说明 |
|---|---|
| 关键字 | 抽象类和抽象方法必须使用 abstract 关键字修饰 |
| 抽象方法所在类 | 有抽象方法的类必须是抽象类 |
| 抽象类中的抽象方法 | 抽象类中不一定要有抽象方法,但抽象方法必须在抽象类中 |
| 实例化 | 抽象类不能实例化(不能创建对象) |
| 构造方法 | 抽象类可以有构造方法(供子类通过 super() 调用) |
| 子类选择 | ① 重写父类所有的抽象方法;② 子类也是抽象类,直接继承父类的抽象方法 |
| 继承关系 | 抽象父类和具体子类之间是继承关系,子类仍然属于父类类型 |
文件:Animal.java
package course;
public abstract class Animal {
public Animal() {
System.out.println("调用动物的构造方法....");
}
public abstract void eat();
public abstract void sleep();
public abstract void move();
public abstract void play();
}
eat()、sleep()、move()、play()super() 调用文件:Cat.java
package course;
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
@Override
public void sleep() {
// 空实现
}
@Override
public void move() {
// 空实现
}
@Override
public void play() {
// 空实现
}
}
extends 继承抽象类 Animal@Override 注解重写父类的所有抽象方法(必须全部重写,否则子类也要声明为抽象类)文件:Dog.java
package course;
public class Dog extends Animal {
public void eat() {
System.out.println("狗吃肉");
}
@Override
public void sleep() { }
@Override
public void move() { }
@Override
public void play() { }
}
Animal,重写所有抽象方法eat() 方法没有写 @Override 注解,但仍然是对父类抽象方法的重写(注解只是编译检查,不写也能重写)文件:Test01.java
package course;
public class Test01 {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // 输出:狗吃肉
}
}
Dog 对象,调用重写后的 eat() 方法文件:Emp.java
package course;
// 有抽象方法的类必须是抽象类
public abstract class Emp {
/**
* 抽象方法——没有方法体
*/
public abstract void work();
}
work()abstract 修饰文件:javaEmp.java
package course;
public class javaEmp extends Emp {
@Override
public void work() {
System.out.println("Java开发工程师在开发Java项目");
}
}
work() 抽象方法,给出具体的实现前端开发工程师、后端测试工程师 等)文件:BaseService.java
package course;
public abstract class BaseService {
public abstract void add();
public abstract void remove();
public abstract void update();
public abstract void query();
}
文件:UserService.java
package course;
public class UserService extends BaseService {
@Override
public void add() { }
@Override
public void remove() { }
@Override
public void update() { }
@Override
public void query() { }
}
文件:Demo02.java
在实际开发中,有些类、方法或变量不希望被改变:
final 关键字就是用来解决这些问题的——用于限制类、方法或变量被修改。
文件:Demo02.java、FinalClass.java
// 使用 final 修饰的类,不能被继承
public final class 类名 {
// ...
}
应用场景:
Math、Arrays),只能被调用,不能被继承String 类就是用 final 修饰的,不能被继承文件:FinalClass.java、SubClass.java
// 父类中的 final 方法
public class FinalClass {
final String SPRING = "春天";
public final void showInfo() {
System.out.printf(SPRING);
}
}
// 子类无法重写父类的 final 方法
public class SubClass extends FinalClass {
// ❌ 编译报错:不能重写父类的 final 方法
// public final void showInfo() {
// System.out.printf(SPRING);
// }
}
FinalClass 类本身没有用 final 修饰,所以 SubClass 可以继承它FinalClass 中的 showInfo() 方法用 final 修饰,子类 不能重写 该方法SubClass 中的重写代码,否则编译报错文件:Demo02.java、Test02.java
// final 修饰基本类型变量——值不可修改
final int i = 10;
// i = 20; // ❌ 编译报错:不能修改 final 变量的值
// final 修饰引用类型变量——引用地址不可变,但对象内容可以修改
final Person p = new Person("1001", "张三");
// p = new Person("1002", "李四"); // ❌ 编译报错:不能修改引用地址
p.setName("李四"); // ✅ 可以修改对象的属性
p.setId("1002");
System.out.printf(p.showInfo()); // 输出:Person{id='1002', name='李四'}
常量命名规范:Java 中一般使用 final 修饰的变量作为常量,常量名称通常 全部大写,单词之间用下划线 _ 分隔。例如:
final String SPRING = "春天"; // FinalClass.java
final int MAX_STUDENT_COUNT = 100;
final double PI = 3.1415926;
关键区别:
| 变量类型 | final 的作用 | 示例 |
|---|---|---|
| 基本类型(int、double 等) | 数值本身不可修改 | final int i = 10; → i=20; 报错 |
| 引用类型(对象、数组等) | 引用地址不可修改,但对象 属性 可修改 | final Person p = new Person(...) → p.setName(...) 可以 |
注意:
final修饰引用类型变量时,只是保证这个变量不再指向其他对象,但该对象内部的 成员变量 仍然可以通过 setter 修改。
| 关键字 | 修饰类 | 修饰方法 | 修饰变量 |
|---|---|---|---|
abstract |
类必须被继承 | 方法必须在子类中实现(无方法体) | 不能修饰变量 |
final |
类不能被继承 | 方法不能被重写 | 变量变成常量 |
⚠️
final和abstract不能同时使用,二者语义完全冲突:
abstract表示「需要被继承/重写」final表示「不能被继承/重写」
代码位于
exercise/exercise02包中,对应课堂练习题第 2 题。
文件:exercise/exercise02/Shape.java
package exercise.exercise02;
public abstract class Shape {
private String color;
public Shape(String color) {
this.color = color;
}
/** 计算面积——抽象方法 */
public abstract double getArea();
/** 计算周长——抽象方法 */
public abstract double getPerimeter();
/** 输出信息——普通方法(调用抽象方法) */
public void showInfo() {
System.out.print("颜色:" + this.color);
System.out.printf(",面积:%.2f", getArea());
System.out.printf(",周长:%.2f", getPerimeter());
System.out.println();
}
}
getArea() 和 getPerimeter()showInfo(),内部调用抽象方法输出结果文件:exercise/exercise02/Circle.java
package exercise.exercise02;
public class Circle extends Shape {
private double radius;
private double pi = 3.1415926;
public Circle(String color, double radius) {
super(color); // 调用父类构造方法
this.radius = radius; // 初始化子类属性
}
@Override
public double getArea() {
return pi * radius * radius;
}
@Override
public double getPerimeter() {
return 2 * pi * radius;
}
}
文件:exercise/exercise02/Test.java
package exercise.exercise02;
public class Test {
public static void main(String[] args) {
Circle circle = new Circle("红色", 5);
circle.showInfo();
// 输出:颜色:红色,面积:78.54,周长:31.42
}
}
文件:Demo03.java
模板方法模式是抽象类的经典应用场景。核心思想:父类定义算法的「骨架」(通用结构),子类实现特定的「步骤」(定制化需求)。
Java中的设计模式:模板模式
是把抽象类整体看成是一个模版,模板中不能决定的东西,抽象成方法
让使用模板的类(也就是抽象类的子类)去重写抽象方法,实现定制化的需求。
模板模式的优势:
模板已经定义了通用的结构,使用者只需要关心自己需要实现的功能就可以了
文件:MyOneDayTemplate.java
一般模板类分为两部分:
| 组成部分 | 关键字 | 说明 |
|---|---|---|
| 模板方法 | final |
定义执行步骤的骨架,子类不能修改(不能重写) |
| 抽象方法 | abstract |
需要被子类重写的方法,具体行为由子类决定 |
package course;
public abstract class MyOneDayTemplate {
/**
* 模板方法——使用 final 修饰,只能使用,不能修改
*/
public final void myOneDay() {
System.out.print("《我的一天》");
System.out.print("今天我吃了");
this.eat(); // 步骤 1:吃(抽象方法,子类实现)
System.out.print("然后我又");
this.sleep(); // 步骤 2:睡(抽象方法,子类实现)
System.out.print("最后我又玩了");
this.play(); // 步骤 3:玩(抽象方法,子类实现)
System.out.print("好开心哟~");
System.out.println();
}
// 抽象方法——由子类提供具体的实现
public abstract void eat();
public abstract void sleep();
public abstract void play();
}
关键点:
myOneDay()用final修饰,确保子类不能篡改这个方法的结构。子类只需关注eat()、sleep()、play()三个抽象方法的具体实现。
文件:ZhSanOneDay.java、LiSiOneDay.java
// 张三的一天
public class ZhSanOneDay extends MyOneDayTemplate {
@Override
public void eat() {
System.out.println("饭");
}
@Override
public void sleep() {
System.out.println("菜");
}
@Override
public void play() {
System.out.println("小狗");
}
}
// 李四的一天
public class LiSiOneDay extends MyOneDayTemplate {
@Override
public void eat() {
System.out.println("饭");
}
@Override
public void sleep() {
System.out.println("菜");
}
@Override
public void play() {
System.out.println("小狗");
}
}
文件:Test03.java
public class Test03 {
public static void main(String[] args) {
ZhSanOneDay zhSanOneDay = new ZhSanOneDay();
zhSanOneDay.myOneDay();
LiSiOneDay li = new LiSiOneDay();
li.myOneDay();
}
}
预期输出:
《我的一天》今天我吃了饭然后我又菜最后我又玩了好开心哟~
《我的一天》今天我吃了饭然后我又菜最后我又玩了好开心哟~
| 优势 | 说明 |
|---|---|
| 复用性 | 公共的流程代码写在父类的模板方法中,所有子类共用 |
| 扩展性 | 新增一种「一天」只需新建子类,重写抽象方法即可 |
| 规范性 | 父类通过模板方法强制规定了执行顺序和结构,子类无法改变 |
| 关注分离 | 使用者只需关心自己的定制逻辑(吃什么/睡什么/玩什么),不用关注整体流程 |
| 对比维度 | 普通继承 + 方法重写 | 模板方法模式 |
|---|---|---|
| 父类方法 | 普通方法,子类可选择重写或不重写 | 模板方法用 final 修饰,禁止重写 |
| 执行流程 | 由调用方决定调用顺序 | 父类模板方法定义固定执行顺序 |
| 强制约束 | 弱——子类可以不重写 | 强——子类必须重写抽象方法 |
| 设计意图 | 复用代码 | 定义算法骨架 + 强制子类实现特定步骤 |
练习目标:理解抽象类的定义和使用
public abstract class 声明,定义抽象方法时不要写方法体extends 继承抽象类,使用 @Override 重写所有抽象方法Q1:抽象类能不能被实例化?
不能。抽象类不能直接
new创建对象。
Q2:抽象类中一定要有抽象方法吗?
不一定。抽象类中可以没有抽象方法,但有抽象方法的类必须是抽象类。
Q3:抽象类的子类必须重写所有抽象方法吗?
不一定。子类有两个选择:① 重写父类所有抽象方法变为具体子类;② 子类也声明为抽象类,继承父类的抽象方法。
Q4:抽象类可以有构造方法吗?
可以。虽然抽象类不能直接创建对象,但其构造方法可以在子类实例化时通过
super()调用,用于初始化父类成员。
Q5:abstract 和 final 能否同时修饰一个类或方法?
不能。
abstract表示需要被继承/重写,final表示不能被继承/重写,二者语义冲突。
Q6:final 修饰引用类型变量时,对象内容还能修改吗?
可以。
final修饰引用类型变量时,只是保证引用地址不变(不能重新指向其他对象),但该对象内部的成员变量仍然可以通过 setter 修改。
Q7:final 修饰的类有哪些常见例子?
String类、Math类、Integer等包装类都是用final修饰的,它们不能被继承。
Q8:什么是模板方法设计模式?它的核心思想是什么?
模板方法模式(Template Method Pattern)是抽象类的经典应用场景。核心思想是:父类定义算法的「骨架」(通用结构),子类实现特定的「步骤」(定制化需求)。父类中的模板方法用
final修饰禁止重写,抽象方法用abstract修饰强制子类实现。
Q9:模板方法模式中,模板方法为什么用 final 修饰?
为了防止子类篡改算法的执行流程。如果模板方法不被
final修饰,子类可以重写它,破坏父类定义好的步骤结构,失去了「模板」的意义。