一、抽象类:抽象概念的类
1.1 什么是抽象类?
抽象类就是用 abstract 修饰的类,不能直接实例化(不能 new)。
**为什么需要抽象类? **
举个例子:
-
"动物"是一个抽象概念,你不能创造一个"动物",只能说"这是猫"或"这是狗"
-
"形状"是一个抽象概念,你不能说"给我画一个形状",只能说"画个圆"或"画个方"
**抽象类的特点: **
-
用
abstract修饰 -
不能直接
new创建对象 -
可以有抽象方法(没有方法体的方法)
-
也可以有普通方法和属性
1.2 抽象类的语法
// 抽象类:用abstract修饰
public abstract class Animal {
protected String name;
// 抽象方法:没有方法体,子类必须实现
public abstract void sound();
// 普通方法:有方法体,子类可以直接继承
public void eat() {
System.out.println(name + "在吃东西");
}
// 构造方法(子类调用)
public Animal(String name) {
this.name = name;
}
}
**子类继承抽象类: **
// 用extends继承抽象类
public class Cat extends Animal {
public Cat(String name) {
super(name); // 调用父类构造
}
// 必须实现父类的抽象方法
@Override
public void sound() {
System.out.println(name + "喵喵喵");
}
}
**使用: **
public class Main {
public static void main(String[] args) {
// 不能直接new抽象类
// Animal a = new Animal(); // 错误!
// 只能创建子类对象
Animal cat = new Cat("小花");
cat.eat(); // 继承来的方法
cat.sound(); // 重写的方法
}
}
1.3 抽象方法
抽象方法是只有方法签名、没有方法体的方法。
public abstract class Animal {
// 抽象方法:没有{}
public abstract void sound();
public abstract void move();
}
**子类必须实现所有抽象方法,否则子类也必须是抽象类。 **
二、接口:行为的契约
2.1 什么是接口?
接口是一种行为规范,它定义了一组"能做什么"的方法,但不关心"怎么做"。
**打个比方: **
-
USB接口:定义了供电和数据传输的规范,不管你是U盘还是鼠标,只要符合这个接口就能用
-
Java接口:定义了一组方法的规范,不管哪个类实现,只要符合接口就能互换
2.2 接口的语法
// 定义接口:用interface
public interface Flyable {
// 接口中的方法默认是抽象的(Java 7以前)
// 抽象方法:没有方法体
void fly();
// 接口中的属性默认是常量(public static final)
int MAX_SPEED = 1000;
}
类实现接口:用 implements
// 鸟实现飞接口
public class Bird implements Flyable {
@Override
public void fly() {
System.out.println("鸟儿扇动翅膀飞翔");
}
}
// 飞机实现飞接口
public class Airplane implements Flyable {
@Override
public void fly() {
System.out.println("飞机发动机推动飞翔");
}
}
**使用: **
public class Main {
public static void main(String[] args) {
// 接口引用指向实现类对象
Flyable f1 = new Bird();
Flyable f2 = new Airplane();
f1.fly(); // 鸟儿扇动翅膀飞翔
f2.fly(); // 飞机发动机推动飞翔
}
}
2.3 接口的特点
-
接口不能实例化(不能new)
-
接口的方法默认是抽象的(Java 7及以前)
-
接口的属性默认是常量(public static final)
-
一个类可以实现多个接口(弥补单继承的不足)
-
接口可以继承接口(多继承)
2.4 接口的多实现
// 接口1
public interface Runnable {
void run();
}
// 接口2
public interface Eatable {
void eat();
}
// 一个类可以实现多个接口
public class Person implements Runnable, Eatable {
@Override
public void run() {
System.out.println("人在跑步");
}
@Override
public void eat() {
System.out.println("人在吃饭");
}
}
**这解决了单继承的问题——类只能继承一个父类,但可以实现多个接口。 **
2.5 接口的默认方法(Java 8)
**Java 8开始,接口可以有默认方法: **
public interface Flyable {
// 抽象方法
void fly();
// 默认方法:用default修饰,有方法体
default void land() {
System.out.println("安全降落");
}
}
**作用: ** 如果接口需要新增方法,旧实现类不用全部重写。
public class Bird implements Flyable {
@Override
public void fly() {
System.out.println("鸟儿飞翔");
}
// 不需要实现land(),可以直接用默认的
}
// 使用
Flyable b = new Bird();
b.land(); // 输出:安全降落
2.6 接口的静态方法(Java 8)
public interface MathUtil {
static int max(int a, int b) {
return a > b ? a : b;
}
}
// 调用:直接用接口名调用
int m = MathUtil.max(10, 20); // 20
三、抽象类 vs 接口
3.1 核心区别
| 对比项 | 抽象类 | 接口 |
|--------|--------|------|
| 关键字 | abstract class | interface |
| 继承/实现 | extends(单继承) | implements(多实现) |
| 属性 | 无限制 | 默认常量 public static final |
| 方法 | 抽象 + 普通都可以 | Java 7:抽象方法;Java 8+:抽象 + 默认 + 静态 |
| 构造方法 | 有 | 没有 |
| 什么时候用 | "是什么"的关系 | "能做什么"的关系 |
3.2 选择原则
**用抽象类: **
-
类之间有明显的"是...的"关系(猫是动物)
-
需要共享代码(普通方法)
-
需要控制属性访问权限
**用接口: **
-
类之间有相同的行为但没有继承关系
-
需要多实现(一个类实现多个接口)
-
需要解耦(面向接口编程)
3.3 经典例子:门
// 抽象类:门(抽象概念)
abstract class Door {
protected String material;
public Door(String material) {
this.material = material;
}
// 开门(普通方法)
public void open() {
System.out.println("门打开了");
}
// 关门(普通方法)
public void close() {
System.out.println("门关上了");
}
// 报警(抽象方法,子类实现)
public abstract void alarm();
}
// 木门
class WoodenDoor extends Door {
public WoodenDoor() {
super("木头");
}
@Override
public void alarm() {
System.out.println("木头门发出警报:哔哔哔");
}
}
// 铁门
class IronDoor extends Door {
public IronDoor() {
super("铁");
}
@Override
public void alarm() {
System.out.println("铁门发出警报:滴滴滴");
}
}
四、综合练习:宠物店系统
4.1 需求
用接口和抽象类,实现一个宠物店系统:
-
抽象类 Pet:宠物的基本属性
-
接口 Jumpable:能跳的动作
-
接口 Eatable:能吃的动作
4.2 代码
// 抽象类:宠物
abstract class Pet {
protected String name;
protected int age;
public Pet(String name, int age) {
this.name = name;
this.age = age;
}
// 普通方法
public void showInfo() {
System.out.println("宠物名:" + name + ",年龄:" + age);
}
}
// 接口:能跳
interface Jumpable {
void jump();
}
// 接口:能吃
interface Eatable {
void eat();
}
// 狗:继承Pet,实现Jumpable和Eatable
class Dog extends Pet implements Jumpable, Eatable {
public Dog(String name, int age) {
super(name, age);
}
@Override
public void jump() {
System.out.println(name + "跳起来了!");
}
@Override
public void eat() {
System.out.println(name + "在吃狗粮");
}
}
// 猫:继承Pet,实现Jumpable和Eatable
class Cat extends Pet implements Jumpable, Eatable {
public Cat(String name, int age) {
super(name, age);
}
@Override
public void jump() {
System.out.println(name + "轻盈地跳上了桌子");
}
@Override
public void eat() {
System.out.println(name + "在吃猫粮");
}
}
// 主程序
public class Main {
public static void main(String[] args) {
Pet[] pets = {
new Dog("旺财", 3),
new Cat("小白", 2)
};
System.out.println("=== 宠物店 ===");
for (Pet pet : pets) {
pet.showInfo();
// 强制类型转换后调用接口方法
if (pet instanceof Jumpable) {
((Jumpable) pet).jump();
}
if (pet instanceof Eatable) {
((Eatable) pet).eat();
}
System.out.println();
}
// 统一管理:能跳的宠物
System.out.println("=== 能跳的宠物 ===");
for (Pet pet : pets) {
if (pet instanceof Jumpable) {
Jumpable j = (Jumpable) pet;
j.jump();
}
}
}
}
4.3 运行结果
=== 宠物店 ===
宠物名:旺财,年龄:3
旺财跳起来了!
旺财在吃狗粮
宠物名:小白,年龄:2
小白轻盈地跳上了桌子
小白在吃猫粮
=== 能跳的宠物 ===
旺财跳起来了!
小白轻盈地跳上了桌子
五、常见错误
❌ 错误1:抽象类能直接new
public abstract class Animal {
public abstract void sound();
}
// 错误!抽象类不能实例化
Animal a = new Animal();
// 正确
Animal a = new Cat();
❌ 错误2:接口属性不是常量
public interface Flyable {
// 错误!这是常量,默认是 public static final
int MAX_SPEED = 1000;
// 如果写:
int speed; // 等价于 public static final int speed = 1000;
}
❌ 错误3:接口方法漏写public
public interface Flyable {
// Java 7以前,必须写public
void fly(); // 等价于 public abstract void fly();
// 错误!子类实现时需要public,这里不写会报错
void land();
}
❌ 错误4:抽象方法没有全部实现
public abstract class Animal {
public abstract void sound();
public abstract void move();
}
// 错误!子类必须实现所有抽象方法
class Cat extends Animal {
@Override
public void sound() {
System.out.println("喵");
}
// 缺少move()的实现,报错!
}
// 正确
class Cat extends Animal {
@Override
public void sound() {
System.out.println("喵");
}
@Override
public void move() {
System.out.println("猫在走");
}
}
六、本课总结
**抽象类: **
-
用
abstract class定义 -
单继承,有构造方法
-
适合"是什么"的关系
-
可以有抽象方法和普通方法
**接口: **
-
用
interface定义 -
多实现,无构造方法
-
适合"能做什么"的关系
-
Java 7:抽象方法;Java 8+:默认方法、静态方法
**选择: **
-
有继承关系,用抽象类
-
需要多实现,用接口
-
既要继承又要多实现:先extends一个类,再implements多个接口
七、下节课预告
第6课:常用类
-
String 字符串
-
StringBuilder 可变字符串
-
日期时间 API
-
包装类
学完这课,你就能更高效地处理数据了。
**关注我,跟着老兵学Java,少走弯路。 **
💬 **评论区聊聊:抽象类和接口,哪个更容易搞混? **