java基础——抽象类和接口

时间:2021-11-22 15:58:48
抽象类和接口
在实际的项目中,整个项目的代码一般可以分为结构代码和逻辑的代码。就像建造房屋时,需要首先搭建整个房屋的结构,然后再细化房屋相关的其它的结构,也像制造汽车时,需要首先制作汽车的框架,然后才是安装配件以及美化等工作。程序项目的实现也遵循同样的道理。在项目设计时,一个基本的原则就是——“设计和实现相分离”。也就是说结构代码和逻辑代码的分离,就像设计汽车时只需要关注汽车的相关参数,而不必过于关心如何实现这些要求的制作。程序设计时也是首先设计项目的结构,而不用过多的关系每个逻辑的代码如何进行实现。前面介绍的流程控制知识,主要解决的是逻辑的代码的编写,而类和对象的知识,则主要解决结构代码的编写。那么还有一个主要的问题:如何设计结构代码呢?这就需要使用下面介绍的抽象类和接口的知识了。
抽象类
抽象类(Abstract Class)是指使用abstract 关键字修饰的类,也就是在声明一个类时加入了abstract 关键字。抽象类是一种特殊的类,其它未使用abstract 关键字修饰的类一般称作实体类。例如:
public abstract class A{
        public A(){}
}

抽象方法(Abstract Method)是指使用abstract 关键字修饰的方法。
抽象方法是一种特殊的方法,其它未使用abstract 关键字修饰的方法一般称作实体方法。
public abstract void test();
抽象类和实体类相比,主要有以下两点不同:
★抽象类不能使用自身的构造方法创建对象(语法不允许)
例如下面的语法是错误的:
A a = new A();
但是抽象类可以声明对象,例如下面的代码是正确的:
A a;
A a1,a2;
只是声明出的对象默认都是null 的,无法调用其内部的非静态属性和非静态方法。
说明:抽象类可以使用子类的构造方法创建对象。
★抽象类内部可以包含任意个(0 个、1 个或多个)抽象方法
抽象类内部可以包含抽象方法,也可以不包含抽象方法,对于包含的个数没有限制。而实体类内部不能包含抽象方法。
在抽象类内部,可以和实体类一样,包含构造方法、属性和实体方法,这点和一般的类一样。
抽象方法和实体方法相比,主要有以下几点不同:
★抽象方法没有方法体
也就是说在声明抽象方法时,不能书写方法体的{},而只能以分号结束方法。
下面是实体方法和抽象方法声明的比较:
抽象方法声明:
public abstract void test(int a);
实体方法声明:
public void test(int a){
        方法体
}

