抽象类、接口、程序包与嵌套类的作用
面向对象程序设计中,类(Class)关系到程序中所有对象的创建。
四种扩展类型:
- 抽象类(Abstract Class)
- 接口(Interface)
- 程序包(Package)
- 嵌套类(Nested Class)
抽象类
只要在类名称前加入abstract关键字,就表示该类被声明成一个抽象类。无法直接通过抽象类来创建对象,必须先以继承的方式来声明并定义子类,再以子类创建对象。可以理解为抽象类并不是一个能完全代表对象的类。
在抽象类中的方法可以有抽象方法(Abstract Method),也可以有普通的实例方法(Method),如果在方法名称前面加上abstract关键字,就表示该方法是一个抽象方法。
抽象方法只有原型的声明,并没有具体的实现内容,当声明某方法为抽象方法时,必须以分号“;”结尾。抽象方法会隐藏程序代实现细节,抽象方法必须被子类继承再去实现覆盖。如果子类没有重新定义抽象方法,就会发生编译错误。
抽象类中所定义的抽象方法的主要目的是让程序定义更加完整,但是并没有任何实现的程序代码,真正的程序代码实现部分交由继承该抽象类的子类重新定义(具体实现)。
普通类不存在抽象方法,抽象类可以有抽象方法,也可以有实例方法。虽然无法以抽象类来直接创建对象,但是可以将抽象类声明成引用子类实列的对象变量。
抽象类的使用时机
有时必须声明一些只有“抽象”概念的方法,主要用途在于功程序以引用的方式来调用相关方法。可以把抽象基类看作完整程序的设计接口,而向下派生的相关类必须遵循抽象类的定义规范,覆盖抽象类内所有的成员方法。
声明、定义和使用抽象类
抽象方法声明语法:
abstract 返回值数据类型 成员方法 (参数行)
抽象类声明语法:
abstract class 类名称 {
... 类成员的程序语句
... 抽象成员方法的程序语句
}
抽象类完整的声明和定义语法:
abstract class 抽象类的名称 { // 关键字abstract
成员变量;
返回值类型 方法的名称(参数) {
方法的主体部分;
}
// 定义抽象方法,不具体实现方法的主体
存取权限修饰词 abstract 返回值类型 抽象方法的名称(参数);
}
使用抽象列有一些注意事项:
- 抽象类因为没有定义完整的类成员,不能用于创建对象,就是无法“直接”使用new运算符来实例化类。
- 仍然可以声明抽象类的构造函数(Constructor)。
- 抽象类可以保留普通的类方法。
- 抽象类仍可以使用引用(Reference)对象。
- 抽象方法的存取权限修饰词必须设置为public或protected,不可以设置为private,也不能使用static和final关键字来定义。
认识接口
接口中可以有任何程序语句,只能定义成员常数或抽象成员方法。而所有类成员的具体实现必须交由派生的相关类进行覆盖处理(重新定义)。
接口与抽象类的区别:
- 抽象类:一个派生类只能继承单一基类;至少包含一个完整的方法。
- 接口:可以编写内含多种接口的实现类;所包含的都是抽象方法。
接口可以被视为一种类的扩展,与抽象类相同,可以使用继承的模式轻松将各种不同的接口的成员方法加以结合,形成一个新的接口类型。
接口中所有的成员数据不需经过额外声明,都会自动地被定义成static与final类型,因此接口经常被用来定义程序中所需要的各种常数。
接口声明语法:
interface 接口名称 {
...
返回值的数据类型 成员方法();
...
}
接口的定义
再面向对象设计语言中,接口是负责定义两个互不相干对象(无类的继承关系)彼此互操作的行为协议(Protocol)。用接口的方式来时间多重继承的需求。
声明、定义与使用自定义接口
接口是采用interface关键字声明的一种类类型,不包含任何具体实现的程序语句,而只声明了抽象成员方法。
接口完整的声明和定义语法:
interface 接口名称 {
final 成员变量 = 初始值;
存取权限修饰词 abstract 返回值类型 抽象方法的名称(参数);
// 声明方法,但不具体实现方法的主体
}
- 使用接口有一些注意事项:
- 接口所声明的成员变量必须先初始化,而且不能再被修改。
- 接口中的类方法全部都声明成抽象方法,就是“只声明方法,但不具体实现方法的主体”。
- 不允许保留普通的类方法。
- 接口中的抽象方法存取权限修饰词只能声明为public或不做任何声明,而不可以声明为protected或private。
- 接口无法实例化,因此无法产生构造函数(Constructor)。
使用接口变量创建对象
接口的实现不能直接通过new运算符来创建对象,而必须通过“实现接口的类”来创建对象,或采用“父类变量引用子类创建对象”。
实现多重继承
如果类要实现两个以上的接口,那么在类中要清楚、明确地声明并定义接口中的抽象方法。
声明子接口
类引入继承的概念,有父类(SuperClass)与子类(SubClass)之分,接口引入继承概念,也有父类与子类的关系、基类与派生类的关系,分别对应的是父接口(SuperInterface)或基接口(BaseInterface)、子接口(SubInterface)或派生接口(DerivedInterface)。
继承的基本原则:“保有原来设计的功能并加以扩充,让程序代码可以重复使用。”
和类的不同之处:接口允许单个接口继承多个父接口。
大型程序的开发与应用---程序包的使用
所谓程序包(Package),就是将程序中所有相关的类、接口或方法加以汇总整合并“打包”,在不少程序设计语言中,程序也被称为函数库(Library)。
Java语言的工具程序包需要类似导入的声明方式,使用import关键字,再配合目标程序包的名称,即可导入所需的程序包。
程序分解的概念
将程序分解成许多不同功能的类(Class),将大程序分解成若干个独立的类有助于程序后续的开发和维护。
程序包的需求
将相同类型的类与接口加以封装,形成一种类集合形式的程序包模式。
Java系统之所以封装所有的相关类,有两个原因:
- 方便类名称的管理
- 提供存取保护机制
包装与导入程序包
要将指定类或接口汇总整合到目标程序包,必须在该类或接口的源文件的开头使用package关键字来执行打包操作。
声明语法:
package 指定路径名称;
在加入程序包之后,在编译和执行时稍有不同。新建类文件时,要指定package为test.mypackage。
在“命令提示符”环境下编译和执行的步骤:
- 编译:“javac目录名称\源文件名称.java”。
- 执行:“java程序包名称.类名称”。
当编译源文件时,系统会在当前工作路径中存放所有经过编译的类文件。
程序包编写的语法:
package mypackage; //将文件内所有类与接口汇总整合到mypackage文件夹中
在指定路径名称中使用“.”符号作为分隔符,以创建嵌套结构的路径。编译器会按照用户操作系统的不同自动转换成相应的路径字符串,以形成树形结构的存储目录。
package mypackage.file;
Java编译器会在当前工作路径下按序新建并打开mypackage与file子文件夹,用以存储所有源程序经过编译后的“*.class”文件。
导入程序包
类的来源两种:
- 同一个源文件中的类
- 不同源文件中的类
类的嵌套结构
在Java系统中可以通过静态嵌套类与内部类的声明操作将嵌套结构概念导入类的设计之中,让具有相关功能的类相互配合,进而方便程序设计人员控制这些类的使用范围(作用域)。
在Java类的声明中可以包含其他类声明的成员,这种概念就称为嵌套类(Nested Class)。
嵌套类两种:
- 静态嵌套类(Static Nested Class)
- 非晶态嵌套类(Non-Static Nested Class)
内部类与静态嵌套类
内部类(Inner Class),是指将某类直接定义成另一个类的非静态内部成员。
class OutsideClass { //声明主类
...类成员语句;
class InsideClass { //声明主类的静态嵌套类
...类成员语句;
}
}
由于内部类与其他的实例类成员一样都附属于主类对象,因此可以直接存取对象的实列变量(Instance Variable)与实例方法(Instance Method),并且在内部类之中不得包含任何静态成员(Static Member)。
如果在主类的静态方法(例如main()主执行区块)中创建内部类的对象,就必须使用完整的类路径语句来指定该对象的引用位置,并在外部类中声明内部类的相关方法,以返回内部类创建对象的引用值。
当内部类被声明为主类的静态成员时,我们称为这个静态成员为静态嵌套类。静态嵌套类不同于内部类,并不是依附于主类创建的对象,而是直接附属于主类。因此,静态嵌套类无法直接存取实例变量或调用任何实例方法,只能通过对象来间接存取或调用类的实例成员。由于静态嵌套类属于主类的静态成员,因此可以不通过主类的成员方法来返回对象实现的引用值,既可以直接在主类的静态方法(例如main()主执行区块)中进行内部类对象的创建工作。
匿名类的介绍
直接在类的静态方法中创建静态嵌套类的对象:
public static void main(String args[]) {
//创建静态嵌套类的对象
InnerClass myInner = new InnerClass();
}
或者调用外部类方法返回内部类对象的引用值,以作为内部类对象的创建依据。
public static void main(String args[]) {
//使用外部类方法的返回值来创建内部类对象
OuterClass.InnerClass myInner = OutClass.ImplementInnerClass;
}
如果我们所声明的内部类或嵌套静态类必须继承某个类或实现某个接口,就可以在不定义类名称的情况下,直接通过基类或接口的构造函数引用值来创建该类的对象:
public static void main(String args[]) { //主程序区块
Object myObject = new Object() { //继承Object类的派生类
public String toString() { //覆盖(重新定义)toString方法
return "这是一个内部的匿名类!";
}
}
}
这种不具有类名,但可以直接调用基类或接口的构造函数来创建派生类对象的类,称为匿名类(Anonymous Class)。
匿名类的语法:
基类(接口)名称 派生匿名类对象 = new 基类(接口) 构造函数() {
//声明匿名类成员的程序语句
}
由于匿名类可以继承其他基类或实现的指定接口,因此使用匿名类可以比实现接口的方式更容意且更有效率的实现类的多重继承关系。当使用匿名内部类时,虽然在程序代码中看不出任何的声明语句,但是它的创建必须有所依据,因此匿名内部类一般用于实习一个接口或继承一个特定的类。