第5 章类的高级特性 5.1 例题解析 例5.1.1 详细解析Java中的抽象类。 【例题解析】 在Java语言中,abstractclass和interface是支持抽象类定义的两种 机制。在面向对象的概念中,所有的对象都是通过类来描绘的,但反过来 却不是这样,并不是所有的类都是用来描绘对象的。如果一个类中没有包 含足够的信息来描绘一个具体的对象,这样的类就是抽象类。 抽象类往往用来表征在对问题领域进行分析、设计中得出的抽象概 念,是对一系列看上去不同但本质上相同的具体概念的抽象。例如,在进 行一个图形编辑软件的开发时,会发现问题领域存在着圆、三角形这样一 些具体概念,它们是不同的,但它们又都属于形状这样一个概念。形状这 个概念在问题领域中是不存在的,它是一个抽象概念。正是因为抽象的概 念在问题领域没有对应的具体概念,所以用于表征抽象概念的抽象类是不 能够实例化的。 例5.1.2 总结Java中abstractclass、interface、final和static的概念。 【例题解析】 在语法层面,Java语言对于abstractclass和interface给出了不同的 定义方式,下面以定义一个名为Demo的抽象类为例来说明这种不同。使 用abstractclass定义Demo抽象类的方式如下。 abstract class Demo{ abstract void method1(); abstract void method2(); .} 使用interface定义Demo抽象类的方式如下。 interface Demo{ void method1(); 44 JavaEE 零基础入门实验指导与习题解析 void method2(); .} 首先,在abstractclass方式中,Demo可以有自己的数据成员,也可以有非abstract 的成员方法;而在interface方式的实现中,Demo只能够有静态的不能被修改的数据成 员,也就是必须是staticfinal的。不过,在interface中一般不定义数据成员,所有的成员 方法都是abstract的。从某种意义上说,interface是一种特殊形式的abstractclass。 abstractclass在Java语言中表示的是一种继承关系,一个类只能使用一次继承关 系。但是,一个类却可以实现多个interface。这也是Java语言的设计者在考虑Java对于 多重继承的支持方面的一种折中处理。 其次,在abstractclass的定义中,我们可以赋予方法默认行为。但是在interface的 定义中,方法却不能拥有默认行为。 abstractclass和interface所反映出的设计理念不同,abstractclass表示的是is-a关 系,interface表示的是like-a关系。 实现抽象类和接口的类必须实现其中的所有方法。抽象类中可以有非抽象方法,接 口中则不能有实现方法。 关于抽象类abstractclass: (1)只要是有一个或一个以上抽象方法的类必须用abstract声明为抽象类。 (2)抽象类中可以有具体的实现方法。 (3)抽象类中可以没有抽象方法。 (4)抽象类中的抽象方法必须被它的子类实现,如果子类没有实现,则该子类继续为 抽象类。 (5)抽象类不能被实例化,但可以由抽象父类指向的子类实例来调用抽象父类中的 具体实现方法,通常作为一种默认行为。 (6)要使用抽象类中的方法,必须有一个子类继承于这个抽象类并实现抽象类中的 抽象方法,通过子类的实例去调用。 关于接口interface: (1)接口中可以有成员变量,且接口中的成员变量必须初始化。 (2)接口中的成员方法只能是方法原型,不能有方法主体。 (3)接口的成员变量和成员方法只能是public(或默认不写,效果一样,都表示 public)。 (4)实现接口的类必须全部实现接口中的方法。 关于关键字final: (1)final可用于修饰成员变量、非抽象类(不能与abstract同时出现)、非抽象的成员 方法以及方法参数。 (2)final方法不能被子类的方法重写,但可以被继承。 (3)final类表示该类不能被继承,没有子类;final类中的方法也无法被继承。 (4)final变量表示常量,只能赋值一次,赋值后不能被修改。final变量必须初始化。 上篇 例题解析与习题解答 45 (5)final不能用于修饰构造方法。 (6)对于final参数,只能使用该参数,不能修改该参数的值。 关于关键字static: (1)static可以修饰成员变量和成员方法,但不能修饰类以及构造方法。 (2)被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不 依赖类特定的实例,被类的所有实例共享。 (3)static变量和static方法一般通过类名直接访问,但也可以通过类的实例来访问 (不推荐这种访问方式)。 (4)static变量和static方法同样适用Java修饰符。用public修饰的static变量和 static方法在任何地方都可以通过类名直接访问,用private修饰的static变量和static方 法只能在声明的本类方法及静态块中访问,但不能用this访问,因为this属于非静态 变量。关 于static和final同时使用: (1)staticfinal用来修饰成员变量和成员方法,可简单理解为“全局常量”。 (2)对于变量,表示一旦赋值就不可修改,并且通过类名可以访问。 (3)对于方法,表示不可覆盖,并且可以通过类名直接访问。 例5.1.3 分析下列程序片段,指出程序中的错误。 (1)abstract class Name { private String name; public abstract boolean isStupidName(String name) {} } 【例题解析】 abstractmethod必须以分号结尾,且不带花括号。 (2)public class Something { void doSomething () { private String s=""; int l=s.length(); } } 【例题解析】 局部变量前不能放置任何修饰符(private、public和protected)。final可以用来修饰 局部变量,但它是非修饰符。 (3) abstract class Something { private abstract String doSomething (); } 【例题解析】 abstract方法不能以private修饰。abstract方法是让子类实现具体细节的,不可以 用private把abstract方法封锁起来。同理,abstract方法前也不能加final。 46 JavaEE 零基础入门实验指导与习题解析 (4)public class Something { public int addOne(final int x) { return ++x; } } 【例题解析】 intx被修饰成final意味着x不能在addOne方法中被修改。 5.2 习题解答 1.接口与抽象类有哪些异同点? 参考答案: 在面向对象的概念中,我们知道所有的对象都是通过类来描绘的,但是反过来却不是 这样。并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘 一个具体的对象,这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分 析、设计时得出的抽象概念,是对一系列看上去不同、但是本质上相同的具体概念的抽象。 正是因为抽象的概念在问题领域没有对应的具体概念,所以用以表征抽象概念的抽象类 是不能够实例化的。 接口与抽象类的主要异同点如下。 (1)接口定义了一组特定功能的对外接口与规范,而并不真正实现这种功能,功能的 实现留给实现这一接口的各个类来完成。抽象类一般作为公共的父类为子类的扩展提供 基础,这里的扩展包括属性上的和行为上的。而接口一般来说不考虑属性,只考虑方法, 使得子类可以自由地填补或扩展接口所定义的方法。抽象类表示的是is-a关系,接口着 重表示的是can-do关系。 (2)抽象类在Java语言中表示的是一种继承关系,一个类只能使用一次继承。但 是,一个类却可以实现多个接口,接口可以解决多重继承问题。 (3)接口是抽象方法和常量值的定义的集合,从本质上讲,它是一种只包含常量与抽 象方法的特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实 现。接口里面不能有私有的方法或变量,接口中的所有常量必须是publicstaticfinal的, 且必须给其初值(接口的实现类中不能重新定义,也不能改变其值)。接口中的方法必须 是publicabstract的,这是系统默认的,在定义接口时写不写修饰符都是一样的。抽象类 中可以有私有方法或私有变量,抽象类中的变量默认是友元型,其值可以在子类中重新定 义,也可以重新赋值。 (4)实现抽象类和接口的类必须实现其中的所有方法。在抽象类中可以有自己的数 据成员,也可以有非abstract的成员方法。而在接口中,只能够有静态的不能被修改的数 据成员,所有的成员方法都是abstract的。实现接口时一定要实现接口中定义的所有方 法,而实现抽象类时可以有选择地重写需要用到的方法。在一般的应用中,顶级的是接 口,然后是抽象类实现接口,最后才到具体类实现。 上篇 例题解析与习题解答 47 2.区分接口与抽象类分别在什么场合使用。 参考答案: 如果预计要创建类的多个版本,则创建抽象类,因为抽象类提供简单的方法来控制类 版本。如果创建的功能将在大范围的异类对象间使用,则使用接口;如果要设计小而简练 的功能块,则使用接口;如果要设计大的功能单元,则使用抽象类。如果要向类的所有子 类提供通用的已实现功能,则使用抽象类,抽象类主要用于关系密切的对象;而接口适合 为不相关的类提供通用功能。接口多定义对象的行为;抽象类多定义对象的属性。 3.一个类如何实现接口? 实现某接口的类是否一定要重载该接口中的所有抽象 方法? 参考答案: 一个类使用关键字implements实现某接口。实现某接口的类如果不是抽象类,则需 要通过重载来实现该接口中的所有抽象方法;如果这个类是抽象类,则它可以不必实现该 接口中的所有抽象方法。 4.对于以下程序,运行javaStaticTest后得到的输出结果是什么? public class StaticTest { static { System.out.println("Hi there"); }p ublic void print() { System.out.println("Hello"); }p ublic static void main(String args []) { StaticTest st1=new StaticTest(); st1.print(); StaticTest st2=new StaticTest(); st2.print(); }} 参考答案: Hi there Hello Hello 5.编写程序,要求创建一个抽象类Father,其中有姓名、身高、体重等属性及爱好(唱 歌)等方法。创建子类Son类继承Father类,并且增加性格这个属性,改写父类的方法 (爱好)。 参考答案: public class test { public static void main(String args[]) { Son son=new Son("乖儿子",1.78f,61f, "篮球"); 48 JavaEE 零基础入门实验指导与习题解析 son.showInfo(); son.singsong(); } }a bstract class Father { float high,weight; protected String name; Father(String name,float high,float weight) { this.name=name; this.high=high; this.weight=weight; }a bstract void singsong(); abstract void showInfo(); }c lass Son extends Father { String specialty; Son(String name, float high,float weight,String specialty) { super(name,high,weight); this.specialty=specialty; }v oid singsong(){ System.out.println(name+"is singging loudly!"); }v oid showInfo() { System.out.println("姓名:"+ name+";身高:"+ high+";体重:"+ weight+";爱好:"+ specialty); } }