【简要回答】

相同点:

1.可以定义抽象方法

2.不能创建本类对象,只能由子类去实例化子类对象

不同点:
1.子类实现:implements;子类继承父类:extends

2.抽象类可以继承接口;接口只能继承接口,不能继承类。一个类只能继承一个父类,但可以实现多个接口

3.抽象类可以定义成员属性,接口不能定义成员属性。(接口没有了成员属性,只有方法,只能用static final关键字定义静态常量,不能定义静态变量。子类实现接口后,只能重写方法;子类继承抽象类,连带着父类的成员属性也继承过来了)

4.抽象类有构造器,接口没有构造器。(抽象类的构造器作用:限定子类的构造行为,例如)

版本差异:
Java8:接口

1.可以定义静态方法

2.用default关键字实现方法(default关键字修饰的方法,不是抽象方法,不强制要求子类实现方法,保证向前兼容)

Java9:接口

1.能在接口中使用private方法(加入default关键字后代码不好维护,加入private后将内部逻辑拆开放在内部)

使用场景:
1.当既可以用接口也可以用抽象类,尽量选择接口,灵活度更高

2.当需要子类继承成员变量或者需要控制子类的实例化(构造器中定义固定数量的参数)

最后一段话总结


【详细回答】
接口的诞生:

接口是如何诞生的?这我从头捋捋:

类和对象:

假设现在是一个没有对象,只有基本数据类型和函数的世界,当我们的数据和函数多了,维护起来就会越发困难,于是我将其中一些有关联性质的数据和函数封装起来,类和对象就这么诞生了。

我只需要创建一个对象,就拥有了特定的属性和方法。这些属性和方法,都聚合在了一个实例上,维护起来比较方便。

继承:

可还有一个问题,现在大多数类的属性和方法都差不多,完全没必要每次都重新定义,于是将这些重复的东西封装到一个类中,其他类只需要复用他即可。继承就这么诞生了。

多态:

子类继承了父类之后,便拥有了父类的属性和方法,同时可以拓展自己的特性。

我们经常定义一个父类,然后接收多个子类,在程序执行的时候,能自行发挥各自子类的特性。

这一机制就是我们讲的多态。

抽象类:

很多时候我们发现,父类完全没必要实现所有逻辑,也没必要创建父类对象,因为我们就是想让各种各样的子类来完成不同的逻辑。

这时候我们就可以将父类的方法抽象出来,抽象类就这么诞生了。

接口:

这里的抽象类其实就已经有标准和规范的意思,抽象方法就可以理解为一个对外的标准,子类就是这些标准的实现方。

然后我们会发现,抽象类还不算彻底的抽象,因为他除了抽象方法外,还有成员属性。

可我们很多时候就是想定制一个纯粹的标准,让子类去实现抽象方法,不想让子类再继承别的东西,于是我们将抽象类更进一步地抽象,接口就诞生了。

接口只有方法,没有成员属性,子类继承接口后,唯一能做的就是重写方法。

所以说,接口就是一种标准的规范,他规定好方法模板后,子类都得按照这个来实现。

因此接口在程序中的作用就是定义方法、让子类实现,然后供调用方调用。

映射到生活中,子类就是设备厂商,接口调用方就是用户,只管使用,而不用操心各个设备的差异。

实际操作中的接口:

当我们想操作多个拥有共性的对象时,就可以用到接口,比如,我想打印一个数据集,就可以用到java的collection接口,我将方法参数定义为collection,别人就可以传任何collection子类进来,ArrayList可以,HashSet也可以,我根本不用关心数据是用哪个集合存储的,collection有上百个子类,别人爱用哪个就用哪个,都不会影响我的打印逻辑。


抽象类和接口的异同:

抽象类和接口都是为了将方法进行抽象,然后让子类去实现。

public abstract class Animal{
	public abstract void eat();
}
public interface Runnable{
	public abstract void run();
}
相同一:可以定义抽象方法

所以可以定义抽象方法,就是两者第一个相同点。

相同二:不能创建本类对象

第二个相同点是不能创建本类对象,也就是这种父类是没有必要去创建实例对象的。只能由子类去实例化子类对象。


差异一:子类拓展时的关键字不一样

这里他们有个明显的差异点,就是子类要拓展他们时,对抽象类用的是extends关键字,我们称之为继承。

接口用的是implements关键字,我们称之为实现。这只是关键字和称呼的不同,本质都一样。

