当前位置: 首页 > news >正文

Java 每日一刊(第14期):抽象类和接口

“抽象是所有能力的精髓。”

前言

这里是分享 Java 相关内容的专刊,每日一更。

本期将为大家带来以下内容:

  1. 抽象类
  2. 接口
  3. 抽象类和接口的区别
  4. 什么时候用抽象类,什么时候用接口
  5. 抽象类可以实现接口
  6. 接口中的常量其实是 public static final
  7. 标记接口(Marker Interface)
  8. 接口可以扩展多个接口
  9. 接口中的方法可以有多个默认实现来源

抽象类

抽象类是一种不能直接创建对象的类,它是一种模板,通常是为了给其他类用来继承的。抽象类可以包含两种方法:

  • 抽象方法:只有方法名称,没有具体的功能代码,子类必须要实现这些方法。
  • 具体方法:已经写好了功能代码的方法,子类可以直接用。

抽象类的特点

不能直接创建对象:你不能直接写 new Animal() 来创建一个抽象类的对象。它的主要作用是让其他类继承它。

可以有方法的具体实现:有一些方法在抽象类里已经实现了,所以子类可以直接用这些方法。

可以有构造方法:虽然你不能直接创建抽象类的对象,但你可以通过它的子类调用构造方法。

抽象类的例子

假设你想设计一个动物的类(Animal),但你不知道每种动物发出的声音是什么样的。所以你把 makeSound() 定义成一个抽象方法,让不同的动物自己去实现它。

