0%

java接口和抽象类的异同

0. 前言

“java接口和抽象类的异同”这个题绝对是java面试中基础的, 高频的问题. 之前零零散散的复习过很多关于接口和抽象类讲解的文章, 但是没有系统的自己整理下来. 导致知识体系不全面并且很容易忘. 这次花点时间全面的分析一下两者的异同.

1. 概念上的异同

从设计理念上来说,

  • 两者都是对一些类的某些共同的特征的抽象.

  • 接口是对动作的抽象, 抽象类是对根源的抽象.

  • 接口的设计目的, 是对类的行为进行约束, 可以强制要求不同的类具有相同的行为. 而抽象类的设计目的是代码复用. (摘自知乎的一句话, 个人认为总结的很好)

    例如这个函数public void putAll(Map<? extends K, ? extends V> m)就通过接口Map强制要求传入的参数必须有Map接口Map定义的所有行为(所有方法).

    对于抽象类AbstractMap, 因为对于任何映射来说, 有很多操作是相同的. 比如get(), remove()等方法. 所以如果继承这个类, 新的AbstractMap实现类就只需要关注新的类特有的一些结构和操作. 而不需要关注所有实现类都相同的方法了.

2. 语法上的异同

语法上来说, 抽象类和接口真的就是相同的地方寥寥无几了.

首先看一下抽象类和接口相比于普通类在语法上有哪些特点.

2.1 抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* java abstract class test based on jdk 11
* @author Haiyang Yu
*/
public abstract class AbstractC // abstract class modifier cannot be final
// modifier "abstract" cannot be omitted
{
// all modifiers are allowed on a field
public int a = 1;
private int b = 1;
protected int c = 1;
int d = 1;
private volatile int e = 1;
private transient int f = 1;
private static int g = 1;
private final int h = 1;

//allow all constructors
public AbstractC(int a){;}
protected AbstractC(int a, int b){;}
AbstractC(int a, int b, int c){;}
private AbstractC(int a, int b, int c, int d){;}

// all regular methods are allowed
public void f1(){;}
private void f2(){;}
protected void f3(){;}
void f4(){;}
private static void f5(){;}
private native void f6();
private synchronized void f7(){;}
private static synchronized void f8(){;}

// abstract method
public abstract void abstractF1();
protected abstract void abstractF2();
abstract void abstractF3();
//private abstract void abstractF4(); // private cannot decorate the abstract method
//public native abstract void abstractF5(); // native cannot decorate the abstract method
//public static abstract void abstractF6(); // static cannot decorate the abstract method
//public synchronized abstract void abstractF7(); // synchronized cannot decorate the abstract method
//public static synchronized abstract void abstractF8(); // static synchronized cannot decorate the abstract method
}

通过上面的注释可以看到, 抽象类和普通类只有2点区别

1
2
1. 类修饰符. 不能是final, 并且abstract修饰符不能被省略.
2. 抽象方法. 普通类不能有抽象方法, 抽象类可以有被abstract修饰的抽象方法. 但是要注意, abstract不能和修饰符private, native, static, synchronized一起使用

除此之外, 成员变量, 构造器, 非抽象方法与普通类没有区别.

2.2 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

/**
* java Interface Test based on jdk 11
* @author Haiyang Yu
*/
public abstract interface In // interface modifier cannot be final
{ // interface modifier is always abstract

public static final int a = 1; // interface field is always public static final

//private int b = 1; //interface field cannot be private neither protected

//public In(){;} //cannot have public constructors
//private In(){;} //cannot have private constructors
//protected In(){;} //cannot have protected constructors
//In(){;} //cannot have default constructors

public abstract void abstractF1(); // regular abstract method is always public and abstract by default
//private void abstractF2(); // private abstract method is not allowed
//protected void abstractF3();// protected abstract method is not allowed
void abstractF4(); // equivalent to public void abstractF4();


//public void f1() {;} //cannot have regular method, default modifier required

default void f2() {;}
default public void f3() {;} // default method is always public
private void f4(){;} // private method is allowed since jdk1.9


public static void staticF1() {;} //static method is allowed since jdk1.8
// static method is public by default
private static void staticF2() {;} // private static method is allowed since jdk1.9
//protected static void staticF3() {;} // private static method is not allowed
}

接口与普通类的差别就非常大了. 可以说是几乎没有什么共同点.

我们从类修饰符, 成员变量, 构造器, 普通方法, 抽象方法 这5个角度来分析.

1
2
3
4
5
6
7
8
1. 类修饰符 接口的修饰符不能是final, 默认是abstract修饰. 所以不写abstract也可以.
2. 成员变量 接口中的成员变量默认是public static final修饰的. 不能含有protected, private, volatile, transient修饰的变量.
3. 构造器 接口中不能含有任何构造器
4. 普通方法 接口在这一点是最复杂的. 因为会随着jdk版本的变化而变化
在jdk1.7及之前, 默认不能有普通方法的.
在jdk1.8中, 可以有默认的方法和默认的静态方法. 但是默认方法前面要加修饰符default. 同时, 默认方法和默认静态方法的访问控制级别是public修饰的, 不写也是public, 而不是默认的访问控制级别.
在jdk1.9中, 可以有私有方法和私有静态方法. 对于protected和默认的访问控制级别, 还是不允许.
5. 抽象方法 接口中抽象方法默认是public的和abstract的. 其他的访问控制级别不允许. 普通类中不允许有抽象方法.

2.3 接口和抽象类的异同

通过上面两部分的分析, 很容易得出结论了

我们还是从类修饰符, 成员变量, 构造器, 普通方法, 抽象方法 这5个角度来分析.

1
2
3
4
5
6
1. 接口和抽象类的修饰符不能是final, 必须被abstract修饰. 但是接口是默认abstract的, 可以不用写.
2. 接口的成员变量默认是public static final的. 抽象类的成员变量和普通类一样, 没有限制
3. 接口不能有构造器, 抽象类的构造器和普通类一样, 没有限制
4. 接口中的普通方法只能有4种类型, default 方法, default static方法, private 方法, private static方法. 其他类型的方法不允许. 并且也不允许存在synchronized, native关键字. 其中default方法的访问控制级别默认是public的. 抽象类中的普通方法没有任何限制.
5. 接口中的抽象方法默认带了public, abstract关键字, 所以不写也可以. 但是其他的访问级别(private, protected)是不允许的.
抽象类的抽象方法就必须写abstract关键字了. 并且访问控制基本可以包含public, protected, 默认访问级别. 但是private的访问级别是不被允许的. 同时, abstract不能和修饰符private, native, static, synchronized一起使用.
1
2
对了, 还有个共同点, 就是这两个都不能被实例化.
还有个点, 就是接口和类的区别, 即接口可以extends多个接口, 不能extends类. 抽象类可以extends一个类, 可以implements多个接口

总结到这里, 感觉应付一般的面试够用了. 但是, 在学习的过程中由发现了很多新的问题, 比如, **接口中能有内部类吗, 能有内部接口吗?, 内部抽象类的特点和抽象类还一样吗?,内部的接口或抽象类的修饰符有哪些要求, 必须是static吗, 允许final吗?**等等. 所以, 学习是一个不断的探索过程, 会的越多, 不会的越多.

希望每个人对学习有一颗敬畏之心, 要知道山外有山, 天外有天. 这样才能不断的进步.