★抽象方法所在的类必须为抽象类
也就是说,如果抽象方法声明在一个类内部,则该类必须为抽象类。(说明:抽象方法也可以出现在接口内部,这个将在后续进行介绍)。这样,在继承时,如果继承的类是抽象类,而该抽象类中还包含抽象方法时,则该子类必须声明成抽象类,否则将出现语法错误。如果子类需要做成实体类的话,则必须覆盖继承的所有抽象方法。这个是抽象类最核心的语法功能——强制子类覆盖某些方法。
介绍了这么多抽象类和抽象方法的知识以后,那么抽象类有什么用途呢?
抽象类的用途主要有两个:
★严禁直接创建该类的对象
如果一个类内部包含的所有方法都是static 方法,那么为了避免其它程序员误用,则可以将该类声明为abstract,这样其它程序员只能使用类名.方法名调用对应方法,而不能使用对象名.方法名进行调用。
这样的类例如API 中的Math类说明:配合final 关键字使用,将必须该类被继承,这样将获得更加完美的效果。
★强制子类覆盖抽象方法
这样可以使所有的子类在方法声明上保持一致,在逻辑上也必须将方法的功能保持一致。例如游戏中设计类时,设计了怪物类以及相关的子类,每个怪物类都有移动方法,但是每种怪物的移动规则又不相同,这样通过使每个怪物类的移动方法的声明保持一致,方便调用。可以参看前面多态部分的介绍获得更多的关于调用统一知识。
这是抽象类最主要的用途。就像现实社会中,各种银行网点保持统一的装修风格,各种快餐店(肯德基、麦当劳等)保持统一的装修甚至风味,这样便于生活中的识别。通过让存在继承关系的类中功能一样(但是内部实现规则不同)的方法声明成一样的,方便多态的使用。
那么什么时候在设计时使用抽象类呢?这个问题参看一下抽象类的用途自然就知道了。关于抽象类的知识先介绍这么多,下面介绍接口的知识,最终将对抽象类和接口进行一下比较。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
接口
接口(Interface)是一种复合数据类型。
接口就是一个纯粹用来设计的数据类型,在接口这种数据类型中,只能书写两类声明的结构:
★常量数据
所有的常量数据都是public static 的。如果声明时不书写则系统将自动添加这两个修饰符。
抽象方法
接口中的所有方法都只在逻辑上规定该方法的作用,而不能书写方法体。所有接口中的方法都是public abstract 的,如果声明时不书写则系统将自动添加这两个修饰符。
其中接口中的数据是常数,以后不能改变,而方法只是规定要做什么,而不去规定如何进行实现。这样接口就很方便设计人员进行设计,而不必过多的关系对应的方法如何在逻辑上进行实现。
接口声明的语法格式如下:
访问控制符    interface    接口名    [extends     父接口名1,    父接口名2……]{
        常量声明
        方法声明
}
和类的声明一样,访问控制符只能使用public 和默认的。声明时使用interface 关键字声明接口,接口可以继承其它的接口,使用extends 关键字进行继承,多个接口名之间使用逗号进行分隔。和类的集成一样,子接口继承父接口中所有的常量数据和方法,子接口的对象也是父接口的对象。
注意:和抽象类一样,接口只能声明对象,而不能创建对象。接口声明的示例代码如下,
例如声明一个USB 接口来代表实际使用中的USB 结构:
public interface USB{
        /**电压*/
        public static final int V = 5;
        /**读取数据*/
        public abstract byte[] readData();
        /**写入数据*/
        public abstract void writeData(byte[] data);
}
该接口中规定电压常量为5V,声明了两个方法,要求实现USB 时必须实现这样两个方法,至于如何实现这里不做规定。这样这个数据类型就只是设计上的说明,而不牵扯具体的实现,这样在项目中使用时则比较通用。
从这点来看,接口类似于现实中使用的各个国家标准,标准中只规定该类型最终需要达到的标准,而不规定如何实现,各个厂商可以根据自己的产品工艺实现该要求即可。
在实际的项目中,设计接口需要对于项目的整体有比较深刻的了解和认识,这样才可以设计出需要的接口结构,关于接口的设计这里不作太深入的论述。如果需要更深刻的了解设计的结构, 
一般声明对应的类来实现接口,实现接口的语法为:
访问控制符    [修饰符]     class     类名    [extends 父类名]    implements     父接口名1,    父接口名2……
实现接口的语法位于类声明中,位于继承声明的后面,使用implements关键字代表实现,后续是需要实现的接口的名称,一个类可以实现任意多个接口。
实现接口和继承类很类似,声明的类称作接口的子类,接口为该类的父接口,子类中继承父接口中所有的数据和方法,因为接口中所有的方法都是抽象方法,所以如果子类中不实现(覆盖)父接口中的方法,则该类必须声明为抽象类。
例如计算机实现了USB 接口,则示例代码如下:
public class Computer implements USB{
        /**内存容量*/
        int memorySize;
        public abstract byte[] readData(){
        //读数据的逻辑
        }
        public abstract void writeData(byte[] data){
        //写数据的逻辑
        }
}

这里,Computer 类实现了前面的USB 接口,在Computer 类内部可以书写和Computer 类相关的属性、方法和构造方法,这里对于实现接口没有影响,而因为实现了USB 接口,则必须覆盖USB 接口中的readData 和writeData 抽象方法,至于方法内部的代码,则根据逻辑的需要进行实现,从而实现接口中要求实现的功能。
类似的,也可以使一个数码相机实现USB 接口,则实现的示例代码为:
public class DigitalCamera implements USB{
        /**厂商名称*/
        String vendorName;
        public abstract byte[] readData(){
        //读数据的逻辑
        }
        public abstract void writeData(byte[] data){
        //写数据的逻辑
        }
}

