欢迎光临
我们一直在努力

Java 抽象类与接口:从概念到实战的深度解析

目录

1. 什么是抽象类?

2. 抽象类的特点

2.1 抽象方法

2.2 实例代码演示

步骤 1:创建抽象类 Animal

步骤 2:创建子类 Fly(鸟类)继承 Animal

步骤 3:创建子类 Run(哺乳动物)继承 Animal

步骤 4:测试类

3. 抽象类的实际应用场景

4. 接口:工程中的核心规范

4.1 什么是接口?

4.2 接口与抽象类的区别

4.3 为什么 Java 只允许单继承?

4.4 为什么接口不会出现菱形问题?

5. 接口的代码实践

5.1 定义接口

5.2 定义实现类

5.3 测试类

5.4 接口的多态应用

6. 总结


1. 什么是抽象类?

在 Java 中,被 abstract 关键词修饰的类,就叫做抽象类。

2. 抽象类的特点

  • 不能直接实例化:不能使用 new 关键词直接创建对象,只能被子类继承使用。
  • 代码复用与解耦:抽象类的功能和继承父类的作用相同——都是抽取公共代码,实现代码复用,从而减少耦合,提高工程效率。

2.1 抽象方法

抽象类有两个相关概念,其中一个是抽象方法:

  • 只有方法的声明(只有方法名),没有方法体(没有方法的具体实现逻辑)。
  • 特点是使用 abstract 关键词修饰。
  • 只能存在于抽象类、接口中。
  • 抽象类中可以有非抽象成员(不使用 abstract 关键词修饰的方法、属性)。

2.2 实例代码演示

下面通过一个完整的例子来理解抽象类的使用:

步骤 1:创建抽象类 Animal

// Animal.java
public abstract class Animal {
// 抽象方法:只有声明,没有方法体
public abstract void move();

// 非抽象方法
public void eat() {
System.out.println("动物正在进食…");
}
}

步骤 2:创建子类 Fly(鸟类)继承 Animal

// Fly.java
public class Fly extends Animal {
// 必须实现父类的抽象方法
@Override
public void move() {
System.out.println("鸟儿在天空中飞翔");
}
}

步骤 3:创建子类 Run(哺乳动物)继承 Animal

// Run.java
public class Run extends Animal {
@Override
public void move() {
System.out.println("哺乳动物在地上奔跑");
}
}

步骤 4:测试类

// Test.java
public class Test {
public static void main(String[] args) {
// 创建子类对象
Animal bird = new Fly();
Animal mammal = new Run();

// 调用方法
bird.move(); // 输出:鸟儿在天空中飞翔
bird.eat(); // 输出:动物正在进食…

mammal.move(); // 输出:哺乳动物在地上奔跑
mammal.eat(); // 输出:动物正在进食…
}
}

从测试结果可以看到,抽象类的使用和普通父类方法重写没有本质区别,但它强制子类必须实现特定的抽象方法,从而保证了代码的规范性。

3. 抽象类的实际应用场景

正如老师所说:“今天我讲完抽象类之后,除了你们 25 级软工的大二还要再学习一遍 JavaSE 的小部分知识以外,你这一辈子再也不会碰到抽象类了。(除非你很有想法,主动找一些工程源码或者看 Java 自带的 API)”

这说明了抽象类在实际工程开发中使用频率相对较低,更多出现在教学和底层框架设计中。但理解抽象类对于掌握面向对象编程思想至关重要。

4. 接口:工程中的核心规范

如果说抽象类在实际工程中用得很少,那么接口则是每个计算机从业者都必须掌握的概念。老师强调:“你只要是从事和计算机相关的行业,不管是前后端、全栈开发、测试、AI,百分之一千会用到接口。”

4.1 什么是接口?

让我们通过一个生动的故事来理解接口的作用:

有两家开发数据库的公司,他们垄断了全球所有数据库的市场。A 数据库公司开发了两种查询数据库的方法分别是:selectAll、selectNameById;公司 B 也开发了两种查询方法分别是 findAll、findNameById。

大壮是 A 企业的员工,他的女朋友也在 A 企业。大壮女朋友现在和大壮闹分手,她说:“我再也不想你出现在我的生活里”。那么大壮只能从 A 公司跳槽到 B 公司,可是他只会用 A 公司给出的两个方法,不会用 B 公司给出的方法,他不知道 B 公司给出的这些方法是干什么的,哪怕这两个方法的实现逻辑是相同的。

