【Java】用一个动物王国的例子,讲清楚抽象类和接口

简介: 【Java】用一个动物王国的例子,讲清楚抽象类和接口

情景构造

抽象类示例:动物王国

为了方便大家理解抽象类和接口,我们先创建一个情景:

抽象类示例:动物王国

想象有一个庞大的动物王国,其中有很多种不同类型的动物。这些动物有一些共通的特性,比如都有名字,但它们的行为(如吃、睡觉)可能各不相同。这时,我们可以定义一个抽象类来表示这些动物的共通特性。

抽象类 Animal:

  • 有一个共通属性:name(所有动物都有名字)
  • 有抽象方法:eat() 和 sleep() (所有动物都会吃和睡觉,但具体怎么做各不相同)
public abstract class Animal {
    protected String name;
    
    public abstract void eat();
    public abstract void sleep();
    
}

在这个抽象类中,我们定义了动物的基本属性和它们应该有的行为,但没有给出具体的实现方法。这就是像在说:“所有的动物都会吃和睡觉,但没中动物的方式都不同,所以你们(子类)自己去实现吧。”

接口示例:技能接口

现在,假设在动物王国中,有些动物 具备特殊的技能,比如飞翔或者游泳。并不是所有的动物都会飞或游泳,而且这些节能的实现方式也各不相同。这时,我们可以使用接口来定义这些技能。

接口 Flyable:

  • 有一个方法:fly()(能飞的动物都会飞,但飞行方式各不相同)
public interface Flyable {  
    void fly();  
}

接口 Swimmable:

  • 有一个方法:swim()(能游泳的动物都会游泳,但游泳的方式各不相同)
public interface Swimmable {
    void swim();
}

接口在这里就像是一种“契约”。一个类实现了一个接口,就意味着它承诺提供接口中的方法的具体实现。我们的例子中,如果某个动物会飞,那么它就可以实现 Flyable 接口,并提供 fly() 方法的具体实现。同样,如果某个动物会游泳,它就可以实现 Swimmable 接口,并提供 swim() 方法的具体实现。

实现抽象类和接口

public class Bird extends Animal implements Flyable {  
    @Override  
    public void eat() {  
        System.out.println(getName() + " eats worms.");  
    }  
  
    @Override  
    public void sleep() {  
        System.out.println(getName() + " sleeps in a nest.");  
    }  
  
    @Override  
    public void fly() {  
        System.out.println(getName() + " flies with its wings.");  
    }  
}  
  
public class Fish extends Animal implements Swimmable {  
    @Override  
    public void eat() {  
        System.out.println(getName() + " eats algae.");  
    }  
  
    @Override  
    public void sleep() {  
        System.out.println(getName() + " sleeps at the bottom of the river.");  
    }  
  
    @Override  
    public void swim() {  
        System.out.println(getName() + " swims with its fins.");  
    }  
}

在这个例子中,Bird 类继承了 Animal 抽象类并实现了 Flyable 接口,因为它既能像其他动物一样吃和睡觉,也能飞。而 Fish 类继承了 Animal 抽象类并实现了 Swimmable 接口,因为它能游泳。通过这种方式,我们可以灵活地组合不同的行为和属性,来创建丰富多样的动物类型。

抽象类

抽象类的特性


  • 一个方法可以被修饰为abstract,此时代表这个方法可以不进行实现,此时被称为抽象方法
  • 若一个类中包含抽象方法,那这个类也必须是抽象类
  • 没有抽象方法,类也能被写为抽象类
  • 什么时候一个类可以被定义为抽象类?
  • 当一个类不能具体描述某个对象的时候
  • 抽象类中定义的成员可以和普通类中无区别
  • 抽象类不能实例化
  • 既然不能实例化,那存在的意义是什么?
  • 为了被继承
  • 当一个普通类继承抽象类后,必须重写抽象类中的抽象方法
  • 抽象类也能发生向上转型、动态绑定、重写
  • 此时抽象方法也需要满足重写的特点
  • 被 static 、final、private 修饰都是不能发生重载的
  • 可以理解为:abstract 和 final 是对立的
  • 当一个普通类 A 继承了一个抽象类,不想重写这个抽象类中的方法,此时可以把这个普通类 A 改为抽象类
  • 但是如果 A 这个抽象类再次被普通类 B 继承,此时 B 这个类需要重写所有未被重写的抽象方法(出来混,总是要还的)

