接口和抽象类存在的意义和适用场景以及为什么要使用接口

本文详细解析了Java接口的概念、特性和应用场景,对比了接口与抽象类的区别,并探讨了接口在项目中的重要作用。

目录:

1.接口特性
2. 抽象类和接口的区别以及使用场景
3. 为什么要使用接口

- 接口的特性:

  1. 接口不是类,不能使用new运算符实例化一个接口,但是可以声明接口的变量,这个接口变量必须引用实现了接口的类对象
  2. 使用instanceof可以检查一个对象是否属于某个特定类,也可以使用它来检查一个对象是否实现了某个特定的接口
  3. 接口可以被扩展,即一个接口可以继承另一个接口
  4. 在接口中不能包含实例域或静态方法,也不能在接口中实现方法,但却可以包含常量
  5. 接口中的方法自动地被设置为public abstract,接口中的域被自动设为public static final。虽然在接口中可以让方法省略public,但是在具体实现类必须加上public
interface demo{
    int a = 5;
    Integer b = 10;
    String c = new String();
}

java8接口新特性(针对以上第四点)

1. 可以有静态方法,但必须去实现它

interface demo{
    static int d(){
        return 0;
    }
}

由于java可以同时实现多个接口(接口与接口之间使用逗号隔开),如果一个类同时实现了多个接口,而这几个接口中同时存在同名、同参数的静态方法,使用实现类去调用接口中的静态方法,将不知道具体调用哪一个。所以,java拒绝使用“实现类.接口静态方法”,只能使用“接口.接口静态方法”去访问。由于在实现类中也可以定义与接口中同名同参的静态方法,但是不能用@Override修饰,所以接口中的静态方法不能被重写

2. 可以有default方法,且必须去实现它

interface demo{
    default int d(){
        return 0;
    }
}

实现类可以去重写接口中default方法,但必须显式加上public,去掉default,因为default关键字只能出现在接口中

看到这里,对接口的特性已经有了一定的了解。你可能会有这么几个困惑:

1. 接口跟抽象类有什么区别呢?

语法上的一些区别就不说了,什么抽象类可以有构造器,接口不能有构造器啊什么的,说了也没多大实际意义。我就说一下他们存在的意义和适用的场景。

其实这并没有什么标准答案,每个人的理解都不同,都是根据自己实际做项目的经验总结出来的,每一种理解都是可以相互借鉴的。

接口带来的最大好处就是避免了多继承带来的复杂性和低效性,并且同时可以提供多重继承的好处。接口和抽象类都可以体现多态性,但是抽象类对事物进行抽象,更多的是为了继承,为了扩展,为了实现代码的重用,子类和父类之间体现的是is-a关系;接口则更多的体现一种行为约束,一种规则,一旦实现了这个接口,就要给出这个接口中所有方法的具体实现,也就是说实现类对于接口中所有的方法都是有意义的。

举个列子,现在要定义一个猫类和一个狗类,你可以直接定义两个类

class Cat{
    private String color;
    private String name;

    public void shout(){
        System.out.println("喵喵喵");
    }

    public void eat(){
        System.out.println("吃饭啦");
    }
}

class Dog{
    private String color;
    private String name;

    public void shout(){
        System.out.println("汪汪汪");
    }

    public void eat(){
        System.out.println("吃饭啦");
    }
}

这样设计完全可以,但是当某一天又要设计一个猪类呢,是不是又要重复以上的代码,这样是不利于代码的重用的,我们看到猫类和狗类都有名字和颜色属性,都有吃和叫这两个行为,那么是不是可以抽象出一个公共基类呢,这个公共基类和这些动物类的关系明显是is-a关系。由于猫和狗叫的方式不同,所以这个类不能是普通的类,只能是抽象类

abstract class BaseAnimal{
    private String color;
    private String name;

    public abstract void shout();
    public void eat(){
        System.out.println("吃饭啦");
    }
}

class Cat extends BaseAnimal{

    public void shout(){
        System.out.println("喵喵喵");
    }
}

class Dog extends BaseAnimal{

    public void shout(){
        System.out.println("汪汪汪");
    }
}