了解到这个情况,AB 两家公司的老总就给出了一个解决方案:既然我们这两家公司的查询方案都相同,为什么不统一一下查询方法的签名呢?于是两家公司将两种查询方法统一命名为 selectAll、selectNameById。

这就是接口的核心作用:用来规范和管理工程逻辑的命名。在实际开发中,我们先规定需要实现的方法以及这个方法是用来做什么的(定义接口),然后再来定义实现类来完成方法的具体实现逻辑(方法体)。

4.2 接口与抽象类的区别

听着是不是和抽象类差不多?那么抽象类和接口的区别在哪呢?

对比维度抽象类接口
设计目的 代码复用,抽取公共代码 规范行为,定义契约
使用时机 在项目进展过程中抽取重复代码 在项目开展之前规定需要用的方法
继承/实现 Java 只允许单继承 一个类可以实现多个接口
方法实现 可以有具体方法实现 Java 8 前只能是抽象方法

4.3 为什么 Java 只允许单继承?

这是为了防止菱形问题(Diamond Problem):

class A {
public void eat() {
System.out.println("A eat");
}
}

class B extends A {
@Override
public void eat() {
System.out.println("B eat");
}
}

class C extends A {
@Override
public void eat() {
System.out.println("C eat");
}
}

// 如果允许多继承,会出现问题:
// class D extends B, C { }
// D 调用 eat() 时,编译器不知道该执行 B 的还是 C 的版本,产生歧义。

4.4 为什么接口不会出现菱形问题?

因为接口里默认都是抽象方法,没有方法体;哪怕多个接口有同名抽象方法,最终都由实现类自己重写一遍,实现类说了算,不会产生冲突。

5. 接口的代码实践

5.1 定义接口

// Function.java
public interface Function {
// 抽象方法(默认就是 public abstract)
void selectAll();
void selectNameById(int id);
}

5.2 定义实现类

建议将接口的实现类命名为:接口名 + Impl

// FunctionImpl.java
public class FunctionImpl implements Function {
@Override
public void selectAll() {
System.out.println("查询所有数据");
}

@Override
public void selectNameById(int id) {
System.out.println("根据ID查询名称,ID:" + id);
}
}

5.3 测试类

// TestInterface.java
public class TestInterface {
public static void main(String[] args) {
// 接口不能使用 new 创建对象,必须使用实现类的构造方法
Function func = new FunctionImpl();

// 调用接口方法
func.selectAll(); // 输出:查询所有数据
func.selectNameById(1001); // 输出:根据ID查询名称,ID:1001
}
}

5.4 接口的多态应用

既然可以使用接口的引用类型创建实际对象,那么就可以用到多态:

// 定义第二个接口
public interface Function2 {
void customMethod();
}

// 实现类实现多个接口
public class MultiFunctionImpl implements Function, Function2 {
@Override
public void selectAll() {
System.out.println("实现 selectAll");
}

@Override
public void selectNameById(int id) {
System.out.println("实现 selectNameById,ID:" + id);
}

@Override
public void customMethod() {
System.out.println("Function2 的自定义方法");
}
}

// 测试多态
public class TestPolymorphism {
public static void main(String[] args) {
Function func1 = new MultiFunctionImpl();
Function2 func2 = new MultiFunctionImpl();

// 接口约束了对象能够使用的方法
func1.selectAll(); // 正确
// func1.customMethod(); // 编译错误:Function 接口中没有 customMethod 方法

func2.customMethod(); // 正确
// func2.selectAll(); // 编译错误:Function2 接口中没有 selectAll 方法
}
}

6. 总结

抽象类和接口是 Java 面向对象编程中两个重要的概念,它们的核心区别在于:

  • 设计目的不同:抽象类侧重于代码复用,接口侧重于行为规范。
  • 使用场景不同:抽象类用于有共同特征的类层次结构,接口用于定义跨不同类别的行为契约。
  • 继承机制不同:Java 只支持单继承(避免菱形问题),但支持多接口实现。
  • 方法实现不同:抽象类可以有具体实现,接口在 Java 8 前只能是抽象方法。
  • 在实际工程开发中,接口的使用频率远高于抽象类。理解它们的区别和适用场景,对于编写高质量、可维护的 Java 代码至关重要。

    赞(0)
    未经允许不得转载:171主机测评 » Java 抽象类与接口:从概念到实战的深度解析
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址