作用


  • 其本身不能被实例化,若想使用,只能创建该抽象类的子类,然后让子类重写抽象类中的抽象方法

普通的类也能被继承呀,普通的方法也可以被重写呀,为什么非得用抽象类和抽象方法呢?

- 使用抽象类相当于多了一重编译器的校验

- 若实际工作不应该由父类完成,而是由子类完成,此时如果不小心误用成父类了,使用普通类编译器不会报错,但是父类是抽象类的话就会在实例化的时候提示错误,让我们可以尽早发现问题

Tips:

- 很多语法存在的意义都是为了“预防出错”,

- 例如我们使用过的 final 也是类似,若加上了 final 变量被不小心修改了,编译器就能及时提醒我们

- 充分利用编译器的校验,在实际开发中是非常有意义的

接口

概念

  • 接口就是公共的行为标准规范,大家在实现时,只要符合规范,就可以使用
  • 在 Java 中,接口可以看作是:多个类的公共规范,是一种引用数据类型

1. 接口的定义,可以使用 interface 定义(定义好后会生成一个单独的字节码文件)

创建接口时,接口的命名一般以大写字母 I 开头

  • 接口的命名一般使用“形容词”词性的单词
  • 阿里编码规范中约定,接口中的方法和属性不要加任何修饰符号,保持代码的简洁性

2. 接口当中的成员变量,默认为 public static final 修饰,定义的时候必须初始化,可以省略 怕public static final,

例如:public static final int a = 10;可以写为:int a = 10;

3. 接口中的方法默认是 public abstract 修饰的。你不写的时候,也是抽象方法,所以不能有具体的实现

4.接口当中,使用 default 修饰的方法和 static 修饰的方法是可以有具体实现的

public interface USB {
  double brand = 3.0; // 默认被:public static final修饰 
  void openDevice();
  void closeDevice(); 
} 
 
public class TestUSB implements USB{
  public static void main(String[] args) { 
    System.out.println(USB.brand); // 可以直接通过接口名访问,说明是静态的 
    
    USB.brand = 2.0; 
    //编译报错:Error:无法为最终变量brand分配值 
        //说明brand具有final属性 
  } 
}

5. 接口不能被实例化,它是抽象类,没有具体对象

public class TestUSB { 
  public static void main(String[] args) { 
    USB usb = new USB(); 
  } 
} // Error:USB是抽象的;无法实例化

6. 当接口需要被类实现,此时使用关键字 implements 来实现

例如:class TestUSB implements USB

7. 当一个类实现了一个接口,那么此时这个类就要重写接口中的方法

8.接口也可以发生向上转型、动态绑定、多态

接口的特性

1. 接口是一种引用类型,但不能直接 new 接口

2. 接口中每一个方法都是 public 的抽象方法,即接口中的方法会被隐式的指定为 public abstract(唯一的,其他修饰符都会报错)

3. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现

4. 重写接口中的方法时,不能使用默认的访问权限

5. 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量

6. 接口中不能有静态代码块和构造方法

7. 接口虽然不是类,但是接口编译完成后字节码文件的后缀也是 .class

8. 如果类没有实现接口中的所有抽象方法,则类必须被设置为抽象类

实现多个接口

  • 类和类之间是单继承的,一个类只能有一个父类,即java中不支持多继承,但一个类可以实现多个接口
interface IA {
    void testA();
}
 
interface IB {
    void testB();
}
 
public class TestDemo implements IA,IB{  //同时实现IA和IB接口
    //这个类同时具备了IA和IB接口的行为,需要重写每一个方法
    @Override
    public void testA() {
        System.out.println("testA()");
    }
 
    @Override
    public void testB() {
        System.out.println("testB()");
    }
 
    public static void main(String[] args) {
        TestDemo t = new TestDemo();
        t.testA();
        t.testB();
    }
}

接口间的继承

  • 接口间的继承就相当于把多个接口合并在了一起
interface IA {
    void testA();
}
 