很明显这样实现了代码的重用,以后要设计一个猪类,只要继承BaseAnimal类并实现shout方法就可以了,而不用写那么多重复的代码,那么能不能把BaseAnimal定义成接口呢,而不是抽象类?肯定是不行的,先不说接口中不能定义color和name这两个域,即使这些动物只有行为没有属性,也是不能把BaseAnimal定义成接口的,这些动物都有一个共同的行为,就是吃饭,每个实现类都去实现一遍相同的代码是没有意义的。如果定义为接口,那么Cat类和Dog类是没办法扩展的,实现类要想扩展只能改变接口,但是接口和抽象类一旦定义好,是不会轻易修改的,除非修改的内容对于实现类或者继承这个抽象类的类是有意义的,因为接口和抽象类设计出来作为被公共使用的对象,你不会知道有多少人在使用这个接口或者抽象类,一旦修改接口或抽象类,势必要惊动使用者,而修改的内容,使用者也不一定能用到。抽象类就明显不同了,要想扩展猫类,直接修改猫类就行了,而狗类完全不受影响。

之前我们说了,接口是一种约束和规则,一旦实现了某个接口,就必须实现这个接口中所有的方法,且这些方法对于这个类都是有意义的。现在定义一个接口,接口中有两个方法,一个方法可以比较,一个方法显示动物的技能。当需要设计一个既可以比较又有技能的动物类时,只要实现这个接口,那就必定要实现这两个方法。没有接口,你可能设计出来的动物类只有技能,而忘了能比较这个功能。所以说,接口就是一份契约,由类实现契约。契约中我编写了某些大的方针与前提,而签了约的类可以具体问题具体分析来实现自己的功能

在我们的项目中,为什么数据访问层和业务逻辑层用的是接口而不是抽象类,就是因为当有一个专门的人根据项目需求把接口设计好之后,下面的人就可以根据这份规则去实现接口,完成项目功能,下面的人不能随意扩展,也不需要去扩展,扩展只能交给设计接口的人。只要项目需求不变,接口是不需要改变的,如果经常变动接口,只能说明这个接口是设计的不合理的。

以后在考虑是使用接口还是使用抽象类的时候,只要根据自己实际的项目场景,根据这样设计的目的以及设计的合理性去选择。一般情况下,抽象类和接口是要同时使用的

2. 为什么要使用接口,方法的定义和实现都在一个类里完成不就行了?

我当初刚开始做java的时候,也是有这个困惑的,为什么非要整个接口出来,有个方法需要加个参数,就要先去改接口,再去改实现,本来只要改一个地方,现在要改两个地方,麻不麻烦。

  • 这主要还是看你项目的复杂程度,项目团队的水平和人数。如果你的项目是你一个人在做,或者两三个水平差不多的人在做,一个功能,从业务逻辑到数据访问都可以由同一个人完成,那么使用接口的意义就不是很大,因为这种需求明确,业务不是很复杂的系统,一旦完成基本是不会怎么改了,用接口反而增大任务量,而且接口设计还是挺有难度的,设计接口对于抽象能力有很高的要求,如果要更改实现类,还要去更改接口,那么这个接口肯定是不合理的;相反的,如果你的业务比较复杂,系统比较庞大,就需要有一个抽象能力很高的人来专门设计这个接口,他可以忽略实现的细节,把精力更多的花在设计上,这样效率会更高。如果你的团队水平有高有低,那么也是需要设计一个接口来统一规范的。还有一种情况就是项目人员很庞大,项目提交容易出错的情况下,也是需要用到接口的
  • 接口的另一大好处就是体现多态性,来实现系统的解耦,如果你的系统比较庞大,当项目上线后,数据量越来越大,对于性能的要求也越来越高,需要将原先的mysql换成oracle,或者需要将原生的jdbc换成herbinate,那么接口的好处就体现出来了,完全可以用herbinate新写一套实现,而不会惊动上层,上层业务逻辑层是丝毫不会察觉到你有任何变动的。
  • 接口和实现类分离,分工协作,大大提高工作效率。设计接口的人专注于设计接口,这样的系统更能符合客户的需求;一个功能,写逻辑业务的程序员不必等到写数据访问的程序员写完才能开始写,他们可以同时进行,提高了工作效率。当然,不使用接口,就把所有的方法都设计好,具体实现代码让其他人去填也是可以的,但是这样不方便,也不规范。
  • 千万不要为了使用接口而使用接口,一个小项目,mysql完全能在十年内都符合要求,别人问你为什么要用接口,你对他说,为了以后怎么样怎么样,万一需要把mysql换成oracle呢。但是如果这个万一一直没有发生,是不是这个项目就永远只有一套实现了,只有一套实现,那么解耦不解耦就根本体现不出。还不如一开始简单点,不用接口,等以后这个万一真的发生了,再重构就行了,毕竟ide重构也不是吃素的。

