Java基础7-一文搞懂抽象类和接口,从基础到面试题,揭秘其本质区别(二)

简介: Java基础7-一文搞懂抽象类和接口,从基础到面试题,揭秘其本质区别(二)

Java基础7-一文搞懂抽象类和接口,从基础到面试题,揭秘其本质区别(一):https://developer.aliyun.com/article/1535655

设计思想区别

从前面抽象类的具体实现类的实现方式可以看出,其实在 Java 中,抽象类和具体实现类之间是一种继承关系,也就是说如果釆用抽象类的方式,则父类和子类在概念上应该是相同的。接口却不一样,如果采用接口的方式,则父类和子类在概念上不要求相同。

接口只是抽取相互之间没有关系的类的共同特征,而不用关注类之间的关系,它可以使没有层次关系的类具有相同的行为。因此,可以这样说:抽象类是对一组具有相同属性和方法的逻辑上有关系的事物的一种抽象,而接口则是对一组具有相同属性和方法的逻辑上不相关的事物的一种抽象。

仍然以前面动物类的设计为例来说明接口和抽象类关于设计思想的区别,该动物类默认所有的动物都具有吃的功能,其中定义接口的示意代码如下:

public interface Animal{//所有动物都会吃public void eat();}

定义抽象类的示意代码如下:

public abstract class Animal{//所有动物都会吃public abstract void eat();}

不管是实现接口,还是继承抽象类的具体动物,都具有吃的功能,具体的动物类的示意代码如下。

接口实现类的示意代码如下:

public class concreteAnimal implements Animal{//所有动物都会吃public void eat(){}}

抽象类的实现类示意代码如下:

public class concreteAnimal implements Animal{//所有动物都会吃public void eat(){}}

当然,具体的动物类不光具有吃的功能,比如有些动物还会飞,而有些动物却会游泳,那么该如何设计这个抽象的动物类呢?可以别在接口和抽象类中增加飞的功能,其中定义接口的示意代码如下:

public interface Animal{//所有动物都会吃public void eat();
//所有动物都会飞
public void fly();
复制代码

}

定义抽象类的示意代码如下:

public abstract class Animal
{
    //所有动物都会吃
    public abstract void eat();