public class Dog extends Animal{}
public class Thread implements Runnable{}

然后,抽象类可以去实现接口,接口只能继承接口,不能继承类。

public abstract class Animal implements A{}
public interface Runnable extends A,B,C{}

同时,一个类最多只能继承一个父类,但可以实现多个接口。

public abstract class Pet extends Animal implements A,B,C{}

所以,当我们发现既可以用抽象类,也可以用接口时,尽量选择用接口,这样子类的灵活度会更高。

差异二:抽象类可以定义成员属性,而接口不能定义成员属性

抽象类更进一步的抽象后,就诞生了接口,接口比抽象类更纯粹,因为他没有了成员属性,只有方法。

子类实现接口后,唯一能做的就是重写方法。不像抽象类,子类继承抽象类之后,连带着将父类的成员属性也继承过来了。

这里就是两者的又一差异点:抽象类可以定义成员属性,而接口不能定义成员属性,只能定义静态属性,而且只能用final关键字定义静态常量,不能定义静态变量。

接口除了没有成员属性外,还没有构造器,可以说是非常纯粹了。

public abstract class Animal{
	protected String name;
	public abstract void eat();
}
public interface Runnable{
	public static final String CRAB="螃蟹";
	public abstract void run();
}

说白了,接口就是一个只有方法和静态常量的类。

这里你可能会有疑惑,抽象类都不能被实例化,还要构造器有啥用呢?

public abstract class Animal{
	protected String name;
	protected Animal(String name){
		this.name=name;
	}
	public abstract void eat();
}

class Dog extends Animal{
	public Dog(String name){
	super(name);
}
@Override
	public void eat(){
		System.out.println(name+"要开吃了~");
	}
}

他的用处就是:限定子类的构造行为。

比如,抽象类可以将构造器定义好几个参数,子类要想实例化则必须想办法传入这几个参数才行,两者的异同就讲完了。


一些重要的版本差异:

接口在java8之前更加纯粹,那时接口只能定义抽象方法,不能自己实现方法,也不能定义静态方法。

到了java8,才可以定义静态方法,以及可以用default关键字来实现方法。

public interface Runnable{
	void run();
	default void defaultMethod(){
		System.out.println("default 方法");
		static void staticMethod(){
			Syste.out.println("静态方法");
		}
	}
}

这里不要死记硬背,一定要求理解Java设计者背后的意图,为什么突然在Java8加了这个特性呢?

因为Java8许多原有的接口新增了很多方法,这些新增的方法,会影响到之前的子类。

如果你继承了某个接口,突然接口新增了一个方法,而你没有去实现,你的子类就会编译失败。

为了保证向前兼容,Java8就推出了default关键字,被default关键字修饰的方法,就不是抽象方法了。

也就不会强制要求已有的子类去实现方法。

default方法出现后,大家发现还挺好用,就大量在接口在去实现一些默认逻辑。

public interface Runnable{
	void run();
	default void defaultMethod(){
		privateMethod();
		privateMethod();
		privateMethod();
	}
	private void privateMethod(){
		System.out.println("私有方法");
	}
}

可是一个方法中实现复杂逻辑,就会导致代码难以维护,于是Java9又推出了一个新特性,那就是能在接口中定义private方法,

这样就可以将一些内部逻辑拆开了,可以发现,越到后面,抽象类和接口的差异也就越小。


那什么时候改用抽象类,什么时候改用接口呢?

当你需要让子类继承成员变量,或者需要控制子类的实例化时,你就用抽象类,否则,你就用接口。

只要记住,接口是更加纯粹的抽象类,就能理解两者的异同。

纯粹就代表着精简,接口比抽象类少了成员属性和构造器,只留下了静态常量和方法,更能体现标准和规范的含义,这也是我们为什么经常说:要面向接口开发。


一段话总结:

接口更关注“规范”,就是一种“约束”——你实现了这个接口,就必须保证这些行为存在;不提供复用逻辑,纯粹就是规范。

抽象类更关注代码复用,让多个子类复用属性和通用方法,提升开发效率;抽象类的主要目的是将“共性抽取为模板”,减少重复代码。

需要补充的是:接口在 Java 8 之后有了 default 方法,也可以提供一定程度的代码复用。


【掌握程度】

能够清晰地定义接口和抽象类:解释它们的基本定义和特点。

理解它们的区别:包括成员变量、方法实现、继承和实现方式。

知道使用场景:描述接口和抽象类适用的不同编程场景。


Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