【Java 基础】抽象类与接口详解
一、抽象类(abstract)
1.1 为什么要有抽象类?
在面向对象的继承体系中,父类往往需要定义子类的通用行为,但某些行为在父类中无法确定具体实现。
举个例子:定义一个Animal类,其中eat()(吃)和speak()(叫)方法是所有动物的通用行为,但不同动物的 “吃” 和 “叫” 完全不同(猫吃鱼、狗吃骨头;猫喵喵叫、狗汪汪叫)。如果在父类中给eat()/speak()写死方法体,显然不符合逻辑;但如果去掉方法体,普通类又不允许空方法体 —— 这就是抽象类的诞生背景。
抽象类的核心价值:封装子类的共性属性 / 行为,同时将差异化行为声明为抽象方法,交由子类各自实现。
1.2 abstract 关键字语法
abstract关键字可修饰类或方法:
- 修饰类:该类为抽象类,无法实例化,仅用于被继承;
- 修饰方法:该方法为抽象方法,只有方法声明,无方法体,需子类重写。
java
运行
// 抽象类定义语法
public abstract class 类名 {
// 抽象方法(无方法体,以分号结尾)
public abstract void 方法名();
// 抽象类中可包含普通方法(有方法体)
public void commonMethod() {
System.out.println("这是抽象类中的普通方法");
}
}
1.3 抽象类核心特点
特点 1:抽象类不能实例化,仅用于被继承
抽象类的设计初衷是作为 “父类模板”,直接new抽象类会编译报错:
java
运行
public class AbstractTest {
public static void main(String[] args) {
// 编译报错:'Person' is abstract; cannot be instantiated
Person p1 = new Person();
}
}
// 抽象类
abstract class Person {}
特点 2:抽象类可以有构造器,便于子类实例化调用
抽象类虽不能自己实例化,但构造器可被子类通过super()调用,用于初始化父类属性:
java
运行
abstract class Person {
String name;
int age;
// 无参构造器
public Person() {}
// 有参构造器
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
// 子类继承抽象类
class Chinese extends Person {
// 子类构造器调用父类有参构造器
public Chinese(String name, int age) {
super(name, age);
}
// 子类无参构造器(默认调用父类无参构造器)
public Chinese() {}
}
特点 3:子类必须重写父类所有抽象方法(否则子类也需声明为抽象类)
如果子类不想成为抽象类,就必须实现父类的全部抽象方法:
java
运行
public class AbstractTest {
public static void main(String[] args) {
// 子类可正常实例化
Chinese chinese = new Chinese();
chinese.eat();
chinese.breath();
}
}
// 抽象父类:定义抽象方法
abstract class Animal {
public abstract void breath(); // 呼吸(抽象方法)
public abstract void eat(); // 吃(抽象方法)
}
// 中间抽象子类:仅实现部分抽象方法,仍需声明为抽象类
abstract class Person extends Animal {
// 实现eat方法
@Override
public void eat() {
System.out.println("人类吃有营养的食物");
}
}
// 具体子类:必须实现剩余的抽象方法
class Chinese extends Person {
@Override
public void breath() {
System.out.println("中国人呼吸新鲜空气");
}
}
1.4 抽象类常见面试题(核心练习)
问题 1:为什么抽象类不可以使用 final 关键字声明?
答:final修饰的类表示 “不可被继承”,而抽象类的核心设计目的是被继承(子类需重写抽象方法)。两者语义冲突,因此抽象类不能被final修饰。
问题 2:一个抽象类中可以定义构造器吗?
答:可以。抽象类的构造器并非用于自身实例化,而是供子类通过super()调用,完成父类属性的初始化。
问题 3:是否可以这样理解:抽象类就是比普通类多定义了抽象方法,除了不能直接实例化之外,没有任何不同?
答:不可以。抽象类和普通类的核心区别是 “设计意图”:
- 普通类是 “可实例化的具体类”,侧重封装具体功能;
- 抽象类是 “模板类”,侧重定义子类的规范,强制子类实现抽象方法;
- 此外,抽象类可包含抽象方法,普通类则不能。
问题 4:抽象类中可以有非抽象的成员方法吗?
答:可以。抽象类既可以包含抽象方法(强制子类实现),也可以包含普通方法(封装子类的共性行为,减少代码冗余)。
1.5 抽象类的匿名子类
当抽象类的子类仅需临时使用一次,无需单独定义类文件时,可使用匿名子类(匿名内部类),语法如下:
java
运行
abstract class Person {
public abstract void breath();
public abstract void eat();
}
public class PersonTest {
public static void main(String[] args) {
// 方式1:传统子类(需单独定义类)
// Worker worker = new Worker();
// worker.eat();
// worker.breath();
// 方式2:匿名子类(临时使用,无需定义类)
Person person = new Person() {
@Override
public void eat() {
System.out.println("匿名子类:吃健康餐");
}
@Override
public void breath() {
System.out.println("匿名子类:呼吸新鲜空气");
}
};
person.eat();
person.breath();
}
}
// 传统子类(可选)
// class Worker extends Person {
// @Override
// public void eat() {
// System.out.println("Worker:吃工作餐");
// }
//
// @Override
// public void breath() {
// System.out.println("Worker:好好呼吸");
// }
// }
二、接口(interface)
2.1 为什么要有接口?
Java 是单继承语言(一个类只能继承一个父类),但实际开发中,一个类往往需要具备多种 “能力”(如:一个类既要 “飞”,又要 “跑”)。
接口的核心价值:
- 突破单继承限制,实现 “多实现” 效果;
- 定义统一行为标准:强制多个类遵循相同的行为规范,而不限制具体实现方式(“规定做什么,不规定怎么做”)。
2.2 接口的定义与语法
接口是特殊的抽象类型,使用interface关键字定义,子类通过implements实现接口:
java
运行
// 1. 接口定义
public interface 接口名 {
// 全局常量(默认public static final)
int MAX_SPEED = 100;
// 抽象方法(默认public abstract)
void method();
}
// 2. 类实现单个接口
public class 实现类名 implements 接口名 {
@Override
public void method() {
// 实现接口方法
}
}
// 3. 类实现多个接口(突破单继承)
public class 实现类名 implements 接口1, 接口2 {
// 实现所有接口的抽象方法
}
// 4. 类继承父类 + 实现接口
public class 实现类名 extends 父类名 implements 接口名 {
// 重写父类抽象方法 + 实现接口方法
}
2.3 接口的核心特点
特点 1:接口中仅能定义全局常量和抽象方法(JDK8 前)
接口中的成员有默认修饰符:
- 变量:默认
public static final(全局常量); - 方法:默认
public abstract(抽象方法)。
java
运行
interface Flyable {
// 等价于 public static final int MAX_SPEED = 7900;
int MAX_SPEED = 7900;
// 等价于 public static final int MIN_SPEED = 1;
int MIN_SPEED = 1;
// 等价于 public abstract void fly();
void fly();
// 等价于 public abstract void stop();
void stop();
}
特点 2:接口不能定义构造器,无法实例化
接口是 “行为规范”,而非 “对象模板”,因此无构造器,也不能new:
java
运行
interface Flyable {
// 编译报错:Interfaces cannot have constructors
public Flyable() {}
}
特点 3:Java 类可实现多个接口
java
运行
interface AA { void method1(); }
interface BB { void method2(); }
// 实现多个接口,需重写所有抽象方法
class CC implements AA, BB {
@Override
public void method1() {
System.out.println("实现AA接口的method1");
}
@Override
public void method2() {
System.out.println("实现BB接口的method2");
}
}
特点 4:接口之间可多继承
java
运行
interface AA { void method1(); }
interface BB { void method2(); }
// 接口DD继承AA和BB,拥有两者的抽象方法
interface DD extends AA, BB {}
// 实现DD需重写method1和method2
class EE implements DD {
@Override
public void method1() {}
@Override
public void method2() {}
}
2.4 JDK8 接口新特性
JDK8 打破了 “接口只有常量和抽象方法” 的限制,新增默认方法和静态方法:
(1)默认方法(default)
- 有方法体,实现类可直接调用,也可重写;
- 解决 “接口升级兼容问题”(新增方法时,无需修改所有实现类)。
(2)静态方法(static)
- 有方法体,只能通过 “接口名。方法名” 调用,实现类无法重写。
java
运行
interface Fly {
// 默认方法
default void defaultMethod() {
System.out.println("接口默认方法:实现类可直接调用");
}
// 静态方法
static void staticMethod() {
System.out.println("接口静态方法:仅接口可调用");
}
}
class SubClass implements Fly {
// 可选:重写默认方法
@Override
public void defaultMethod() {
System.out.println("子类重写接口默认方法");
}
}
public class Test01 {
public static void main(String[] args) {
SubClass sc = new SubClass();
sc.defaultMethod(); // 调用重写后的默认方法
Fly.staticMethod(); // 调用接口静态方法
}
}
2.5 接口实战练习:USB 接口案例
需求:定义 USB 接口,实现键盘、鼠标的 USB 功能,并通过设备类调用 USB 设备。
java
运行
// 1. 定义USB接口(统一行为标准)
public interface USB {
// USB设备的使用方法
void useUSB();
}
// 2. 实现类:键盘
class KeyBoard implements USB {
@Override
public void useUSB() {
System.out.println("键盘:通过USB接口输入文字");
}
}
// 3. 实现类:鼠标
class Mouse implements USB {
@Override
public void useUSB() {
System.out.println("鼠标:通过USB接口控制光标");
}
}
// 4. 设备类:调用USB设备
class Device {
// 使用USB设备的方法(参数为USB接口,面向接口编程)
public void useDevice(USB usb) {
usb.useUSB();
}
}
// 5. 测试类
public class USBTest {
public static void main(String[] args) {
Device device = new Device();
// 调用键盘
device.useDevice(new KeyBoard());
// 调用鼠标
device.useDevice(new Mouse());
// 匿名实现类调用USB
device.useDevice(new USB() {
@Override
public void useUSB() {
System.out.println("匿名USB设备:临时测试用");
}
});
}
}
输出结果:
plaintext
键盘:通过USB接口输入文字
鼠标:通过USB接口控制光标
匿名USB设备:临时测试用
三、抽象类 vs 接口(核心区别)
| 维度 | 抽象类(abstract class) | 接口(interface) |
|---|---|---|
| 继承 / 实现 | 单继承(extends) | 多实现(implements) |
| 构造器 | 可以有 | 不能有 |
| 方法 | 抽象方法 + 普通方法 | JDK8 前:仅抽象方法;JDK8 后:默认 / 静态方法 |
| 变量 | 普通变量 + 常量 | 仅全局常量(public static final) |
| 设计意图 | 封装子类共性(属性 + 行为) | 定义行为标准(仅行为) |
四、总结
- 抽象类:是 “模板类”,用于封装子类共性,抽象方法强制子类实现差异化行为,不能实例化但可定义构造器;
- 接口:是 “行为标准”,突破单继承限制,JDK8 后支持默认 / 静态方法,核心是 “统一规范、多实现”;
- 核心区别:抽象类侧重 “复用共性”,接口侧重 “定义规范”,实际开发中常结合使用(类继承抽象类 + 实现多个接口)。
掌握抽象类和接口的本质,不仅能写出更优雅的面向对象代码,也是应对 Java 基础面试的关键!
更多推荐


所有评论(0)