日期: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='李四'}
关键区别:
| 变量类型 | 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
}
}
练习目标:理解抽象类的定义和使用
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修饰的,它们不能被继承。