在该类中,也可以根据该类的需要实现USB 接口中规定的功能,至于如何实现则很可能和Computer 类不同。这样,虽然Computer 类和DigitalCamera 类自身原来的功能不同,但是都可以通过实现USB 接口而实现同样的功能,这样单纯的从是否支持USB 功能来看,这两个类的实现是一样的。按照面向对象的术语来说,这被称作屏蔽了类的类型之间的不同,保证了程序的通用性。
由于实现接口时,不限制实现的接口的数量,则任何一个类都可以实现任意多个接口,这样就使类的通用性获得了极大的增强,很方便类的对象之间的匹配。就像现实中USB 接口规范方便了多种不同设备之间的互联一样。在语法上,实现了接口的对象可以使用子类的构造方法进行创建,这样又很适合多态的结构,可以说接口的出现,使多态的特性更容易的进行实现。
在实际项目中,通过使用一定的接口,使得很多类的对象在实现某种类型的功能时,方法的声明是统一的,便于程序的调用和管理,利于程序项目的扩展。所以在现在的面向对象编程领域中,存在着另外的一个方向——面向接口的编程,其实很多Java 的技术都是这样进行实现的,例如JDBC 部分。由于抽象类和接口的功能比较类似,后续将对于抽象类和接口进行一系列的比较,方便项目设计时的取舍。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------- 
抽象类和接口的比较
抽象类和接口都是进行面向对象设计时专用的设计结构,在实际进行项目设计时,经常需要考虑的问题就是——“使用抽象类还是接口”?下面通过对于抽象类和接口进行简单的比较,熟悉两者之间的区别和联系,从而在实际设
计时使用恰当的结构。
①:什么时候使用抽象类或接口?
当设计中为了规范类中方法声明的结构(即类的行为)时,使用抽象类或接口。也
就是强制子类对外部提供统一的方法声明时,使用抽象类或接口。
②:.抽象类和接口的区别(不同点)
★抽象类是类,而接口是接口。
因为抽象类是一个类,所以类内部可以包含的内容(构造方法、方法和属性等)在抽象类内部都可以存在,当然抽象类也受到类的单重继承的限制。而接口是接口类型,所以接口内部只能包含常量属性和抽象方法,但是一个类可以实现多个接口,所以接口不受类的单重继承的限制。
抽象类内部可以包含实体方法,而接口不能
抽象类是一个类,所以在抽象类内部既可以包含抽象方法也可以包含实体方法,而接口内部的每个方法都必须是抽象方法。
抽象类可以继承类,而接口不能
抽象类是一个类,所以在设计时可以使抽象类继承其它的类,在已有类的基础上进行设计,但是接口不能继承类。
③:抽象类和接口的联系(相同点)
抽象类和接口都可以声明对象,但是都只能使用子类的构造方法进行创建。
抽象类和接口内部都可以包含抽象方法。按照Java 语言的语法,子类在继承抽象类或实现接口时,都必须覆盖这些抽象方法,否则必须声明为抽象类。
抽象类和接口都可以代表一种类型,从而可以统一子类对象的类型,获得良好的可扩展性。
④:什么时候使用抽象类?
当满足以下的条件时,最好使用抽象类进行设计:
子类不继承其它父类
子类中存在完全相同的功能实现的方法
子类中存在相同的属性
设计出的结构需要继承其它类当需要满足d 条件时,只能使用抽象类,否则也可以考虑使用接口实现。
⑤:什么时候使用接口?
当满足以下的条件时,最好使用接口进行设计:
子类已经继承了其它父类
子类中不存在完全相同的功能实现方法
子类中不存在相同的属性
设计出的结构不需要继承其它类当需要满足a 条件时,只能使用接口,否则也可以考虑使用抽象类实现。
⑥:抽象类和接口的其它用途
禁止创建该类的对象时,可以把该类声明为抽象类。
当需要存储大量的常量数据,而这些常量数据将会在项目中的多个类之间使用时,可以使用接口。
当需要统一具有某种功能的类的对象时,可以使用接口。例如Serializable 接口。当然,只有经过大量的系统设计训练以后,才可以更加深刻的理解抽象类和接口的区别和联系,从而更加自如的进行选择。另外,需要说明的是,不是每个项目中都必须使用抽象类或接口的。