文章目录
前言
翻了一下大多数的博客,发现大多对抽象类和接口的描述都是一堆总结堆砌,有时候很难理解其中的概念,这里详细解释一下其中的原理和应用
一、抽象类
1、什么是抽象类?
- 关键字 abstract 修饰的类是抽象类。
- 抽象类即抽象化的,不可描述的。
- 理论上讲,你可以把类定义任何事物和行为,但是不符合面向对象编程思想,既然是面向对象的,行为的对象定义成类,那么抽象类也应该是定义有意义的对象,而不是定义行为。
abstract class Animal { 抽象类 —— 动物
}
2、抽象类的定义规范和要求
2.1、抽象类不能被实例化
- 抽象类不能被实例化。简单而言就是不能使用 new 关键字创建实例
为什么?
1、抽象类是属于对事物对象的抽象化,不可具体的。我们要理解实例化这个概念,就是可创造出来,比如猫狗,现实是确切存在的,你可以实例化出来一个狗,或者猫,但是动物这个抽象类,你可以实例化出来什么?换个说法:你可以把这个"动物"创造出来吗?
2、抽象类设计的目的就是为了多态性、扩展性、复用性等,子类继承才可达到目的。我们需要具体去实现什么功能,调用什么方法,才需要实例化,抽象类内方法是抽象的,实例没有意义(虽然有普通方法可以实现,但是这样理解没问题)
2.2、抽象类内的属性和方法定义
- 2.2.1、抽象类可以定义自己的成员变量和常量、抽象方法、普通方法
abstract class Animal {
定义变量
String name;
抽象方法
abstract void eat();
普通方法
public void play() {
System.out.println("玩耍");
}
}
- 2.2.2、抽象类有构造函数
abstract class Animal {
空参构造,无报错
public Animal() {
}
}
不能实例化,为什么有构造函数?
1、构造函数不是作用于实例化的,主要作用是对类属性初始化操作。抽象类可以定义变量,自然需要构造函数来赋值等操作
抽象类属于被继承才可以实现调用属性和方法,在重写方法中,第一行有个默认代码 super(); 默认调用父类的构造方法,所以既然有了继承、重写,为了实现super(),自然需要构造函数。
2、抽象类属于特殊类,而Java规定每个类都需要构造函数
构造函数是没有返回值的
2.2、抽象类的修饰符要求
- 1、在抽象类中,可以使用权限修饰符public、private、protected修饰方法和属性,默认public,但private 修饰的属性和方法不能被子类继承
abstract class Animal {
普通方法
public void playO() {
System.out.println("玩耍0");
}
protected void play1() { ——> 不可继承
System.out.println("玩耍1");
}
protected void play2() {
System.out.println("玩耍2");
}
}
- 2、可以使用静态关键字 static 修饰普通方法、代码块、成员变量
abstract class Animal {
普通方法
static String name;
普通方法
static void playO() {
System.out.println("玩耍0");
}
代码块
static {}
}
static 不可修饰构造函数、不能修饰抽象方法,这了解一下即可
- 3、普通方法和抽象方法
1、在抽象类中,可以有普通方法,也可也有抽象方法
2、普通方法中可以实现自己的方法体内容
3、抽象方法以分号结尾,无方法体
- 4、abstract 只能修饰抽象类和抽象方法
2.4、继承类要求
继承使用关键字 extends 实现对类或者抽象类的继承
- 2.4.1、实现抽象方法
抽象类中定义的抽象方法必须实现重写,普通方法可以不重写,如果不实现抽象方法,则报错
为什么一定要实现?
抽象类属于模板设计,什么是模板化设计,简而言之,就是其他子类继承了抽象类,现在全部子类的公共部分都需要更新,我不可能一个一个去更新吧,所以我更新代码时,更新抽象类的方法即可,可以理解为抽象类属于子类的公共模块,模块内的东西更新了,其他子类自然也更新。
抽象类的目的所在就是这个,你不实现,那设计这个抽象类干嘛?所以Java规定强制实现抽象方法
扩展一下
1、重写发生在父类和子类之间,方法的返回类型、方法名、参数类型都一致,且权限修饰符必须大于或等于父类
2、重载发生在一个类中,针对方法参数的不同,包括参数类型、参数个数、参数顺序不同,都是重载,无关返回值,返回值可相同,也可不同,即跟方法的权限修饰符、返回值类型、形参、方法体都没有关系
3、抽象类的应用
3.1、实现共有特性特征和行为
当我们把事物抽象出一个共性时,确定这个事物的最基本行为和属性都不会变,必须存在,就可以使用抽象类的抽象方法和属性定义,如果实现扩展,也可以直接继承抽象类抽象方法,实现行为的扩展。这也是多态的一个体现。
比如我定义一个狗类动物的抽象类,这类动物有毛、会吃等这些都是基本属性和行为,新增其他狗类时,直接继承扩展基本方法,如果这个狗有其他特定行为,在自定义独有方法即可。
这个场景强调的是不会变,共有的,必须存在的行为和属性。比如狗是四肢行走这个行为定义,但是我现在加一条狗品种,它必须也是四肢行走,才会归属狗类动物中
3.2、代码复用
这个是抽象类的目的之一,当子类普遍存在一个行为和共有属性(可有可没有),即可以使用抽象类,实现代码的复用
这个场景主要强调复用,我需要就直接调用父类的即可,不用自己再去具体实现。如果不需要,则可以不调用即可,灵活度高
4、抽象类总结
- 抽象类不能被实例化
- 抽象类可以有普通方法,也可以有抽象方法
- 抽象方法必须被子类实现
- 子类中可以有抽象方法,但是子类也必须变成抽象类
- 抽象类和子类属于继承,一定有关系
- 抽象类属于模板设计
二、接口
1、什么是接口?
用 interface 定义的是接口,子类使用关键字 implements 实现接口
接口也是一种事物的抽象,但它是对行为的抽象
public interface Demo{ 定义接口
//抽象方法
void play();
}
2、接口的的定义规范和要求
2.1、接口不能被实例化
为什么?
1、接口不是类,只是对行为的规范,不是类怎么实例化,因为接口是对行为的抽象,即使实例化也不符合面向对象思想
2、接口是抽象的,其内方法方法是抽象的,属性属于静态、不可修改的,所以实现实例没有意义
3、接口内的方法声明和属性,在内存都存储在元空间中,实例化new意味着在堆中开辟一块内存存储,但是接口需要存储的都存储了,白占据一块空间内存干嘛?所以接口不需要实例化
关于内存知识这里不多说,在内存主要分为堆、栈、元空间
有人问那接口定义静态属性也存储在元空间吗?是的,静态常量(接口只可以定义静态常量,下文讲)存储在元空间的静态数据区中
2.2、接口内的抽象方法
- 新增了默认方法,暂且先不说
- 除了默认方法其他方法全是抽象方法,必须被子类实现
为什么?
因为接口属于行为的抽象,简而言之就是对功能的抽象,设计接口就是为了其他子类实现,方便扩展,由子类去实现不同的状态和结果,也属于多态性的表现,所以不需要在接口内实现,
2.3、接口方法和属性定义
2.3.1、接口方法和属性的编译
- 接口内方法只能被 权限修饰符 public 修饰(默认),因为接口是给其他子类实现的,需要公开的权限,所以不能是 private、protected 、 final
- 接口内属性定义必须初始化
为什么?
在定义属性时,编译器在编译的时会自动加上 public static final 修饰符,所以必须初始化,即赋值
方法也会自动加上 public abstract
public interface Demo{
//抽象方法
public abstract void play();
//常量
public static final String s = "我是接口";
}
2.3.2、接口没有构造方法
为什么没有呢?
从上面例子就明显能看出,接口不需要构造方法
1、接口属于实现,不是继承,所以不用担心违反super()的执行
2、接口内的属性都是常量,静态、不可改变的,所以不需要构造方法来初始化
2.4、接口的静态方法
接口可以在接口内定义静态方法,有方法体,因为不属于类,只是被子类实现,所以区别于类名调用静态方法,而是接口名调用静态方法
为什么添加静态方法?
随着发展,需求的增加,有时候需要在接口内定义静态方法,才能达到特定目的,而接口中的静态方法有个很多的特点就是可以直接使用接口名调用静态方法,不用创建实例,而对于一些工具类,把特定的方法聚集在接口中,是更好的选择,更加方便也提高了接口的适用性
2.5、接口的默认方法
- 接口可以使用关键字 default 定义默认方法,有方法体
- 默认方法可实现,也可不实现
- 默认方法是指可以在不破坏接口实现的情况下,向现有的接口添加新的方法
为什么定义默认方法?
1、默认方法是指可以在不破坏接口实现的情况下,向现有的接口添加新的方法。不破坏接口实现的情况下,大大提高的接口的适用性,使接口具有了更好的扩展性。当我们大部分子类需要新增一个方法时,就可以在接口定义一个默认方法,子类调用即可
2、实现代码代码复用性:它可实现,也可不实现,需要则实现即可,当有一些重复性代码、共性代码时,就可使用默认方法写在接口中,大大减少了代码的重复性,提高代码的复用性
3、接口总结
- 接口是抽象化的,不能实例化
- 接口抽象方法必须实现,默认修饰 public abstract
- 接口属性是被 public static final 修饰,静态的、不可修改的
- 接口没有构造方法
- 接口有静态方法,含有方法体,主要特点是使用接口名调用静态方法
- 接口有默认方法,含有方法体,主要目的是为了扩展性和复用性
三、抽象类和接口区别
- 抽象类是模板设计,是对事物主体的抽象,接口是行为规范,对行为的抽象
- 接口和抽象类都不能实例化
- 抽象类可有构造函数,接口没有构造函数
- 抽象类可有成员变量,接口只能有 public static final 修饰的常量
- 抽象类主要修饰符有public、protected,,而接口默认修饰 public abstract
- 抽象类可以有抽象方法、普通方法,接口也有抽象方法、静态方法、默认方法,但接口不能有普通方法
- 一个子类只能继承一个抽象类,而一个子类却可以实现多个接口