interface IB {
    void testB();
}
 
interface IC extends IA,IB {
    void testC();
}
 
public class TestDemo implements IC{ 
//不仅具备自己testC功能,还扩展了IA和IB的功能
    @Override
    public void testA() {
        System.out.println("testA()");
    }
 
    @Override
    public void testB() {
        System.out.println("testB()");
    }
    
    @Override
    public void testC() {
        System.out.println("testC()");
    }
}


相关文章
|
8月前
|
消息中间件 缓存 前端开发
从资损百万到零事故:Java 接口幂等设计的艺术与实践
在分布式系统中,重复请求常引发严重资损,如支付双扣、库存超卖等问题,其根源在于接口缺乏幂等性设计。本文通过真实案例揭示幂等性的重要性,并详解8种主流解决方案,涵盖唯一请求ID、乐观锁、悲观锁、状态机等,帮助开发者构建稳定系统,保障业务一致性。无论你是架构师还是开发工程师,都能从中获得实战指导,有效规避重复调用带来的风险。
731 2
|
8月前
|
数据采集 JSON Java
Java爬虫获取1688店铺所有商品接口数据实战指南
本文介绍如何使用Java爬虫技术高效获取1688店铺商品信息,涵盖环境搭建、API调用、签名生成及数据抓取全流程,并附完整代码示例,助力市场分析与选品决策。
|
7月前
|
算法 安全 Java
除了类,Java中的接口和方法也可以使用泛型吗?
除了类,Java中的接口和方法也可以使用泛型吗?
222 11
|
6月前
|
Java Go 开发工具
【Java】(9)抽象类、接口、内部的运用与作用分析,枚举类型的使用
抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体。抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接 口、枚举)5种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用。抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类abstract static不能同时修饰一个方法。
286 0
|
8月前
|
存储 缓存 安全
Java集合框架(二):Set接口与哈希表原理
本文深入解析Java中Set集合的工作原理及其实现机制,涵盖HashSet、LinkedHashSet和TreeSet三大实现类。从Set接口的特性出发,对比List理解去重机制,并详解哈希表原理、hashCode与equals方法的作用。进一步剖析HashSet的底层HashMap实现、LinkedHashSet的双向链表维护顺序特性,以及TreeSet基于红黑树的排序功能。文章还包含性能对比、自定义对象去重、集合运算实战和线程安全方案,帮助读者全面掌握Set的应用与选择策略。
770 23
|
8月前
|
安全 Java 开发者
Java集合框架:详解Deque接口的栈操作方法全集
理解和掌握这些方法对于实现像浏览器后退功能这样的栈操作来说至关重要,它们能够帮助开发者编写既高效又稳定的应用程序。此外,在多线程环境中想保证线程安全,可以考虑使用ConcurrentLinkedDeque,它是Deque的线程安全版本,尽管它并未直接实现栈操作的方法,但是Deque的接口方法可以相对应地使用。
438 12
|
8月前
|
存储 安全 Java
Java集合框架(一):List接口及其实现类剖析
本文深入解析Java中List集合的实现原理,涵盖ArrayList的动态数组机制、LinkedList的链表结构、Vector与Stack的线程安全性及其不推荐使用的原因,对比了不同实现的性能与适用场景,帮助开发者根据实际需求选择合适的List实现。
|
8月前
|
Java API 网络架构
java调用api接口自动判断节假日信息
java调用api接口自动判断节假日信息
2800 0
|
10月前
|
安全 Java API
Java 抽象类与接口在 Java17 + 开发中的现代应用实践解析
《Java抽象类与接口核心技术解析》 摘要:本文全面剖析Java抽象类与接口的核心概念与技术差异。抽象类通过模板设计实现代码复用,支持具体方法与状态管理;接口则定义行为规范,实现多态支持。文章详细对比了两者在实例化、方法实现、继承机制等方面的区别,并提供了模板方法模式(抽象类)和策略模式(接口)的典型应用示例。特别指出Java8+新特性为接口带来的灵活性提升,包括默认方法和静态方法。最后给出最佳实践建议:优先使用接口定义行为规范,通过抽象类实现代码复用,合理组合两者构建灵活架构。
303 2