// 抽象类使用 abstract 关键字定义
abstract class Animal {// 抽象方法,没有实现,需要子类去实现public abstract void makeSound();// 具体方法,子类可以直接使用public void sleep() {System.out.println("Sleeping...");}
}// 使用 extens 关键字继承自抽象类 Animal 的具体类
class Dog extends Animal {// 实现抽象方法@Overridepublic void makeSound() {System.out.println("Woof");}
}public class Test {public static void main(String[] args) {Animal dog = new Dog(); // 创建 Dog 对象dog.makeSound();        // 输出:Woofdog.sleep();            // 输出:Sleeping...}
}

解释:

  • Animal 类是抽象类,它定义了一个抽象方法 makeSound(),不同的动物发出的声音不同,所以这个方法没有实现。
  • Dog 类继承了 Animal 类,并实现了 makeSound() 方法。
  • sleep() 方法在抽象类里已经实现了,子类(如 Dog)可以直接使用它。

抽象类的使用场景

当你想要让多个类(例如不同的动物)共享一些相同的功能(如 sleep() 方法),但有些方法(如 makeSound())需要不同的实现时,你可以使用抽象类。

接口

接口是一种比抽象类更抽象的东西,它定义了一些方法,但这些方法都没有实现。类可以通过 implements 关键字来“实现”接口里的方法。

接口的特点

不能有具体方法:接口里的方法在 Java 8 之前都是空的,没有具体实现。它只告诉你这个方法应该存在,但具体怎么做由类自己决定。

可以多实现:一个类可以实现多个接口,而不像继承只能继承一个父类。

接口强调行为:接口更像是“契约”或“协议”,它规定了一个类应该具备哪些功能。

接口的例子

假设你想定义一种交通工具的接口(Vehicle),每种交通工具都应该有 start()stop() 方法,但你不需要知道每种交通工具具体是怎么启动或停止的,这由具体的交通工具类去实现。

// 使用 interface 关键字定义一个接口
interface Vehicle {// 接口中的方法都是抽象的,不需要写具体实现void start();void stop();
}// 使用 implements 关键字实现接口的具体类
class Car implements Vehicle {// 实现接口中的方法@Overridepublic void start() {System.out.println("Car is starting");}@Overridepublic void stop() {System.out.println("Car is stopping");}
}public class Test {public static void main(String[] args) {Vehicle car = new Car(); // 创建 Car 对象car.start();  // 输出:Car is startingcar.stop();   // 输出:Car is stopping}
}

解释:

  • Vehicle 是一个接口,它定义了 start()stop() 两个方法,但没有给出具体实现。
  • Car 类实现了 Vehicle 接口,并具体定义了这两个方法该怎么做。

接口的使用场景

当你希望不同的类(如汽车、飞机)有相同的行为(如 start()stop()),但它们的具体实现不同时,使用接口会更合适。

抽象类和接口的区别

对比点抽象类接口
定义可以包含抽象方法和具体方法只能包含抽象方法(Java 8 之后可以有默认方法)
成员变量可以包含成员变量只能包含常量(public static final
构造函数可以有构造函数不能有构造函数
继承关系只能继承一个抽象类可以实现多个接口
适用场景用于定义类的通用行为,提供部分实现用于定义类的行为规范,不提供实现
访问修饰符可以有不同的访问修饰符(publicprotected默认所有方法是 public

什么时候用抽象类,什么时候用接口

用抽象类的情况:如果你有一些功能是所有子类共享的,但某些功能需要子类去实现,这时抽象类比较合适。例如,所有的动物都会“睡觉”(sleep()),但发出的声音不同(makeSound())。

用接口的情况:如果你想为完全不同的类定义共同的行为,而不关心它们之间是否有继承关系,接口更合适。例如,汽车和飞机都是交通工具,都需要 start()stop() 功能,但它们可能没有其他的共同点。

抽象类可以实现接口

尽管抽象类不能被直接实例化,但它们可以实现接口中的方法,而不一定要求子类必须重新实现。这种做法适合为接口提供部分实现,子类可以选择使用或覆盖它们。

interface Walkable {void walk();
}abstract class Animal implements Walkable {@Overridepublic void walk() {System.out.println("Animal is walking.");}// 抽象类中可以有自己的抽象方法public abstract void makeSound();
}

在这个例子中,抽象类 Animal 实现了 Walkable 接口中的 walk() 方法,而子类可以继承这个实现或进行自定义。

接口中的常量其实是 public static final

在接口中定义的字段默认是 public static final,即它们都是常量,必须在声明时初始化,且不能修改。即使你没有显式声明这些修饰符,编译器也会自动加上。

interface MyConstants {// 实际上等同于:public static final int MAX_VALUE = 100int MAX_VALUE = 100; 
}

这意味着接口中的所有字段都是全局常量,并且只能用于共享常量的场景。

标记接口(Marker Interface)

Java 中有一种特殊的接口叫做标记接口(Marker Interface),它没有任何方法,作用仅仅是标记某个类具有某种特性。比如 java.io.Serializable,实现了这个接口的类可以被序列化。

interface MarkerInterface {// 没有方法,仅用于标记类具有某种属性
}class MyClass implements MarkerInterface {// 这个类被标记为拥有 MarkerInterface 的属性
}

标记接口本质上是一种元数据,用于表示类的某些行为或特性。这种设计理念后来被注解(Annotations)部分取代。

接口可以扩展多个接口

一个接口可以继承多个接口,这让 Java 实现了某种形式的“多继承”,但它不同于类的多继承,类的多继承在 Java 中是被禁止的。这种机制允许你通过继承多个接口来组合各种行为。

interface Eater {void eat();
}interface Sleeper {void sleep();
}interface Animal extends Eater, Sleeper {void makeSound();
}class Dog implements Animal {@Overridepublic void eat() {System.out.println("Dog is eating.");}@Overridepublic void sleep() {System.out.println("Dog is sleeping.");}@Overridepublic void makeSound() {System.out.println("Dog says: Woof!");}
}

Dog 类通过实现 Animal 接口,间接地获得了 EaterSleeper 接口中的方法。这种设计使得代码更具扩展性。

接口中的方法可以有多个默认实现来源

在 Java 8 引入了默认方法后,一个类可以从多个接口继承默认方法。如果这些接口中定义了相同的方法名,类必须手动解决冲突。这种情况在多重继承的情况下常见。

interface InterfaceA {default void show() {System.out.println("InterfaceA");}
}interface InterfaceB {default void show() {System.out.println("InterfaceB");}
}class MyClass implements InterfaceA, InterfaceB {@Overridepublic void show() {InterfaceA.super.show();  // 选择调用 InterfaceA 的 show()}
}

在这种情况下,你必须明确指明调用哪个接口的默认实现。通过 InterfaceA.super.show() 可以解决冲突。

本期小知识

在 Java 8 之后,接口中可以定义默认方法(default methods)和静态方法。这是接口功能的重要扩展,因为传统的接口不能包含实现。

默认方法:允许在接口中定义方法的默认实现,不强制要求实现类去实现它们。这在维护接口的向后兼容性上起到了很大作用。

interface Vehicle {default void start() {System.out.println("Starting the vehicle");}
}

静态方法:接口中的静态方法只能通过接口名称调用,不能被子类继承或覆盖。

interface Vehicle {static void checkEngine() {System.out.println("Engine checked");}
}

http://www.mrgr.cn/news/33224.html

相关文章:

  • 【OSS安全最佳实践】降低因操作失误等原因导致数据丢失的风险
  • 从Yargs源码学习中间件的设计
  • 考研数学精解【6】
  • [OpenGL]使用OpenGL绘制带纹理三角形
  • 不同编程语言的互相调用
  • js 一个数组合并到一个数组
  • 群晖套娃:群晖+飞牛fnOS二合一,群晖nas安装飞牛fnOS系统实录(飞牛fnOS初体验,如何挂载网盘视频,轻松实现影视刮削)
  • PHP项目中Vendor错误导致项目失败的解决方案
  • 检索索引对象中的重复值、删除重复值pandas.Index.duplicated
  • Python 3 元组
  • python画图1
  • CocosCreator 3.x 实现角色移动与加载时动态屏幕边缘检测
  • 嵌入式 开发技巧和经验分享
  • PHP如何从字符串中删除转义字符
  • 【C++】仿函数
  • 未来视界,触手可及:bigmp4 引领 AI 视频处理新革命
  • 教师师生关系研判:基于信任建立、课堂管理、学生心理支持、沟通技巧与反馈改进的综合分析
  • 嵌入式常用GUI介绍
  • 用Python实现时间序列模型实战——Day 30: 学习总结与未来规划
  • NXP实战笔记(十六):NXP 32K3xx系列单片机有关OTA升级的思考