前言
这两者经常在笔试以及面试中经常问及,甚至在项目实战中还在纠结用哪一种类型
今天就此问题详细剖析一下
以往我的文章中也有稍微提及: java框架零基础从入门到精通的学习路线(超全)
此处将其这些文章抽离公共部分,详细总结下这两概念
1. 抽象类
抽象类是类和类之间的共同特征,将这些共同特征进一步形成抽象类,由于类本身不存在,所以抽象类无法创建对象。
类到对象是实例化,对象到类是抽象
抽象方法不能被 final 修饰,因为抽象方法就是被子类实现的
- 采用 abstract 关键字定义的类就是抽象类,采用 abstract 关键字定义的方法就
是抽象方法
- 抽象的方法只需在抽象类中,提供声明,不需要实现
- 如果一个类中含有抽象方法,那么这个类必须定义成抽象类。抽象类中不一定有抽象方法,抽象方法必须出现在抽象类中
- final和abstract不能同时同时使用,这两个关键字是对立的
- 抽象类的子类可以是抽象类。也可以是非抽象类
- ==一个非抽象的类,继承抽象类,必须将抽象类中的抽象方法进行覆盖/重写/实现==
==抽象类无法实例化,无法创建对象,抽象类是被子类来继承的==
抽象类可以有抽象类的子类
public class AbstractTest01 {
public static void main(String[] args) {
new a();//错误,不可创建对象
}
}
//定义一个抽象类
abstract class a{
}
//不是抽象类可以继承抽象类并且实例化
class b extends a{
}
//抽象子类可以继承抽象类
abstract class c extends a{
}
==抽象类无法实例化,但抽象类有构造函数可以供子类使用==
如果抽象类定义了有参构造函数而没有无参构造函数,子类定义找不到无参构造函数会出错,抽象类无法实例化调用对象(new对象),但是有构造函数可以给子类使用
abstract class a{
}
class b extends a{
public b(){
super();
}
}
==抽象方法表示没有实现的方法,没有方法体的方法==
- 没有方法体,以分号结尾
- 前面的修饰符列表中有abstract关键字
比如public abstract void dosome();
但是java语言中凡是没有方法体的方法都是抽象方法(×)
因为Object类中就有很多方法都没有方法体,都是以“;”结尾的,但他们都不是抽象方法
public class AbstractTest03 {
public static void main(String[] args) {
//此时不能再 new Employee 了
Person p = new Employee();
} }
abstract class Person {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
//此方法各个子类都可以使用
public void commonMethod1() {
System.out.println("---------commonMethod1-------");
}
//采用 abstract 定义抽象方法
public abstract void printInfo();
}
abstract class Employee extends Person {
//再次声明该方法为抽象的
public abstract void printInfo();
}
class Student extends Person {
//实现抽象的方法
public void printInfo() {
System.out.println("Student.printInfo()");
}
}
2. 接口
接口是特殊的抽象类,类与类是继承extends,类与接口是实现implements,其实都是继承
- 接口是一种“引用数据类型”,完全抽象的,支持多继承,且一个接口可以继承多个接口,只有常量+抽象方法
- 所有的元素都是public修饰的,抽象方法的public abstract可以省略,常量的public static final可以省略,方法不能有方法体
定义的格式
[修饰符列表] interface 接口名{}
==支持多继承,且一个接口可以继承多个接口==
每一个interface 都会生成一个class后缀名的文件
public class text1 {
public static void main(String[] args) {
}
}
interface a{
}
interface b extends a{
}
interface c extends a,b{
}
==接口中的方法是抽象,所以不能有方法体==
==定义抽象方法的时候可以省略修饰符public abstract
==
interface a{
int sum(int a,intb);//可以省略,默认系统会给你加上
void dosome();//可以执行通过
void dosome(){};//不可以执行通过,不能有方法体
}
==常量的定义结合final 是public static final可以省路==
public class text1 {
public static void main(String[] args) {
//定义输出的时候 需要用类名加变量 也就是a.i
//而且不允许在修改常量
}
}
interface a{
public static final int i=100;
int z=1;//z也是一个常量而不是变量 不能修改
}
3. 两者结合
==当一个非抽象的类实现接口的话,必须将接口中所有的抽象方法全部实现(覆盖、重写)==
// 特殊的抽象类,完全抽象的,叫做接口。
interface MyMath{
double PI = 3.1415926;
int sum(int a, int b);
int sub(int a, int b);
}
// 这样没问题
abstract class MyMathImpl implements MyMath {
}
//编译出错
class MyMathImpl implements MyMath {
}
//添加方法重写实现
//修正
class MyMathImpl implements MyMath {
//错误:正在尝试分配更低的访问权限; 以前为public
/*
int sum(int a, int b){
return a + b;
}
*/
// 重写/覆盖/实现 接口中的方法(通常叫做实现。)
public int sum(int a, int b){
return a + b;
}
public int sub(int a, int b){
return a - b;
}
}
接口中的方法必须是public :
如果删除执行不通过,因为接口是public,如果继承后,不写public执行后是分配更低的权限这是不行的,所以要添加public
==结合多态面向接口编程==
public class Test02{
public static void main(String[] args){
//错误: MyMath是抽象的; 无法实例化
//new MyMath();
// 父类型的引用指向子类型的对象
MyMath mm = new MyMathImpl();
// 调用接口里面的方法(面向接口编程。)
int result1 = mm.sum(10, 20);
System.out.println(result1);
int result2 = mm.sub(20, 10);
System.out.println(result2);
}
}
==接口与接口之间可以多继承,一个类也可以多个接口==
interface X{
}
interface Y{
}
interface Z extends X,Y{ //接口和接口支持多继承。
}
一个类可以实现多个接口
java中类和类只支持单继承。实际上单继承是为了简单而出现的,java中的接口弥补了单继承带来的缺陷
interface A{
void m1();
}
interface B{
void m2();
}
interface C{
void m3();
}
// 实现多个接口,其实就类似于多继承。
class D implements A,B,C{
// 实现A接口的m1()
public void m1(){
}
// 实现B接口中的m2()
public void m2(){
System.out.println("m2 ....");
}
// 实现接口C中的m3()
public void m3(){
}
}
==强转类型的时候,需要有继承关系才可==
A a = new D();
//a.m2(); // 编译报错。A接口中没有m2()方法。
B b = new D();
C c = new D();
// 这个编译没问题,运行也没问题。
// 调用其他接口中的方法,你需要转型(接口转型。)
B b2 = (B)a;
b2.m2();
// 直接向下转型为D可以吗?可以
D d = (D)a;
d.m2();
但如果没有继承关系不能强转
M m = new E();
// 经过测试:接口和接口之间在进行强制类型转换的时候,没有继承关系,也可以强转。
// 但是一定要注意,运行时可能会出现ClassCastException异常。
// 编译没问题,运行有问题。
K k = (K)m;
if(m instanceof K){
K k = (K)m;
interface K{
}
interface M{
}
class E implements M{
}
==继承和实现同时存在==
extends在前 implements在后
public class Test04{
public static void main(String[] args){
// 创建对象(表面看Animal类没起作用!)
Flyable f = new Cat(); //多态。
f.fly();
}
}
// 动物类:父类
class Animal{
}
interface Flyable{
void fly();
}
// 动物类子类:猫类
// Flyable是一个接口,是一对翅膀的接口,通过接口插到猫身上,让猫变的可以飞翔。
class Cat extends Animal implements Flyable{
public void fly(){
System.out.println("飞猫起飞,翱翔太空的一只猫,很神奇,我想做一只猫!!");
}
}
4. 面试总结
此处的总结偏向于两者概念的总结,将其制作成表格
- 抽象类是半抽象的,有构造方法,只允许出现常量和抽象方法。类和类之间只能单继承,一个抽象类只能继承一个类(单继承)。
- 接口是完全抽象的,没有构造方法,接口和接口之间支持多继承,一个类可以同时实现多个接口
比较内容 | 抽象类 | 接口(特殊的抽象类) |
---|---|---|
构造方法 | 可以有 | 不可以有 |
方法 | 可以有抽象方法(抽象方法只能被abstract修饰,不可被private、static、synchronized和native修饰)和普通方法 | 只能有抽象方法,但1.8版本之后可以有默认方法。接口只有定义,不可有方法实现 |
实现 | extend | implments |
类修饰符 | public、default、protected | 默认public |
变量 | 可以有常量也可以有变量 | 只能是静态常量默认有public static final修饰,必须附上初始值,不能被修改 |
多继承 | 单继承 | 多个接口 |
静态方法 | 可以有 | 不可以 |
应用场景:
- 关系密切:多个功能的集成部分,可用抽象类(抽象类提供简单方法)
- 关系不密切:不相关的类提供公共部分,可用接口