一、抽象类
1. 为什么要有抽象类?
问题场景:
class Animal {
// eat()和speak()具体应该怎么写呢?没法写
public void eat() {
// 空实现,没有实际意义
}
public void speak() {
// 空实现,没有实际意义
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
@Override
public void speak() {
System.out.println("汪汪汪");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
@Override
public void speak() {
System.out.println("喵喵喵");
}
}
问题分析:
-
父类Animal中的
eat()和speak()方法无法给出具体实现 -
每个子类都有差异化的行为
-
父类中的空方法没有实际意义,反而可能误导使用者
解决方案: 使用抽象类,将无法确定具体实现的方法声明为抽象方法
2. 抽象类的语法
// 抽象类使用abstract修饰
abstract class Animal {
// 普通属性
private String name;
// 抽象方法:只有声明,没有实现
public abstract void eat();
public abstract void speak();
// 普通方法:可以有具体实现
public void sleep() {
System.out.println("动物在睡觉");
}
// 构造方法
public Animal(String name) {
this.name = name;
}
// getter方法
public String getName() {
return name;
}
}
// 子类必须重写所有抽象方法
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(getName() + "吃骨头");
}
@Override
public void speak() {
System.out.println("汪汪汪");
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(getName() + "吃鱼");
}
@Override
public void speak() {
System.out.println("喵喵喵");
}
}
// 使用示例
public class Test {
public static void main(String[] args) {
Animal dog = new Dog("小黑");
Animal cat = new Cat("小花");
dog.eat(); // 小黑吃骨头
dog.speak(); // 汪汪汪
dog.sleep(); // 动物在睡觉
cat.eat(); // 小花吃鱼
cat.speak(); // 喵喵喵
}
}
3. 抽象类的特点
核心特性:
-
不能实例化:抽象类不能直接创建对象
// Animal animal = new Animal(); // 编译错误! Animal animal = new Dog("小黑"); // 正确:多态 -
可以有构造方法:便于子类调用
abstract class Animal { private String name; public Animal(String name) { this.name = name; } // ... } -
子类必须重写抽象方法:除非子类也是抽象类
abstract class Bird extends Animal { // 可以不重写抽象方法,但Bird也必须是抽象类 } -
可以有普通方法:这是抽象类和接口的重要区别
-
可以有成员变量:普通类有的,抽象类都可以有
4. 抽象类 vs 普通类
|
对比项 |
抽象类 |
普通类 |
|---|---|---|
|
实例化 |
不能直接实例化 |
可以直接实例化 |
|
方法 |
可以有抽象方法 |
不能有抽象方法 |
|
继承 |
必须被继承 |
可以被继承 |
|
构造方法 |
可以有 |
可以有 |
|
成员变量 |
可以有 |
可以有 |
|
使用场景 |
作为模板,定义规范 |
具体实现 |
二、接口
1. 为什么要有接口?
问题场景:
class Bird extends Animal {
public void fly() {
System.out.println("鸟会飞");
}
}
class Plane {
public void fly() {
System.out.println("飞机会飞");
}
}
class Superman {
public void fly() {
System.out.println("超人会飞");
}
}
问题分析:
-
Bird、Plane、Superman都有fly()方法
-
但它们之间没有继承关系
-
需要一种机制来描述"会飞"这个行为特征
解决方案: 使用接口,定义一组行为规范
2. 接口的语法
// 定义接口
interface Flyable {
// 接口中的方法默认是public abstract
void fly();
// JDK 8开始,接口可以有默认方法
default void takeOff() {
System.out.println("起飞");
}
// JDK 8开始,接口可以有静态方法
static void showInfo() {
System.out.println("这是一个飞行接口");
}
}
// 实现接口
class Bird extends Animal implements Flyable {
public Bird(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(getName() + "吃虫子");
}
@Override
public void speak() {
System.out.println("叽叽喳喳");
}
@Override
public void fly() {
System.out.println(getName() + "在天空飞翔");
}
}
class Plane implements Flyable {
@Override
public void fly() {
System.out.println("飞机在飞行");
}
}
// 使用示例
public class Test {
public static void main(String[] args) {
Flyable bird = new Bird("小燕子");
Flyable plane = new Plane();
bird.fly(); // 小燕子在天空飞翔
bird.takeOff(); // 起飞(默认方法)
plane.fly(); // 飞机在飞行
Flyable.showInfo(); // 这是一个飞行接口(静态方法)
}
}
3. 接口的特点
核心特性:
-
不能实例化:接口不能直接创建对象
// Flyable flyable = new Flyable(); // 编译错误! Flyable flyable = new Bird("小鸟"); // 正确:多态 -
实现类必须重写所有抽象方法:除非实现类是抽象类
-
接口可以多实现:一个类可以实现多个接口
interface Swimmable { void swim(); } class Duck extends Animal implements Flyable, Swimmable { // 必须重写所有抽象方法 @Override public void fly() { /* ... */ } @Override public void swim() { /* ... */ } @Override public void eat() { /* ... */ } @Override public void speak() { /* ... */ } } -
接口可以继承接口:支持多继承
interface A { void methodA(); } interface B extends A { void methodB(); } class C implements B { @Override public void methodA() { /* ... */ } @Override public void methodB() { /* ... */ } } -
JDK 8+特性:
-
默认方法(default)
-
静态方法(static)
-
私有方法(private,JDK 9+)
-
4. 接口 vs 抽象类
|
对比项 |
接口 |
抽象类 |
|---|---|---|
|
方法 |
都是抽象方法(JDK 8前) |
可以有抽象方法和普通方法 |
|
成员变量 |
只能是常量(public static final) |
可以有普通变量 |
|
构造方法 |
不能有 |
可以有 |
|
实现方式 |
实现(implements) |
继承(extends) |
|
数量限制 |
一个类可以实现多个接口 |
一个类只能继承一个抽象类 |
|
设计理念 |
行为规范 |
模板设计 |
|
使用场景 |
定义能力、行为 |
定义模板、通用功能 |
5. 接口的成员
interface MyInterface {
// 常量(默认public static final)
int MAX_VALUE = 100;
String DEFAULT_NAME = "default";
// 抽象方法(默认public abstract)
void method1();
// 默认方法(JDK 8+)
default void defaultMethod() {
System.out.println("默认方法");
privateMethod(); // 调用私有方法
}
// 静态方法(JDK 8+)
static void staticMethod() {
System.out.println("静态方法");
}
// 私有方法(JDK 9+)
private void privateMethod() {
System.out.println("私有方法");
}
}
三、综合应用示例
// 定义多个接口
interface Flyable {
void fly();
}
interface Swimmable {
void swim();
}
interface Runnable {
void run();
}
// 抽象类
abstract class Animal {
private String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public abstract void eat();
public void sleep() {
System.out.println(name + "在睡觉");
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
// 具体实现类
class Duck extends Animal implements Flyable, Swimmable, Runnable {
public Duck(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(getName() + "吃小鱼");
}
@Override
public void fly() {
System.out.println(getName() + "在低空飞行");
}
@Override
public void swim() {
System.out.println(getName() + "在游泳");
}
@Override
public void run() {
System.out.println(getName() + "在奔跑");
}
}
class Eagle extends Animal implements Flyable {
public Eagle(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println(getName() + "吃兔子");
}
@Override
public void fly() {
System.out.println(getName() + "在高空翱翔");
}
}
// 测试类
public class Test {
public static void main(String[] args) {
Duck duck = new Duck("唐老鸭", 3);
Eagle eagle = new Eagle("老鹰", 5);
// 鸭子多才多艺
duck.eat();
duck.fly();
duck.swim();
duck.run();
duck.sleep();
System.out.println("----------------");
// 老鹰专精飞行
eagle.eat();
eagle.fly();
eagle.sleep();
System.out.println("----------------");
// 多态应用
Flyable[] flyables = {duck, eagle};
for (Flyable flyable : flyables) {
flyable.fly();
}
}
}
四、总结
1. 抽象类的使用场景
-
需要定义模板方法,让子类重写部分方法
-
需要定义一些通用属性和方法
-
需要定义构造方法
-
子类之间存在"is-a"关系
2. 接口的使用场景
-
定义行为规范,不关心具体实现
-
需要多继承的场景
-
定义能力、角色、特征
-
实现类之间可能存在"has-a"关系
3. 设计原则
-
面向接口编程:优先使用接口定义行为
-
单一职责原则:一个接口只定义一组相关行为
-
接口隔离原则:不要定义过于臃肿的接口
-
依赖倒置原则:高层模块不应该依赖低层模块,两者都应该依赖抽象
4. 最佳实践
-
抽象类用于代码复用和模板设计
-
接口用于定义规范和扩展能力
-
合理使用多态,提高代码的灵活性和可扩展性
-
根据实际需求选择抽象类或接口,不要过度设计
通过合理使用抽象类和接口,可以设计出结构清晰、易于扩展、符合面向对象设计原则的程序架构。
1万+



被折叠的 条评论
为什么被折叠?