总的来说,还是看情况而定吧,没有一尘不变的硬性规定的。主要还是看你的项目和项目团队。

### ### 一、Java中抽象类接口的作用 在 Java 中,抽象类(Abstract Class)接口(Interface)是面向对象编程中实现抽象化的两种核心机制。它们都能定义抽象方法,但设计目的应用场景存在显著差异。 抽象类用于定义类的抽象模板,封装多个子类的公共行为状态,强调的是“是什么”(is-a 关系)。它可以包含抽象方法具体方法,支持部分实现,适合多个相关类共享基础逻辑的场景。例如,定义一个 `Animal` 类,其中包含 `sleep()` 方法抽象的 `makeSound()` 方法,所有子类都可以继承并实现抽象方法[^1]。 接口则用于定义行为契约(can-do 关系),强调“能做什么”。它不关心实现细节,只规定一组方法签名,实现类必须提供具体实现。接口适用于解耦设计,允许无关类实现相同接口,如 `File` `URL` 都实现 `Serializable` 接口接口支持多重继承,一个类可以实现多个接口,从而突破 Java 单继承的限制[^2]。 ### ### 二、Java中抽象类接口的区别 #### 1. 语法特性差异 抽象类使用 `abstract` 关键字声明,可以包含抽象方法具体方法(包括 `static` `default` 方法)。它允许定义普通成员变量(实例变量或静态变量),并且可以拥有构造方法用于子类初始化父类状态。抽象方法可以是 `protected` 或 `public`(默认 `public`)。 接口使用 `interface` 关键字声明,所有方法默认是 `public abstract`,并且不能有构造方法。在 Java 8 之前,接口只能包含抽象方法;从 Java 8 开始,支持 `default` `static` 方法,并允许在接口中提供方法体。接口中的成员变量只能是 `public static final` 常量,且必须显式初始化[^3]。 ```java // 抽象类示例 abstract class Animal { public abstract void makeSound(); public void sleep() { System.out.println("Animal is sleeping."); } } // 接口示例 interface Flyable { void fly(); } ``` #### 2. 设计目的与使用场景 抽象类用于表示“是什么”的关系,适合多个相关类共享部分实现逻辑的场景。它强调类的继承关系,适合构建具有共同状态行为的类体系。例如,多个动物类可以继承一个 `Animal` 抽象类,共享 `sleep()` 方法的实现。 接口用于表示“能做什么”的关系,强调行为契约,适合跨类的行为规范定义。由于 Java 的单继承限制,接口可以突破这一限制,使一个类实现多个接口来获得多种行为能力。例如,一个类可以同时实现 `Flyable` `Swimmable` 接口,表示它既能飞也能游泳[^4]。 #### 3. 多重继承与扩展性 抽象类不支持多重继承,一个类只能继承一个抽象类。而接口支持多重继承,一个类可以实现多个接口,从而获得多种行为定义。这种机制提高了程序的灵活性可扩展性,特别适合构建松耦合的系统结构。 ### ### 三、抽象类接口使用建议 - 当多个类之间存在“is-a”关系,并且需要共享状态行为时,应优先使用抽象类。 - 当需要定义行为规范、实现类之间无直接继承关系,或者需要支持多重继承时,应使用接口。 - 如果接口中需要提供默认实现(如 Java 8 的 `default` 方法),可以在接口中定义,避免修改所有实现类。 - 抽象类更适合封装可变状态部分实现逻辑,而接口更适合定义不变的行为契约。 ###
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值