目录
一、引言
抽象类和接口是Java语言中对于抽象类定义进行支持的两种机制,赋予了Java强大的面向对象能力。接口和抽象类是在面向对象基础上的扩充,首先抽象类从字面解释就是一种很难想象,在java中抽象类就是多个事物可以抽象的看成一种事物,它们都有相同的方法,可以被理解为抽象,然而接口,是对类的一种实现。
抽象类和接口两者之间具有很强的相似性,从设计方向来说,二者的定位甚至都是一样的,皆是“将设计和实现彻底分离”, 很多人在初学的时候会以为它们可以随意互换使用,但是实际则不然。
本文通过对抽象类和接口的概念,特点,使用以及在java中存在的意义等几个方面做简单的介绍,通过实际案例分析抽象类和接口在使用过程中的不同,最终总结了抽象类和接口的异同点以及在实际操作中应该如何选择抽象类和接口。
二、抽象类
2.1抽象类的基本概念
- 抽象类是指在普通类的结构里面增加抽象方法的组成部分。
- 在所有的普通方法上面都会有一个“{}”,这个表示方法体,有方法体的方法一定可以被对象直接使用。而抽象方法,是指没有方法体的方法,同时抽象方法还必须使用关键字abstract做修饰。
- 拥有抽象方法的类就是抽象类,抽象类要使用abstract关键字声明。
范例:
abstract class A{//定义一个抽象类
public void fun(){//普通方法
System.out.println("存在方法体的方法");
}
public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
}
//定义一个Employee类
public abstract class Employee{
public abstract void work();//抽象函数。需要abstract修饰,并分号;结束
}
//定义一个Master继承Employee
class Master extends Employee {
public void work() {
System.out.println("正在赋予权限");
}
}
//定义Assistant类继承Employee
class Assistant extends Employee {
public void work() {
System.out.println("正在使用该系统");
}
}
//定义一个 Manager继承Employee
class Manager extends Employee {
public void work() {
System.out.println("正在维护此系统");
}
}
2.2抽象类的使用
- 抽象类不能创建对象,如果创建了,编译无法通过而报错,只能创建其非抽象子类的对象
- 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的
- 抽象类中不一定包含抽象方法,但是有抽象方法一定要是抽象类
- 抽象类的子类,必须重写抽象父类中所有抽象方法,否则,编译无法通过而报错, 除非该子类也是抽象类
2.3抽象类的特点
- 抽象类和抽象方法必须使用abstract关键字修饰
- 抽象类中不一定有抽象方法,但是有抽象方法的一定是抽象类或接口
- 抽象类不能实例化 (实例 = 对象) [也就是不能创建对象],如果非要实例化,可以通过多态的形式创建,也就是 父类引用指向子类对象
- 抽象类的子类 1.重写父类(抽象类)中所有的抽象方法[推荐方案] 2.要么将自己也变成一个抽象类
2.4抽象类的成员特点
- 成员变量:可以是一般变量,也可以是常量
- 成员方法:可以是一般方法,也可以是抽象方法
- 构造方法:有构造方法,目的是为了方便子类进行初始化
2.5抽象类中的注意事项
在使用抽象类时需要注意以下几点:
- 抽象类不能被实例化,实例化的工作应该交由它的子类来完成,它只需要有一个引用即可。
- 抽象方法必须由子类来进行重写。
- 只要包含一个抽象方法的抽象类,该方法必须要定义成抽象类,不管是否还包含有其他方法。
- 抽象类中可以包含具体的方法,当然也可以不包含抽象方法。
- 子类中的抽象方法不能与父类的抽象方法同名。
- abstract不能与final并列修饰同一个类。
- abstract 不能与private、static、final或native并列修饰同一个方法。
2.6java中抽象类存在的意义
- 通过继承它实现多态,后期绑定,可以为将来要实现的东西做好接口,实现重用性。
- 接口就是更纯粹的抽象类
2.7抽象类的例子
引例1:有三类图书,科技书,文艺书和教材,三类书都具有页码,折扣,每页价格三个属性,显示种类,显示图书价格两个方法(图书价格=每页价格页码折扣),用抽象类的方式完成相关代码
具体代码1:
//定义抽象类Book
abstract class BooK{
int bookPage;
float discount;
float pagePrice;//定义三个成员属性
public Object price;
public BooK(int bookPage,float discount,float pagePrice) {
this.bookPage=bookPage;
this.discount=discount;
this.pagePrice=pagePrice;
}
abstract void showKind();//显示图书种类
public float getPrice() {
return bookPage*discount*pagePrice;
}
//定义科技书
class ScienceBook extends BooK{
public ScienceBook(int bookPage,float discount,float pagePrice) {
super(bookPage,discount,pagePrice);
}
//实现抽象方法
public void showKind() {
System.out.println("The book's type is science");
}
}
//定义文艺书
class ArtBook extends BooK{
public ArtBook(int bookPage,float discount,float pagePrice) {
super(bookPage,discount,pagePrice);
}
//实现抽象方法
public void showKind(){
System.out.println("The book's type is art");
}
}
public class BookTest{
public void main(String[] args) {
// TODO Auto-generated method stub
BooK sb=new ScienceBook(520,0.7f,0.2f);
sb.showKind();
System.out.println(getPrice());
BooK tb=new ArtBook(400,0.8f,0.3f);
tb.price=tb.getPrice();
tb.showKind();
System.out.println(getPrice());
}
}
}
引例2:定义一个基础班月学员类和一个就业班学院类,两个类都包括姓名,年龄,班级等成员变量,学习和吃饭两种成员方法
具体代码2:
public abstract class student05{
//姓名
private String name;
//年龄
private int age;
//班级
private String grand;
public student05(){}
public student05(String name,int age,String grand){
this.name = name;
this.age = age;
this.grand = grand;
}
//getXxx()/setXxx()
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
public String getGrand(){
return grand;
}
public void setGrand(String grand){
this.grand = grand;
}
//学习study(抽象方法)
public abstract void study();
//吃饭eat(具体方法)
public void eat(){
System.out.println("学习累了,去吃饭");
}
}
//定义具体基础班学员类
class BasicStudent extends student05{
public BasicStudent(){}
public BasicStudent(String name,int age,String grand){
super(name,age,grand);
}
public void study(){
System.out.println("基础班学员学习的是JavaSE");
}
}
//定义具体就业班学员类
class WorkStudent extends student05{
public WorkStudent(){}
public WorkStudent(String name,int age,String grand){
super(name,age,grand);
}
public void study(){
System.out.println("就业班学员学习的是JavaEE");
}
}
public class student05test{
public static void main(String[] args){
//基础班学员测试
//多态(测试)
//方式1
student05 s = new BasicStudent();
s.setName("小刚");
s.setAge(20);
s.setGrand("bdqn_S1");
System.out.println(s.getName()+"---"+s.getAge()+"---"+s.getGrand());
s.study();
s.eat();
System.out.println("---------------------------------");
//方式2
s = new BasicStudent("小刚",20,"bdqn_S1");
System.out.println(s.getName()+"---"+s.getAge()+"---"+s.getGrand());
s.study();
s.eat();
System.out.println("---------------------------------");
//就业班学员测试
//方式1
s = new WorkStudent();
s.setName("小明");
s.setAge(25);
s.setGrand("bdqn_Y2");
System.out.println(s.getName()+"---"+s.getAge()+"---"+s.getGrand());
s.study();
s.eat();
System.out.println("---------------------------------");
//方式2
s = new BasicStudent("小明",25,"bdqn_Y2");
System.out.println(s.getName()+"---"+s.getAge()+"---"+s.getGrand());
s.study();
s.eat();
System.out.println("---------------------------------");
}
}
//运行结果:
小刚---20---bdqn_S1
基础班学员学习的是JavaSE
学习累了,去吃饭
---------------------------------
小刚---20---bdqn_S1
基础班学员学习的是JavaSE
学习累了,去吃饭
---------------------------------
小明---25---bdqn_Y2
就业班学员学习的是JavaEE
学习累了,去吃饭
---------------------------------
小明---25---bdqn_Y2
基础班学员学习的是JavaSE
学习累了,去吃饭
---------------------------------
三、接口
3.1接口的概念
- 接口是抽象方法的集合。如果一个类实现了某个接口,那么它就继承了这个接口的抽象方法。这就像契约模式,如果实现了这个接口,那么就必须确保使用这些方法。接口只是一种形式,接口自身不能做任何事情。
- 接口可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法所组成。接口是解决Java无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。或者我们可以直接把接口理解为100%的抽象类,既接口中的方法必须全部是抽象方法。(JDK1.8之前可以这样理解)
3.2接口的使用
由于接口里面存在抽象方法,所以接口对象不能直接使用关键字new进行实例化。接口的使用原则如下:
- 接口必须要有子类,但此时一个子类可以使用implements关键字实现多个接口
- 接口的子类(如果不是抽象类),那么必须要覆写接口中的全部抽象方法;
- 接口的对象可以利用子类对象的向上转型进行实例化。
3.3接口的特点
- 用 interface 来定义
- 接口中的所有成员变量都默认是由public static final修饰的
- 接口中的所有方法都默认是由public abstract修饰的
- 接口没有构造方法。构造方法用于创建对象
- 实现接口的类中必须提供接口中所有方法的具体实现内容
- 多个无关的类可以实现同一个接口
- 一个类可以实现多个无关的接口
- 与继承关系类似,接口与实现类之间存在多态性
- 接口也可以继承另一个接口,使用extends关键字
- 实现接口的类中必须提供接口中所有方法的具体实现内容
- 多个无关的类可以实现同一个接口
- 一个类可以实现多个无关的接口
- 与继承关系类似,接口与实现类之间存在多态性
3.4java中接口存在的重要意义
- 重要性:在Java语言中,抽象类和接口是支持抽象类定义的两种机制。正是由于这两种机制的存在,才赋予了Java强大的 面向对象能力。
- 简单、规范性:如果一个项目比较庞大,那么就需要一个能理清所有业务的架构师来定义一些主要的接口,这些接口不仅告诉开发人员你需要实现那些业务,而且也将命名规范限制住了(防止一些开发人员随便命名导致别的程序员无法看明白)。
- 维护、拓展性:比如你要做一个画板程序,其中里面有一个面板类,主要负责绘画功能,然后你就这样定义了这个类。可是在不久将来,你突然发现这个类满足不了你了,然后你又要重新设计这个类,更糟糕是你可能要放弃这个类,那么其他地方可能有引用他,这样修改起来很麻烦。如果你一开始定义一个接口,把绘制功能放在接口里,然后定义类时实现这个接口,然后你只要用这个接口去引用实现它的类就行了,以后要换的话只不过是引用另一个类而已,这样就达到维护、拓展的方便性。
3.5接口使用的案例
引例1:
//构建一个抽象类People
abstract class People{
//父类属性私有化
private String name;
private int age;
//提供父类的构造器
public People(String name,int age){
this.name = name;
this.age = age;
}
//提供获取和设置属性的getter()/setter()方法
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
//提供一个抽象方法
public abstract void talk();
}
//定义一个接口
interface Study{
//设置课程数量为3
int COURSENUM = 3;
//构建一个默认方法
default void stu(){
System.out.println("学生需要学习"+COURSENUM+"门课程");
}
}
//再定义一个接口
interface Write{
//定义一个抽象方法
void print();
}
//子类继承People,实现接口Study,Write
class Student06 extends People implements Study,Write{
//通过super关键字调用父类的构造器
public Student06(String name, int age) {
super(name, age);
}
//实现父类的抽象方法
public void talk() {
System.out.println("我的名字叫"+this.getName()+",今年"+this.getAge()+"岁");
}
//实现Write接口的抽象方法
public void print() {
System.out.println("学生会写作业");
}
}
public class InterfaceDemo{
public static void main(String[] args) {
//构建student对象
Student06 student = new Student06("dodo", 22);
//调用父类的抽象方法
student.talk();
//调用接口Write中的抽象方法
student.print();
//调用接口Study中的默认方法
student.stu();
}
}
//运行结果:
我的名字叫dodo,今年22岁
学生会写作业
学生需要学习3门课程
引例2:
public class TestInterface {
public static void main(String[] args) {
Volant volant = new Angel();
volant.fly();
System.out.println(Volant.FLY_HIGHT);
Honest honest = new GoodMan();
honest.helpOther();
}
}
/**
* 飞行接口
*/
interface Volant {
int FLY_HIGHT = 100; // 总是:public static final 类型的;
void fly(); //总是:public abstract
}
/**
* 善良接口
*/
interface Honest {
void helpOther();
}
/**
* Angel 类实现飞行接口和善良接口
*/
class Angel implements Volant, Honest {
public void fly() {
System.out.println("我是天使,飞起来啦!");
}
public void helpOther() {
System.out.println("扶老奶奶过马路!");
}
}
class GoodMan implements Honest {
public void helpOther() {
System.out.println("扶老奶奶过马路!");
}
}
//运行结果:
我是天使,飞起来啦!
100
扶老奶奶过马路!
四、抽象类和接口的异同
4.1相同点
- 都不能被直接实例化
- 都可以通过继承实现其抽类方法
4.2不同点
参数 | 抽象类 | 接口 |
---|---|---|
默认的方法实现 | 有默认的方法实现 | 接口是完全抽象的,他根本不存在方法的实现 |
实现 | 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。 | 子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现 |
构造器 | 抽象类可以有构造器 | 接口不能有构造器 |
与正常Java类的区别 | 除了你不能实例化抽象类之外,它和普通Java类没有任何区别 | 接口是完全不同的类型 |
访问修饰符 | 抽象方法可以有public、protected和default这些修饰符 | 接口方法默认修饰符是public。你不可以使用其它修饰符 |
main方法 | 抽象方法可以有main方法并且我们可以运行它 | 接口没有main方法,因此我们不能运行它。(java8以后接口可以有default和static方法,所以可以运行main方法) |
多继承 | 抽象方法可以继承一个类和实现多个接口 | 接口只可以继承一个或多个其它接口 |
速度 | 它比接口速度要快 | 接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。 |
添加新方法 | 如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码 | 如果你往接口中添加方法,那么你必须改变实现该接口的类 |
4.3如何选择抽象类和接口
- 如果你拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类
- 如果你想实现多重继承,那么必须使用接口。由于Java不支持多继承,子类不能够继承多个类,但可以实现多个接口。因此你就可以使用接口来解决它。
- 如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类。
五、总结
我们可以把接口看成一个工具,当我们实现一个接口时,就意味着能使用这一工具,当我们实现多个接口时,就能使用多种和工具,而抽象类则可以看成是一个祖先,所以一个类就只能继承一个祖先。在设计类的时候,首先考虑用接口抽象类的特性,当你发现某些方法可以复用的时候,可以使用抽象类来复用代码。简单说,接口用于抽象事物的特性,抽象类用于代码复用。
当然,不是所有类的设计都要从接口到抽象类,再到类。程序设计本就没有绝对的范式可以遵循。上面的说法只是提供一个角度来理解抽象类和接口的关系,每个人都会有自己的理解,有人认为两者一点关系都没有,这也有道理。总之,模式和语法是死的,人是活的。
参考文献: https://blog.csdn.net/aptentity/article/details/68942916
https://blog.csdn.net/m0_51529857/article/details/116354867
https://blog.csdn.net/weixin_45070922/article/details/113049308
https://blog.csdn.net/nmvc01/article/details/115637517