继承在面向对象开发思想中是一个非常重要的概念,它使整个程序架构具有一定的弹性。在程序中复用一些已经定义完善的类,不仅可以减少软件开发周期,也可以提高软件的可维护性和可扩展性。
在Java 语言中,一个类继承另一个类需要使用关键宇 extends,关键字extends 的使用方法如下:
class Child extends Parent {}
因为 Java 只支持单继承,即一个类只能有一个父类,所以类似下面的代码是错误的:
class Child extends Parent1, Parents2 {}
子类在继承父类之后,创建子类对象的同时也会调用父类的构造方法。
在Object 类中,主要包括 clone()、finalize()、equals()、toString()等方法,其中常用的两个方法为equals()和 toString()方法。由于所有的类都是 Object类的子类,所以任何类都可以重写 Object 类中的方法。
1、getClass()方法
getClass()方法是 Object 类定义的方法,它会返回对象执行时的 Class 实例,然后使用此实例调用setName()方法可以收得类的名称。语法如下:
getClass).getname();
可以将getClass()方法与toString()方法联合使用。
2、toString()方法
toString0方法的功能是将一个对象返回为字符串形式,它会返回一个 String 实例。在实际的应用中通常重写 toString()方法,为对象提供一个特定的输出模式。当这个类转换为字符串或与字符串连接时,将自动调用重写的toString()方法。
例题1
运行结果:
3、equals()方法
在Java 语言中,有两种比较对象的方式,分别为“=”运算符与equals()方法。两者的区别在于:“==” 比较的是两个对象引用内存地址是否相等,而 equalsO方法比较的是两个对象的实际内容。来看下面的实例。
对象类型的转换在Java编程中经常遇到,主要包括向上转型和向下转型操作。
7.3.1 向上转型
向上转型可以被理解为将子类类型的对象转换为父类类型的对象,即把子类类型的对象直接赋值给父类类型的对象,进而实现按照父类描述子类的效果。
因为人类(People)是教师类(Teacher)的父类,所以通过向上转型,能够把教师类(Teacher)类型的对象 (new Teacher();)直接赋值给人类(People)类型的变量(tom)。也就是说,进行问上转型,父类类型的对象可以引用子类类型的对象。而且,向上转型是安全的,因为向上转型是将一个较具体的类的对象转换为一个较抽象的类的对象。例如,可以说平行四边形是四边形,但个能说四边形是平行四边形。
7.3.2 向下转型
向下转型可以被理解为将父类类型的对象转换为子类类型的对象。但是,运用向下转型,如果把一个较抽象的类的对象转换为一个较具体的类的对象,这样的转型通常会出现错误。例如,可以说某只鸽子是一只鸟,却不能说某只鸟是一只鸽子。因为鸽子是具体的,鸟是抽象的。一只鸟除了可能是鸽子,还有可能是老鹰、麻雀等。因此,向下转型是不安全的。
当在程序中执行向下转型操作时,如果父类对象不是子类对象的实例,就会发生 ClassCastException昇常,所以在执行向下转型之前需要养成一个良好的习惯,就是判断父类对象是杏为子类对象的实例。这个判断通常使用 instanceof 关键宇来完成。可以使用 instanceof 关键字判断是否一个类实现了某个接口(接口会在第 7.8 节中进行讲解),也可以用它来判断一个实例对象是否属于一个类。
例题7.7
方法的重载就是在同一个类中允许存在一个以上的同名方法,只要这些方法的参数个数或类型不同即可。为了更好的诠释重载,来看下面例题。
例题7.8
例题7.9
7.6.1 final变量
final关键字可用于变量声明,一旦该变量被设定,就不可以再改变该变量的值。通常,由final定义的变量为常量。例如,在类中定义PI值,可以使用如下语句:
final double PI = 3.14;
当在程序中使用到 P1 这个常量时,它的值就是3.14。如果在程序中再次对定义为final 的常堇赋值,編译器将不会接受。
final 关健宇定义的安量必须在声明时对其进行赋值操作。final 除了可以修饰基本数据类型的常量,还可以修饰对象列用。由于数组也可以被看作一个对象来引用,所以final 可以修饰数组。一旦一个对象列用被修饰为 final 后,它就只能恒定指向一个对象,无法将其改变以指向另一个对象。一个既是 stati又是fimal 的宇段只占据一段不能改变的存储空间。为了深入了解 final 关键字,来看下面的实例。
本实例在运行之前,Eclipse就会报出例题7.7所示的编译错误。常量PI不允许被修改。
7.6.2 final方法
将方法定义为 final 类型,可以防止子类修改父类的定义与实现方式,同时定义为final的方法的执行效率要高于非final方法。在修饰权限中曾经提到过 private 修饰符,如果一个父类的某个方法被设置为 private,子类将无法访向该方法,自然无法復盖该方法。也就是说,一个定义为private 的方法隐式破指定为 final 类型,因此无须将一个定义为private 的方法再定义为 fimal 类型。例如下面的语句:
private final void test(){
... //省略一些程序代码
}
例题7.11
本实例在运行之前,Eclipse 就会报出的编译错误。因为打开电视这个方法是由 final修饰的,子类无法重写。所以 Baby 想要打开电视,就只能找爸爸来打开了。
7.6.3 final类
定义为final的类不能被继承。如果希望一个类不被任何类继承,并且不允许其他人对这个类进行任何改动,可以将这个类设置为final类。
final 类名{}
如果将某个类设置为final类,则该类中的所有方法都被隐式设置为final方法,但是final类中的成员变量可以被定义为final或非final形式。
利用多态可以使程序具有良好的扩 展性,并可以对所有类对象进行通用的处理。在7.3 节甲己经学之过于类对象可以作为父类的对象实例使用,这种将子类对象视为父类对象的做法称为“向上转型”。
假如现在要编写一个绘制图形的方法draw0,如果传入正方形对象就绘制正方形,如果传人园形
对象就绘制圆形,这种场景可以使用重载来实现,定义如下:
public void draw(Square s){ //绘制正方形的方法
}
public void draw(Circular c){ //绘制圆形的方法
}
但是这种写法有个问题:正方形和圆形都是图形,这场景细分的重载方式不仅增加了代码量,还降低了 “易用度”。如果定义一个图形类,让它处理所有继承该类的对象,根据“向上转型”原则可以使每个继承图形类的对象作为 draw0方法的参数,然后在 draw0方法中做一些限定就可以根据不同图形类对象绘制相应的图形。这样处理能够很好地解决代码九余问题,同时程序也易于维护。
通常可以说四边形具有 4 条边,或者更具体一点,平行四边形是具有对边平行且相等特性的特殊四边形,等腰三角形是其中两条边相等的三角形,这些描述都是合乎情理的。但仅对于 “图形”这个词却不能使用具体的语言进行描述,它有几条边?它有几个角?它有多大?没有人能说清楚。同理,仅用来描述特征且极具抽象性类,在Java 中被定义为抽象类。
7.8.1 抽象类
在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承与多态处理。回想继承和至态原理,继承树中越是在上方的类越抽象,如鸽子类继承鸟类、鸟类继承动物类等。在多态机制中,并不需要将父类初始化为对象,我们需要的只是了类对象,所以在Java 语言中设置抽象类不可以实例化为对象。
使用 abstract 关键宇定 义的类称为抽象类,而使用这个头键宇定义的方法称为抽象方法。抽象方法没有方法体,这个方法本身没有任何意义,除非它被重写,而季戰区个抽象方法的抽象类必须被继來,买际上抽象类除了破继承设有任何意义。定义抽象类的语法如下:
public abstract class Parent{
abstract void testAbstract(); //定义抽象方法
}
反过来讲,如果声明一个抽象方法,就必须将承载这个抽象方法的类定义为抽象类,不能在非抽象类中获取抽象方法。换句话说,只要类中有一个抽象方法,此类就被标记为抽象类。
课堂笔记:
抽象方法
修饰符 abstract 返回参数 方法名(传入人数);
抽象类 有抽象方法的类一定是抽象类
修饰符 abstract class 类名{
}
7.8.2 接口
接口是抽象类的延伸,可以将它看作是纯粹的抽象类,按口中的所有方法都没有方法体。对于 7.8.1 节中遗留的问题,可以将 draw0方法封装到一个接口中,使需要 drawO方法的类实现这个接口,同时也继承图形类,这就是接口存在的必要性。
接口使用 interface 关键字进行定义,其语法如下:
public interface Paintable {
void draw(); //定义接口方法可省略 public abstract关键字
}