    //所有动物都会飞
    public void fly(){};
}
复制代码

这样一来,不管是接口还是抽象类的实现类,都具有飞的功能,这显然不能满足要求,因为只有一部分动物会飞,而会飞的却不一定是动物,比如飞机也会飞。那该如何设计呢?有很多种方案,比如再设计一个动物的接口类,该接口具有飞的功能,示意代码如下:

public interface AnimaiFly{//所有动物都会飞public void fly();}

那些具体的动物类,如果有飞的功能的话,除了实现吃的接口外,再实现飞的接口,示意代码如下:

public class concreteAnimal implements Animal,AnimaiFly{//所有动物都会吃public void eat(){}
//动物会飞
public void fly();
复制代码

}

那些不需要飞的功能的具体动物类只实现具体吃的功能的接口即可。另外一种解决方案是再设计一个动物的抽象类,该抽象类具有飞的功能,示意代码如下:

public abstract class AnimaiFly{//动物会飞public void fly();}

但此时没有办法实现那些既有吃的功能,又有飞的功能的具体动物类。因为在 Java 中具体的实现类只能实现一个抽象类。一个折中的解决办法是,让这个具有飞的功能的抽象类,继承具有吃的功能的抽象类,示意代码如下:

public abstract class AnimaiFly extends Animal{//动物会飞public void fly();}

此时,对那些只需要吃的功能的具体动物类来说,继承 Animal 抽象类即可。对那些既有吃的功能又有飞的功能的具体动物类来说,则需要继承 AnimalFly 抽象类。

但此时对客户端有一个问题,那就是不能针对所有的动物类都使用 Animal 抽象类来进行编程,因为 Animal 抽象类不具有飞的功能,这不符合面向对象的设计原则,因此这种解决方案其实是行不通的。

还有另外一种解决方案,即具有吃的功能的抽象动物类用抽象类来实现,而具有飞的功能的类用接口实现;或者具有吃的功能的抽象动物类用接口来实现,而具有飞的功能的类用抽象类实现。

具有吃的功能的抽象动物类用抽象类来实现,示意代码如下:

public abstract class Animal{//所有动物都会吃public abstract void eat();}

具有飞的功能的类用接口实现,示意代码如下:

public interface AnimaiFly{//动物会飞public void fly();}

既具有吃的功能又具有飞的功能的具体的动物类,则继承 Animal 动物抽象类,实现 AnimalFly 接口,示意代码如下:

public class concreteAnimal extends Animal implements AnimaiFly{//所有动物都会吃public void eat(){}
//动物会飞
public void fly();
复制代码

}

或者具有吃的功能的抽象动物类用接口来实现,示意代码如下:

public interface Animal{//所有动物都会吃public abstract void eat();}

具有飞的功能的类用抽象类实现,示意代码如下:

public abstract class AnimaiFly{//动物会飞public void fly(){};}

既具有吃的功能又具有飞的功能的具体的动物类,则实现 Animal 动物类接口,继承 AnimaiFly 抽象类,示意代码如下:

public class concreteAnimal extends AnimaiFly implements Animal{//所有动物都会吃public void eat(){}
//动物会飞
public void fly();
复制代码

}

这些解决方案有什么不同呢?再回过头来看接口和抽象类的区别:抽象类是对一组具有相同属性和方法的逻辑上有关系的事物的一种抽象,而接口则是对一组具有相同属性和方法的逻辑上不相关的事物的一种抽象,因此抽象类表示的是“is a”关系,接口表示的是“like a”关系。

假设现在要研究的系统只是动物系统,如果设计人员认为对既具有吃的功能又具有飞的功能的具体的动物类来说,它和只具有吃的功能的动物一样,都是动物,是一组逻辑上有关系的事物,因此这里应该使用抽象类来抽象具有吃的功能的动物类,即继承 Animal 动物抽象类,实现 AnimalFly 接口。

如果设计人员认为对既具有吃的功能,又具有飞的功能的具体的动物类来说,它和只具有飞的功能的动物一样,都是动物,是一组逻辑上有关系的事物,因此这里应该使用抽象类来抽象具有飞的功能的动物类,即实现 Animal 动物类接口,继承 AnimaiFly 抽象类。

假设现在要研究的系统不只是动物系统,如果设计人员认为不管是吃的功能,还是飞的功能和动物类没有什么关系,因为飞机也会飞,人也会吃,则这里应该实现两个接口来分别抽象吃的功能和飞的功能,即除实现吃的 Animal 接口外,再实现飞的 AnimalFly 接口。

从上面的分析可以看出,对于接口和抽象类的选择,反映出设计人员看待问题的不同角度,即抽象类用于一组相关的事物,表示的是“is a”的关系,而接口用于一组不相关的事物,表示的是“like a”的关系。

如何回答面试题:接口和抽象类的区别?

接口(interface)和抽象类(abstract class)是支持抽象类定义的两种机制。

接口是公开的,不能有私有的方法或变量,接口中的所有方法都没有方法体,通过关键字interface实现。

抽象类是可以有私有方法或私有变量的,通过把类或者类中的方法声明为abstract来表示一个类是抽象类,被声明为抽象的方法不能包含方法体。子类实现方法必须含有相同的或者更低的访问级别(public->protected->private)。抽象类的子类为父类中所有抽象方法的具体实现,否则也是抽象类。

接口可以被看作是抽象类的变体,接口中所有的方法都是抽象的,可以通过接口来间接的实现多重继承。接口中的成员变量都是static final类型,由于抽象类可以包含部分方法的实现,所以,在一些场合下抽象类比接口更有优势。

相同点

(1)都不能被实例化(2)接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。

不同点

(1)接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。

(2)实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。

(3)接口强调特定功能的实现,而抽象类强调所属关系。

(4)接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。

(5)接口被用于常用的功能,便于日后维护和添加删除,而抽象类更倾向于充当公共类的角色,不适用于日后重新对立面的代码修改。功能需要累积时用抽象类,不需要累积时用接口。


目录
相关文章
|
9天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
10天前
|
Java
java线程接口
Thread的构造方法创建对象的时候传入了Runnable接口的对象 ,Runnable接口对象重写run方法相当于指定线程任务,创建线程的时候绑定了该线程对象要干的任务。 Runnable的对象称之为:线程任务对象 不是线程对象 必须要交给Thread线程对象。 通过Thread的构造方法, 就可以把任务对象Runnable,绑定到Thread对象中, 将来执行start方法,就会自动执行Runable实现类对象中的run里面的内容。
24 1
|
15天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
41 4
|
21天前
|
安全 Java
在 Java 中使用实现 Runnable 接口的方式创建线程
【10月更文挑战第22天】通过以上内容的介绍,相信你已经对在 Java 中如何使用实现 Runnable 接口的方式创建线程有了更深入的了解。在实际应用中,需要根据具体的需求和场景,合理选择线程创建方式,并注意线程安全、同步、通信等相关问题,以确保程序的正确性和稳定性。
|
20天前
|
Java
Java基础(13)抽象类、接口
本文介绍了Java面向对象编程中的抽象类和接口两个核心概念。抽象类不能被实例化,通常用于定义子类的通用方法和属性;接口则是完全抽象的类,允许声明一组方法但不实现它们。文章通过代码示例详细解析了抽象类和接口的定义及实现,并讨论了它们的区别和使用场景。
|
20天前
|
Java 测试技术 API
Java零基础-接口详解
【10月更文挑战第19天】Java零基础教学篇,手把手实践教学!
18 1
|
1月前
|
安全 Java 数据安全/隐私保护
【一步一步了解Java系列】:探索抽象类与接口的秘密
【一步一步了解Java系列】:探索抽象类与接口的秘密
25 3
|
2月前
|
Java
Java——抽象类和接口
抽象类是一种不能被实例化的类,至少包含一个抽象方法(无实现体的方法),常用于定义一组相关类的共同特征,并强制子类实现特定方法。抽象方法不能被 `static` 或 `final` 修饰,且必须被重写。 接口则是一个完全抽象的类,用于规范类的行为。接口使用 `interface` 关键字定义,不能实例化,并且类与接口之间是实现关系。 内部类是在一个类内定义的类,分为成员内部类、静态内部类、局部内部类和匿名内部类。成员内部类可被修饰符修饰,静态内部类只能访问外部类的静态成员,局部内部类定义在方法内,匿名内部类则隐藏了名字,直接通过 `new` 关键字定义并实现接口或继承类。
20 5
Java——抽象类和接口
|
1月前
|
Java
Java中抽象类和接口有什么区别?
本文阐述了Java中抽象类和接口的区别,包括类型扩展、方法/属性访问控制符、方法实现、使用目的等方面的不同,并提供了抽象类和接口的使用示例,以及Java中其他类型的类(普通类、内部类等)的简介。
98 0
Java中抽象类和接口有什么区别?
|
1月前
|
Java 编译器
【Java】用一个动物王国的例子,讲清楚抽象类和接口
【Java】用一个动物王国的例子,讲清楚抽象类和接口
26 0