在我的一次采访中,我被要求解释界面和抽象类之间的区别。
以下是我的回复:
Java接口的方法是隐式抽象的 并且无法实现。 Java抽象类可以有 实现默认行为的实例方法。
Java接口中声明的变量默认为final。一个 抽象类可能包含非最终变量。
默认情况下,Java接口的成员是公共的。 Java摘要 class可以拥有像private这样的类成员的通常风格, 受保护等
应使用关键字“implements”实现Java接口;一个 应使用关键字“extends”扩展Java抽象类。
接口只能扩展另一个Java接口,一个抽象类 可以扩展另一个Java类并实现多个Java接口。
Java类可以实现多个接口,但只能扩展 一个抽象类。
然而,面试官并不满意,并告诉我这个描述代表“书卷知识”。
他问我一个更实际的回答,解释当我在界面上选择抽象类时,使用实际例子。
我哪里出错了?
答案 0 :(得分:487)
我先给你举个例子:
public interface LoginAuth{
public String encryptPassword(String pass);
public void checkDBforUser();
}
现在假设您的应用程序中有3个数据库。然后,该数据库的每个实现都需要定义上述两种方法:
public class DBMySQL implements LoginAuth{
// Needs to implement both methods
}
public class DBOracle implements LoginAuth{
// Needs to implement both methods
}
public class DBAbc implements LoginAuth{
// Needs to implement both methods
}
但是如果encryptPassword()不依赖于数据库,那么每个类都是一样的呢?那么以上就不是一个好方法。
相反,请考虑这种方法:
public abstract class LoginAuth{
public String encryptPassword(String pass){
// Implement the same default behavior here
// that is shared by all subclasses.
}
// Each subclass needs to provide their own implementation of this only:
public abstract void checkDBforUser();
}
现在在每个子类中,我们只需要实现一个方法 - 与数据库相关的方法。
我尽我所能,希望这会清除你的怀疑。
答案 1 :(得分:190)
这个世界上没有什么是完美的。他们可能期待更多实用的方法。
但是在你的解释之后你可以用稍微不同的方法添加这些线条。
接口是规则(规则,因为您必须为它们提供一个您不能忽略或避免的实现,以便它们像规则一样强加),它们在软件开发中的各个团队之间起着共同的理解文档的作用。
接口提供了将要完成的工作,但不知道如何完成工作。因此,实现完全依赖于开发人员遵循给定的规则(意味着给定方法的签名)。
抽象类可以包含抽象声明,具体实现或两者。
抽象声明就像要遵循的规则一样,具体的实现就像指南(您可以按原样使用它,也可以通过覆盖并给出自己的实现来忽略它)。
此外,具有相同签名的哪些方法可能会改变不同上下文中的行为,作为接口声明提供,作为在不同上下文中相应实现的规则。
编辑: Java 8有助于在界面中定义默认和静态方法。
public interface SomeInterfaceOne {
void usualAbstractMethod(String inputString);
default void defaultMethod(String inputString){
System.out.println("Inside SomeInterfaceOne defaultMethod::"+inputString);
}
}
现在,当一个类实现SomeInterface时,不一定要为接口的默认方法提供实现。
如果我们有另外一个接口有以下方法:
public interface SomeInterfaceTwo {
void usualAbstractMethod(String inputString);
default void defaultMethod(String inputString){
System.out.println("Inside SomeInterfaceTwo defaultMethod::"+inputString);
}
}
Java不允许扩展多个类,因为它会导致“Diamond Problem”,其中编译器无法决定使用哪个超类方法。使用默认方法,接口也会出现菱形问题。因为如果一个类正在实现这两个
SomeInterfaceOne and SomeInterfaceTwo
并没有实现常见的默认方法,编译器无法决定选择哪一个。 为了避免这个问题,在java 8中必须实现不同接口的通用默认方法。如果任何类正在实现上述接口,则必须为defaultMethod()方法提供实现,否则编译器将抛出编译时错误。
答案 2 :(得分:150)
您对使用和实施方面的实际差异做了很好的总结,但没有说明意义上的差异。
接口是对实现类将具有的行为的描述。实现类确保它将具有可以在其上使用的这些方法。这基本上是一个合同或一个班级必须做出的承诺。
抽象类是不同子类的基础,这些子类共享不需要重复创建的行为。子类必须完成该行为,并且可以选择覆盖预定义的行为(只要它未定义为final
或private
)。
你会在java.util
包中找到很好的例子,其中包括List
等接口和已经实现接口的AbstractList
等抽象类。 official documentation描述AbstractList
如下:
此类提供List接口的骨干实现,以最大限度地减少实现由“随机访问”数据存储(例如数组)支持的此接口所需的工作量。
答案 3 :(得分:81)
接口由单例变量(public static final)和公共抽象方法组成。当我们知道 该做什么但不知道如何做 时,我们通常更喜欢实时使用界面。
通过示例可以更好地理解这个概念:
考虑付款类。付款可以通过多种方式进行,例如PayPal,信用卡等。因此我们通常将Payment作为包含makePayment()
方法的接口,而CreditCard和PayPal是两个实现类。
public interface Payment
{
void makePayment();//by default it is a abstract method
}
public class PayPal implements Payment
{
public void makePayment()
{
//some logic for PayPal payment
//e.g. Paypal uses username and password for payment
}
}
public class CreditCard implements Payment
{
public void makePayment()
{
//some logic for CreditCard payment
//e.g. CreditCard uses card number, date of expiry etc...
}
}
在上面的示例中,CreditCard和PayPal是两个实现类/策略。接口还允许我们使用Java中的多继承概念,这是抽象类无法实现的。
我们选择一个抽象类,当 有一些我们知道该做什么的功能,以及我们知道如何执行的其他功能 。
考虑以下示例:
public abstract class Burger
{
public void packing()
{
//some logic for packing a burger
}
public abstract void price(); //price is different for different categories of burgers
}
public class VegBerger extends Burger
{
public void price()
{
//set price for a veg burger.
}
}
public class NonVegBerger extends Burger
{
public void price()
{
//set price for a non-veg burger.
}
}
如果我们将来向给定的抽象类添加方法(具体/抽象),那么实现类将不需要更改其代码。但是,如果我们将来在接口中添加方法,我们必须向实现该接口的所有类添加实现,否则会发生编译时错误。
还有其他差异,但这些是主要的,可能是你的面试官所期望的。希望这很有用。
答案 4 :(得分:39)
Java 8接口更改包括静态方法和默认方法 接口。在Java 8之前,我们只能使用方法声明 接口。但是从Java 8开始,我们可以使用默认方法和 接口中的静态方法。
引入默认方法后,似乎是接口和 抽象类是一样的。但是,它们仍然是不同的概念 在Java 8中。
抽象类可以定义构造函数。他们更有条理 可以有一个与他们相关的州。相反,默认情况下 方法只能在调用其他方面实现 接口方法,没有参考特定的实现 州。因此,两者都用于不同目的并在两者之间进行选择 实际上取决于场景背景。
抽象类对于接口的骨架(即部分)实现有效,但如果没有匹配的接口则不应该存在。
因此,当抽象类被有效地简化为低可见性,接口的骨架实现时,默认方法也可以将其除去吗?决定:不!实现接口几乎总是需要默认方法缺少的部分或全部类构建工具。如果某些界面没有,那显然是一个特例,不应该让你误入歧途。
Java 8引入了“Default Method”或(Defender方法)新功能,允许开发人员在不破坏这些接口的现有实现的情况下向接口添加新方法。它提供了灵活性,允许接口定义实现,在具体类无法为该方法提供实现的情况下将默认使用。
让我们考虑一些小例子来理解它是如何工作的:
public interface OldInterface {
public void existingMethod();
default public void newDefaultMethod() {
System.out.println("New default method"
+ " is added in interface");
}
}
以下类将在Java JDK 8中成功编译,
public class OldInterfaceImpl implements OldInterface {
public void existingMethod() {
// existing implementation is here…
}
}
如果您创建OldInterfaceImpl的实例:
OldInterfaceImpl obj = new OldInterfaceImpl ();
// print “New default method add in interface”
obj.newDefaultMethod();
默认方法永远不会是最终的,无法同步且不能 覆盖Object的方法。他们总是公开的,严重的 限制了编写简短和可重用方法的能力。
可以向接口提供默认方法,而不会影响实现类,因为它包含一个实现。如果在使用实现定义的接口中添加了每个方法,则不会影响实现类。实现类可以覆盖接口提供的默认实现。
默认方法可以向现有接口添加新功能 不破坏这些接口的旧实现。
当我们扩展包含默认方法的接口时,我们可以执行以下操作,
对于Java 8,JDK集合已经扩展,并且forEach方法被添加到整个集合(与lambdas一起使用)。使用传统方式,代码如下所示,
public interface Iterable<T> {
public void forEach(Consumer<? super T> consumer);
}
由于此结果每个实现具有编译错误的类,因此添加了必需实现的默认方法,以便不应更改现有实现。
使用Default方法的Iterable接口如下,
public interface Iterable<T> {
public default void forEach(Consumer
<? super T> consumer) {
for (T t : this) {
consumer.accept(t);
}
}
}
使用相同的机制在JDK接口中添加Stream而不破坏实现类。
由于java类可以实现多个接口,并且每个接口都可以使用相同的方法签名定义默认方法,因此,继承的方法可能会相互冲突。
考虑下面的例子,
public interface InterfaceA {
default void defaultMethod(){
System.out.println("Interface A default method");
}
}
public interface InterfaceB {
default void defaultMethod(){
System.out.println("Interface B default method");
}
}
public class Impl implements InterfaceA, InterfaceB {
}
上述代码将无法编译,并出现以下错误,
java:class Impl从defaultMethod()继承不相关的默认值 类型InterfaceA和InterfaceB
为了修复这个类,我们需要提供默认方法实现:
public class Impl implements InterfaceA, InterfaceB {
public void defaultMethod(){
}
}
此外,如果我们想调用任何超级接口而不是我们自己的实现提供的默认实现,我们可以这样做,
public class Impl implements InterfaceA, InterfaceB {
public void defaultMethod(){
// existing code here..
InterfaceA.super.defaultMethod();
}
}
我们可以选择任何默认实现或两者作为新方法的一部分。
Java接口静态方法,代码示例,静态方法与默认方法
Java接口静态方法与默认方法类似,只是我们不能在实现类中覆盖它们。此功能有助于我们避免在实现类中实现不良而导致的不良结果。让我们用一个简单的例子来看一下。
public interface MyData {
default void print(String str) {
if (!isNull(str))
System.out.println("MyData Print::" + str);
}
static boolean isNull(String str) {
System.out.println("Interface Null Check");
return str == null ? true : "".equals(str) ? true : false;
}
}
现在让我们看一个具有isNull()方法但实现较差的实现类。
public class MyDataImpl implements MyData {
public boolean isNull(String str) {
System.out.println("Impl Null Check");
return str == null ? true : false;
}
public static void main(String args[]){
MyDataImpl obj = new MyDataImpl();
obj.print("");
obj.isNull("abc");
}
}
请注意,isNull(String str)是一个简单的类方法,它不会覆盖接口方法。例如,如果我们将@Override注释添加到isNull()方法,则会导致编译器错误。
现在,当我们运行应用程序时,我们得到以下输出。
接口空检查
Impl Null检查
如果我们将接口方法从static变为default,我们将得到以下输出。
Impl Null检查
MyData Print ::
Impl Null检查
Java接口静态方法仅对接口方法可见,如果我们从MyDataImpl类中删除isNull()方法,我们将无法将其用于MyDataImpl对象。但是,与其他静态方法一样,我们可以使用类名称的接口静态方法。例如,有效的语句将是:
boolean result = MyData.isNull("abc");
在结束帖子之前,我想简要介绍一下功能界面。只有一个抽象方法的接口称为功能接口。
引入了新的注释@FunctionalInterface
以将接口标记为功能接口。 @FunctionalInterface
注释是一种避免在功能界面中意外添加抽象方法的工具。使用它是可选的但很好的做法。
功能接口是人们期待已久的,并且很多人都在寻求Java 8的功能,因为它使我们能够使用lambda表达式来实例化它们。添加了一个带有一堆函数接口的新包java.util.function,以便为lambda表达式和方法引用提供目标类型。我们将在以后的帖子中研究功能接口和lambda表达式。
答案 5 :(得分:37)
除了第一个语句(Java 8发布之后)之外,所有语句都有效:
Java接口的方法是隐式抽象的,不能有实现
来自文档page:
接口是一种类似于类的引用类型,只能包含 常量,方法签名,默认方法,静态方法和嵌套类型
方法体仅适用于默认方法和静态方法。
默认方法:
接口可以有default methods,但与抽象类中的抽象方法不同。
默认方法使您能够向库的接口添加新功能,并确保与为这些接口的旧版本编写的代码具有二进制兼容性。
扩展包含默认方法的界面时,可以执行以下操作:
abstract
。静态方法:
除了默认方法,您还可以在接口中定义静态方法。 (静态方法是一种与定义它的类相关联的方法,而不是与任何对象相关联。该类的每个实例都共享其静态方法。)
这使您可以更轻松地在库中组织辅助方法;
关于interface
使用static
和default
方法的文档页面中的示例代码。
import java.time.*;
public interface TimeClient {
void setTime(int hour, int minute, int second);
void setDate(int day, int month, int year);
void setDateAndTime(int day, int month, int year,
int hour, int minute, int second);
LocalDateTime getLocalDateTime();
static ZoneId getZoneId (String zoneString) {
try {
return ZoneId.of(zoneString);
} catch (DateTimeException e) {
System.err.println("Invalid time zone: " + zoneString +
"; using default time zone instead.");
return ZoneId.systemDefault();
}
}
default ZonedDateTime getZonedDateTime(String zoneString) {
return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
}
}
使用以下指南选择是使用接口还是抽象类。
<强> 接口: 强>
抽象类:
在几个密切相关的类之间共享代码。它建立 是 关系。
在 相关类 之间共享公共状态(状态可以在具体类中修改)
相关帖子:
Interface vs Abstract Class (general OO)
Implements vs extends: When to use? What's the difference?
通过这些例子,您可以理解
不相关的类可以通过接口具有功能,但相关的类通过扩展基类来改变行为。
答案 6 :(得分:21)
你的解释看起来不错,但可能看起来你正在从教科书中读到这一切? : - /
我更烦恼的是,你的榜样有多坚固?您是否懒得将几乎包含在抽象和接口之间的所有差异?
就个人而言,我建议这个链接: http://mindprod.com/jgloss/interfacevsabstract.html#TABLE
详尽的差异清单..
希望它能帮助您和所有其他读者在未来的采访中
答案 7 :(得分:19)
许多初级开发人员错误地认为接口,抽象和具体类是同一事物的微小变化,并且纯粹基于技术理由选择其中一个:我是否需要多重继承?我需要一些地方来放置常用方法吗?我是否需要打扰除了具体课程以外的其他内容?这是错误的,隐藏在这些问题中的主要问题是:&#34;我&#34; 。当您自己编写代码时,您很少会想到其他现有或未来的开发人员在使用代码或使用代码。
接口和抽象类虽然从技术角度来看显然相似,却具有完全不同的含义和目的。
界面定义合约,某些实施将为您实现 。
抽象类提供您的实现可以重复使用的默认行为。
以上这两点是我在面试时所寻求的,并且是一个足够紧凑的摘要。继续阅读以了解更多详情。
换句话说:具体的课程以非常具体的方式完成实际工作。例如,ArrayList
使用连续的内存区域以紧凑的方式存储对象列表,提供快速随机访问,迭代和就地更改,但在插入,删除和偶尔偶数时很糟糕增加;同时,LinkedList
使用双链节点来存储对象列表,而不是提供快速迭代,就地更改和插入/删除/添加,但随机访问非常糟糕。这两种类型的列表针对不同的用例进行了优化,对您将如何使用它们非常重要。如果您试图将性能从与您进行大量交互的列表中挤出来,并且在选择列表类型时由您决定,那么您应该仔细选择要实例化的列表。< / p>
另一方面,列表的高级用户并不真正关心它的实际实现方式,他们应该与这些细节隔离开来。让我们想象Java没有公开List
接口,但只有一个具体的List
类实际上是LinkedList
现在正是什么。所有Java开发人员都会根据实现细节定制他们的代码:避免随机访问,添加缓存以加快访问速度,或者只是自己重新实现ArrayList
,尽管它实际上与所有其他代码不兼容仅适用于List
。那将是可怕的...但现在想象一下,Java大师实际上意识到链接列表对于大多数实际用例来说是可怕的,并且决定切换到他们唯一可用的List
类的数组列表。这会影响世界上每个Java程序的性能,人们也不会为此感到高兴。主要的罪魁祸首是实施细节可用,开发人员认为这些细节是他们可以依赖的永久合同。这就是为什么隐藏实现细节非常重要,并且只定义抽象契约。这是接口的目的:定义一个方法接受什么样的输入,以及期望什么样的输出,而不暴露所有会诱使程序员调整他们的代码以适应可能随任何未来更新而改变的内部细节的内容
抽象类位于接口和具体类之间。它应该有助于实现共享共同或无聊的代码。例如,AbstractCollection
根据大小为0 isEmpty
提供基本实现,contains
为迭代并进行比较,addAll
为重复add
,依此类推。这使实现可以专注于区分它们的关键部分:如何实际存储和检索数据。
接口是代码的不同部分之间的低内聚网关。它们允许库存在和发展,而不会在内部发生变化时破坏每个库用户。它被称为应用程序编程接口 ,而不是应用程序编程类。在较小的规模上,它们还允许多个开发人员通过良好记录的界面分离不同的模块,从而在大型项目上成功协作。
抽象类是实现接口时使用的高内聚帮助程序,假定某些级别的实现细节。或者,抽象类用于定义SPI,服务提供者接口。
API和SPI之间的区别很微妙,但很重要:对于API,重点是谁使用,对于SPI,重点是谁实现< / strong>它。
向API添加方法很简单,API的所有现有用户仍将进行编译。向SPI添加方法很难,因为每个服务提供者(具体实现)都必须实现新方法。如果接口用于定义SPI,则提供者必须在SPI合同发生变化时发布新版本。如果使用抽象类,则可以根据现有抽象方法定义新方法,也可以将其作为空throw not implemented exception
存根定义,这至少允许旧版本的服务实现仍然可以编译和运行。
尽管Java 8引入了接口的默认方法,这使得接口和抽象类之间的界限更加模糊,但这并不是为了使实现可以重用代码,而是为了更容易地更改同时用作接口的接口。 API和SPI(或错误地用于定义SPI而不是抽象类)。
OP的答案中提供的技术细节被认为是&#34;书本知识&#34;因为这通常是在学校和大多数技术书籍中使用的关于语言的方法: 是什么,而不是如何在实践中使用它,特别是在大型应用程序中
这是一个类比:假设问题是:
在舞会之夜,汽车或酒店房间租用什么更好?
技术答案听起来像是:
嗯,在汽车里你可以早点做到,但在酒店房间,你可以更舒适地做到。另一方面,酒店房间只在一个地方,而在车里你可以在更多的地方做,比如,让我们说你可以去远景点看一看,或者在车道上 - 在剧院,或许多其他地方,甚至在不止一个地方。此外,酒店房间有淋浴。
这一切都是真的,但是完全错过了两个完全不同的东西,两者可以同时用于不同的目的,并且&#34;做它&#34;对于这两个选项中的任何一个,方面都不是最重要的。答案缺乏视角,它显示了一种不成熟的思维方式,同时正确地呈现了真实的事实&#34;。
答案 8 :(得分:9)
接口是一个“契约”,实现合同的类承诺实现这些方法。我必须编写一个界面而不是一个类的例子是我将游戏从2D升级到3D时的情况。我必须创建一个界面来在2D和3D版本的游戏之间共享类。
package adventure;
import java.awt.*;
public interface Playable {
public void playSound(String s);
public Image loadPicture(String s);
}
然后我可以基于环境实现这些方法,同时仍然能够从不知道正在加载的游戏版本的对象中调用这些方法。
public class Adventure extends JFrame implements Playable
public class Dungeon3D extends SimpleApplication implements Playable
public class Main extends SimpleApplication implements AnimEventListener,
ActionListener, Playable
通常,在游戏世界中,世界可以是一个在游戏中执行方法的抽象类:
public abstract class World...
public Playable owner;
public Playable getOwner() {
return owner;
}
public void setOwner(Playable owner) {
this.owner = owner;
}
答案 9 :(得分:8)
如何考虑以下方式:
所以当你有一个抽象类Mammals,一个子类Human和一个接口Driving时,你可以说
我的建议是,书本知识短语表明他想要听到两者之间的语义差异(就像其他人已经建议的那样)。
答案 10 :(得分:6)
抽象类不是纯粹的抽象bcz,它是具体的(实现的方法)集合以及未实现的方法。 但 接口是纯粹的抽象bcz只有未实现的方法而不是具体的方法。
为何选择抽象类?
为何选择界面?
答案 11 :(得分:5)
界面就像一组基因,公开记录有某种效果:DNA测试会告诉我我是否得到它们 - 如果我这样做,我可以公开让我知道我是一个“载体”,我的行为或国家的一部分将符合他们。 (但是,当然,我可能有许多其他基因提供超出此范围的特征。)
抽象类就像single-sex species(*)的死亡祖先一样:她不能生活,而是活着(即非抽象)后代继承了她所有的基因。
(*)为了延伸这个比喻,让我们说这个物种的所有成员都生活在同一个年龄段。这意味着死去的祖先的所有祖先也必须死亡 - 同样地,所有活着祖先的后代都必须活着。
答案 12 :(得分:4)
我做工作面试,我会对你的答案不满意(抱歉,但我很诚实)。听起来好像你已经读过差异并修改了答案,但也许你从来没有在实践中使用它。
关于为什么要使用每种方法的一个很好的解释可能远远好于对差异的精确解释。雇主ultimatley希望程序员做一些不了解他们的事情,这在面试中很难证明。如果申请基于技术或文档的工作而不是开发人员角色,那么你给出的答案就会很好。
祝你未来的采访好运。
此问题的答案更多是关于面试技巧而不是您提供的技术资料。也许考虑阅读它。 https://workplace.stackexchange.com/可以成为这类事情的好地方。
答案 13 :(得分:3)
我观察到的主要区别是抽象类为我们提供了已经实现的一些常见行为,而子类只需要实现与它们对应的特定功能。接口只会指定需要完成哪些任务,接口不会给出任何实现。我可以说它指定了自身与实现类之间的契约。
答案 14 :(得分:3)
即使我在多次采访中遇到同样的问题而且相信我让你的时间很难说服面试官。 如果我固有上面的所有答案,那么我需要再添加一个关键点,使其更具说服力并充分利用OO
如果您未规划规则中的任何修改,对于要遵循的子类,在很长一段时间内,请转到界面,因为您无法在其中进行修改,如果你这样做了,你需要在所有其他子类中进行更改,而如果你认为,你想重用这些功能,设置一些规则并打开它进行修改,去吧用于抽象类。
以这种方式思考,您曾使用过耗材服务,或者您已向世界提供了一些代码,并且您有机会修改某些内容,假设进行了安全检查 如果我是代码的消费者,并且在更新后的一个早晨,我发现Eclipse中的所有读取标记,整个应用程序都已关闭。 因此,为了防止这种噩梦,请使用Abstract over Interfaces
我认为这可能会在一定程度上让采访者相信......未来的快乐访谈。
答案 15 :(得分:3)
界面纯粹是抽象的。我们在界面中没有任何实现代码。
抽象类包含两种方法及其实现。
click here to watch tutorial on interfaces and abstract classes
答案 16 :(得分:3)
您选择Java中的接口以避免使用Diamond Problem in multiple inheritance。
如果您希望客户端实现所有方法,请转到界面。这意味着您可以抽象地设计整个应用程序。
如果你已经知道什么是共同点,你选择抽象类。例如,抽取一个抽象类Car
。在更高级别,您实施常见的汽车方法,如calculateRPM()
。这是一种常见的方法,你让客户实现他自己的行为,如
calculateMaxSpeed()
等等。可能你会解释一下你在日常工作中遇到的几个实时例子。
答案 17 :(得分:2)
为了让您在面试中能够提供简单、合理的回答,我提供以下内容...
接口用于为一系列相关类指定 API - 关系就是接口。通常用于具有多个实现的情况,通过配置或在运行时选择正确的实现。 (除非使用 Spring,此时接口基本上就是一个 Spring Bean)。接口通常用于解决多重继承问题。
抽象类是专为继承而设计的类。这也意味着多个实现,所有实现都有一些共同点(在抽象类中找到)。
如果你想搞定它,那就说一个抽象类经常实现接口的一部分——工作就是你的了!
答案 18 :(得分:2)
当我尝试在两个密切相关的类之间共享行为时,我创建了一个包含常见行为的抽象类,并作为两个类的父类。
当我尝试定义一个类型,我的对象的用户可以可靠地调用的方法列表,然后我创建一个接口。
例如,我永远不会创建一个带有1个具体子类的抽象类,因为抽象类是关于共享行为的。但我可能很好地创建一个只有一个实现的接口。我的代码的用户不会知道只有一个实现。实际上,在将来的版本中可能会有几个实现,所有这些实现都是一些新的抽象类的子类,在创建接口时甚至都不存在。
这可能看起来有点过于书呆子(虽然我从来没有看到过这种方式我记得这样)。如果面试官(或OP)真的想要更多我个人的经验,那么我就已经准备好了一个界面的轶事已经发展出来的必要性,反之亦然。
还有一件事。 Java 8现在允许您将默认代码放入接口,进一步模糊了接口和抽象类之间的界限。但是从我所看到的,即使Java核心库的制造商也过度使用了该功能。添加了该功能,这是正确的,可以扩展接口而不会创建二进制不兼容性。但是如果你通过定义一个接口来创建一个全新的Type,那么接口应该只是一个接口。如果你想提供通用代码,那么一定要做一个帮助类(抽象或具体)。不要从一开始就使用您可能想要更改的功能来混乱您的界面。
答案 19 :(得分:1)
根据我的理解,一个由最终变量和没有实现的方法组成的接口由一个类实现,以获得一组彼此相关的方法或方法。另一方面,抽象类可以包含非最终变量和带有实现的方法,通常用作指南或超类,所有相关或类似的类都从中继承。换句话说,抽象类包含所有子类共享的所有方法/变量。
答案 20 :(得分:1)
在完成答案后,你应该跳过这个例子:
<强>摘要:强>
例如,我们有工资函数,它对所有员工都有一些共同的参数。然后我们可以有一个名为CTC的抽象类,它具有部分定义的方法体,它将被所有类型的员工扩展,并根据他们的额外功能进行重新定义。 对于共同的功能。public abstract class CTC {
public int salary(int hra, int da, int extra)
{
int total;
total = hra+da+extra;
//incentive for specific performing employee
//total = hra+da+extra+incentive;
return total;
}
}
class Manger extends CTC
{
}
class CEO extends CTC
{
}
class Developer extends CTC
{
}
<强>接口强>
java中的接口允许具有interfcae功能而不扩展该功能,您必须清楚地了解要在应用程序中引入的功能签名的实现。它会迫使你有所定义。 对于不同的功能。
public interface EmployeType {
public String typeOfEmployee();
}
class ContarctOne implements EmployeType
{
@Override
public String typeOfEmployee() {
return "contract";
}
}
class PermanentOne implements EmployeType
{
@Override
public String typeOfEmployee() {
return "permanent";
}
}
你也可以通过定义的methgos作为抽象类来对抽象类进行强制活动,现在一个类扩展抽象类提醒抽象,直到它覆盖抽象函数。
答案 21 :(得分:1)
根据我的理解以及我的方法,
接口就像规范/契约,任何实现接口类的类都必须实现抽象类中定义的所有方法(默认方法除外(在java 8中引入))
当我知道类的某些方法所需的实现时,我定义了一个类抽象,而某些方法我仍然不知道实现是什么(我们可能知道函数签名但不知道实现)。我这样做,以便在我知道如何实现这些方法的后期开发部分中,我可以扩展这个抽象类并实现这些方法。
注意:除非方法是静态的或默认的,否则在接口方法中不能有函数体。
答案 22 :(得分:1)
这里似乎已经涵盖了几乎所有内容。在abstract
类的实际实现中再补充一点:
abstract
关键字也用于防止实例化类。如果您有一个不希望实例化的具体类 - 将其设为abstract
。
答案 23 :(得分:1)
是的,您的回答在技术上是正确的,但是如果您出错了,那么您不会向他们展示您理解选择其中一个的好处和缺点。此外,他们可能会担心/吓坏他们的代码库与未来升级的兼容性。这种类型的回应可能有所帮助(除了你所说的):
“在接口类上选择抽象类取决于我们的内容 项目未来的代码将是。
抽象类允许更好的向前兼容性,因为您可以 继续向Abstract类添加行为 在不破坏现有代码的情况下 - &gt;这是不可能的 接口类。
另一方面,Interface Classes比Abstract更灵活 类。这是因为他们可以实现多个接口。该 事情是Java没有多重继承,所以使用抽象 类不会让你使用任何其他类层次结构...
所以,最后一个好的一般经验法则是:首选使用Interface 当你的中没有现有/默认实现时的类 代码库。并且,如果您使用抽象类来保持兼容性 知道你将来会更新你的课程。“
祝你下一次面试好运!
答案 24 :(得分:1)
我将尝试使用实际场景来回答这两者之间的区别。
接口带有零有效负载,即不需要维护状态,因此更好地选择将合同(能力)与类相关联。
例如,假设我有一个执行某些操作的Task类,现在要在单独的线程中执行任务我真的不需要扩展Thread类而是更好的选择是使Task实现Runnable接口(即实现)它的run()方法)然后将此Task类的对象传递给Thread实例并调用其start()方法。
现在你可以问一下,如果Runnable是一个抽象类?
从技术上讲,这是可能的,但设计明智,这可能是一个糟糕的选择理由:
换句话说,Task类需要一个能够在一个线程中运行的功能,它通过实现Runnable接口来扩展Thread类,从而使它成为一个线程。
简单地将我们的界面定义为一个能力(契约),同时使用一个 用于定义骨架(公共/部分)实现的抽象类 它
免责声明:愚蠢的例子如下,尽量不判断:-P
interface Forgiver {
void forgive();
}
abstract class GodLike implements Forgiver {
abstract void forget();
final void forgive() {
forget();
}
}
现在你可以选择成为GodLike,但你可以选择仅仅是Forgiver(即不是GodLike)并且做:
class HumanLike implements Forgiver {
void forgive() {
// forgive but remember
}
}
或者您可以选择成为GodLike并做:
class AngelLike extends GodLike {
void forget() {
// forget to forgive
}
}
使用java 8接口的P.S。也可以具有静态以及默认(可覆盖的实现)方法,因此差异性的b / w接口和抽象类更加狭窄。
答案 25 :(得分:1)
接口和抽象类之间的基本区别是,接口支持多重继承而抽象类不支持。
在抽象类中,您还可以提供所有抽象方法,如interface。
为什么需要抽象类?
在某些情况下,在处理用户请求时,抽象类不知道用户的意图。在那种情况下,我们将在类中定义一个抽象方法并询问扩展此类的用户,请在抽象方法中提供您的意图。在这种情况下,抽象类非常有用
为什么需要界面?
让我们说,我有一份我在该领域没有经验的作品。例, 如果你想建造一座建筑物或大坝,那么在那种情况下你会做什么?
在这里,我不打扰他们构建的逻辑。最终的对象是否符合我的要求,只是我的关键点。
这里称为接口和构造函数的需求称为实现者。
答案 26 :(得分:1)
简而言之,我会这样回答:
可以将抽象类视为这两种情况之间的某种东西(它引入了一些状态,但也使您必须定义行为),完全抽象的类是一个接口(这是对类的进一步发展,仅由虚方法组成)据我所知,它在C ++中的语法)。
当然,从Java 8开始,情况有所变化,但是想法仍然相同。
如果您不接受编译器团队的采访,我想这对于典型的Java采访就足够了。
答案 27 :(得分:1)
以下是围绕Java 8的一种解释,该解释试图显示抽象类和接口之间的关键区别,并涵盖Java Associate Exam所需的所有细节。
extend
个类,但可以implement
个任意数量的接口接口通常用于描述一类的能力,而不是其中心身份,例如Automobile类可以实现Recyclable接口,该接口可以应用于许多不相关的对象。抽象类定义其后代的核心身份。如果您定义了Dog抽象类,那么达尔马提亚后裔就是Dog,它们不仅是可驯服的。
在Java 8之前的版本@Daniel Lerps’s answer上,接口就像实现类必须履行的契约。
现在,使用默认方法,它们更像Mixin,它仍然可以执行合同,但也可以提供代码来完成工作。这使接口可以接管一些抽象类的用例。
抽象类的要点是它缺少抽象方法形式的功能。如果一个类没有任何抽象行为(在不同类型之间会有所不同),那么它可能是一个具体的类。
以下是类的一些常规功能,这些类在抽象类中可用,但在接口中不可用:
关于抽象类的注意事项:
final
(因为要扩展其整个用途)抽象类和接口都可以具有零到许多抽象方法。抽象方法:
{}
)abstract
关键字标记。在界面中,此关键字是不必要的private
(因为它们需要由另一个类实现)final
(因为他们还没有尸体)static
(because reasons)还请注意:
抽象类上的静态方法可以直接用MyAbstractClass.method();
调用(即就像普通类一样,也可以通过扩展抽象类的类来调用它)。
接口也可以具有静态方法。这些只能通过接口名称(MyInterface.method();
)来调用。这些方法:
abstract
,即必须有一个身体(请参见上面的“由于原因”)default
(见下文)接口可以具有默认方法,该方法必须具有default
关键字和方法主体。这些只能引用其他接口方法(不能引用特定实现的状态)。这些方法:
static
abstract
(他们有尸体)final
(名称“默认”表示它们可能会被覆盖)如果一个类使用具有相同签名的默认方法实现两个接口,则会导致编译错误,可以通过重写该方法来解决该错误。
接口只能包含上述方法类型或常量。
常量被假定为static
和final
,可以在实现接口的类中使用而无需限定。
在Java 8中,所有接口成员(以及接口本身)都假定为public
,并且不能为protected
或private
(但Java 9 does allow private methods in interfaces)。
这意味着实现接口的类必须定义具有公共可见性的方法(符合通常的规则,即不能以较低的可见性覆盖方法)。
答案 28 :(得分:1)
在抽象类中,您可以编写方法的默认实现!但在界面你不能。基本上,在接口中存在纯虚方法,这些方法必须由实现接口的类实现。
答案 29 :(得分:0)
我相信采访者试图得到的可能是界面和实施之间的区别。
接口 - 不是Java接口,而是&#34;接口&#34;更一般地说 - 代码模块基本上是使用接口的客户端代码签订的合同。
代码模块的实现是使模块工作的内部代码。通常,您可以通过多种不同的方式实现特定的接口,甚至可以在没有客户端代码的情况下更改实现,甚至可以了解更改。
Java接口应仅用作上述一般意义上的接口,以定义类如何使用类来为客户端代码带来好处,而无需指定任何实现。因此,接口包括方法签名 - 名称,返回类型和参数列表 - 用于预期由客户端代码调用的方法,并且原则上应该有足够的Javadoc用于描述该方法的每个方法。使用接口的最有说服力的理由是,如果您计划有多个不同的接口实现,可能会根据部署配置选择实现。
相比之下,Java抽象类提供了类的部分实现,而不是主要用于指定接口。它应该在多个类共享代码时使用,但是当子类也期望提供部分实现时。这允许共享代码只出现在一个地方 - 抽象类 - 同时清楚地表明实现的某些部分不存在于抽象类中,并且应该由子类提供。答案 30 :(得分:0)
你的答案是对的,但面试官需要你根据软件工程的观点来区分,而不是根据Java的细节。
简单的话:
一个界面就像一个商店的界面,它上面显示的任何东西应该在商店里,所以界面中的任何方法都必须在具体的类中实现。现在如果某些类共享一些确切的方法并且在其他类中有所不同。假设界面是关于一个包含两件事的商店,并假设我们有两个商店都包含运动器材,但其中一个有额外的衣服,另一个有额外的鞋子。所以你要做的是为Sport实现一个抽象类,它实现了Sports方法,而另一个方法则没有实现。这里的抽象类意味着这个商店本身并不存在,但它是其他类/商店的基础。这样您就可以组织代码,避免复制代码的错误,统一代码,并确保其他类可重用。