一、类的继承
继承在面向对 象开发思想中是一个非常重要的概念,它使整个程序架构具有-定的弹性,在程
序中复用已经定义完善的类不仅可以减少软件开发周期,还可以提高软件的可维护性和可扩展性。资源包本节将详细讲解类的继承。在第6章中曾简要介绍过继承,其基本思想是基于某个父类的扩展,制定出一个新的子类,子类可以继承承父类原有的属性和方法,也可以增加原来父类所不具备的属性和方法,或者直接重写父空中的某些方法。例如,平行四边形是特殊的四边形,可以说平行四边形类继承了四边形类,这时江边形类将所有四边形具有的属性和方法都保留下来,并基于四边形类展了一些新的平行四边形类特有的属性和方法。
代码
结果
代码
结果
Java语言每个类只能继承一个父类
extends 继承
父类 写法与普通类写法相同
public class 父类类名(){
}
子类
public class 子类类名 extends 父类类名(){}
所有类的构造方法第一行都有一个隐藏的“super();”
作用是在执行该构造方法之前调用其父类构造方法
子类重写父类的方法
返回参数相同 方法名相同 传入参数相同 只有方法体不同
二、Object类
在开始学习使用class关键字定义类时,就应用了继承原理,因为在Java中,所有的类都直接或间接继承了java.lang.Object 类。Object 类是比较特殊的类,它是所有类的父类,是Java类层中的最高层类。当创建一一个类时,总是在继承,除非某个类已经指定要从其他类继承,否则它就是从java.lang.Object类继承而来的,可见Java中的每个类都源于java.lang.Object 类,如String、Integer 等类都是继承于Object类;除此之外自定义的类也都继承于Object类。由于所有类都是Object子类,所以在定义类时,省略了extends Object关键字,如图7.10所示便描述了这一原则。
在Object类中主要包括clone(、finalize(、equalsO、toString0等方法,其中常用的两个方法为equals()和 toString0方法。由于所有的类都是Object 类的子类,所以任何类都可以重写Object类中的方法。
注意:Object类中的getClass()、notify()、notifyAll()、wait()等方法不能被重写,因为这些方法被定义为final类型
Object的重要方法:
1. getClass( )方法
getClass0方法是Object类定义的方法,它会返回对象执行时的Class实例,然后使用此实例调用getName0方法可以取得类的名称。
getClass( ) . getName( );
可以将getClass0方法与toString0方法联合使用。
2. toString( )方法
toString0方法的功能是将一一个对象返回为字符串形式, 它会返回一 个String 实例。在实际的应用中通常重写toString( )方法,为对象提供一个特定的输出模式。当这个类转换为字符串或与字符串连接时,将自动调用重写的toString0方法。的重要方法。
代码
结果
3. equals()方法
前面章节曾讲解过equals()方法,当时是比较“==”运算符与equalsl()方法,说明“==”比较的是两个对象的引用是否相等,而equals0方法比较的是两个对象的实际内容。
代码
结果
三、对象类型的转换
对象类型的转换在java编程中经常遇到,主要包括向上转型与向下转型操作。
1、向上转型
对象类型的转换在Java编程中经常遇到,主要包括向上转型与向下转型操作。本节将首先介绍向上转型。
因为平行四边形是特殊的四边形,也就是说平行四边形是四边形的一种,那么就可以将平行四边形对象看作是一一个四边形对象。例如,鸡是家禽的一种, 而家禽是动物中的一类,那么也可以将鸡对象看作是一个动物对象。
代码
结果
平行四边形类继承了四边形类,四边形类存在一一个 draw0方法,它的参数是Quadranol(四边形类)类型,而在平行四边形类的主方法中调用draw0时给予的参数类型却是Prllelogram (平行四边形类)类型的。这里一直在强调- 一个问题, 就是平行四边形也是种类型的四边形, 所以可以物平行四边形类的对象看作是个四边形类的对象, 这就相当于“Quadrangleobj = new Parallelogram0;”,就是把子类对象赋值给父类类型的变量,这种技术被称为“向上转型”。试想一.下正方形类对象可以作为draw0方法的参数,梯形类对象同样也可以作为draw0方法的参数,如果在四边形类的draw0方法中根据不同的图形对象设置不同的处理,就可以做到在父类中定义一个方法完成各个子类的功能,这样可以使同一份代码毫无差别地运用到不同类型之上,这就是多态机制的基本思想。平行四边形类继承了四边形类,平行四边形类 与四边承图都是将顶级类设置在页面的顶部,然后逐渐向下,所以将子类对形类的关系对象看作是父类对象被称为“向上转型”。由于向上转型是从一个较具体的类到较抽象的类的转换,所以它总是安全的,如可以说平行四边形是特殊的四边形,但不能说四边形是平行四边形。
2、向下转型
通过向上转型可以推理出向下转型是将较抽象类转换为较具体的类。这样的转型通常会出现问题,例如,不能说四边形是平行四边形的一种、 所有的鸟都是鸽子,因为这非常不合乎逻辑。可以说子类对象总是父类的一个实例, 但父类对象不一定是子类的实例。
代码
结果
如果将父类对象直接赋予子类,会发生编译器错误,因为父类对象不一定是子类的实例。例如,一个四边形不一定就是指平行四边形,它也许是梯形,也许是正方形,也许是其他带有四条边的不规则图形。
越是具体的对象具有的特性越多,越抽象的对象具有的特性越少。在做向下转型操作时,将特性范围小的对象转换为特性范围大的对象肯定会出现问题,所以这时需要告知编译器这个四边形就是平行四边形。将父类对象强制转换为某个子类对象,这种方式称为显式类型转换。
重载方法名相同 参数不同
向上转型子类对象赋值给父类引用Animal a = new Dog();自动类型转换
向下转型父类对象赋值给子类引用(Dog)new Animal();Dog a =强制类型转换
四、使用instanceof关键字判断对象类型
当在程序中执行向下转型操作时,如果父类对象不是子类对象的实例,就会发生Class,CastException异常,所以在执行向下转型之前需要养成一个 良好的习惯,就是判断父类对象是否子类对象的实例。这个判断通常使用instanceof操作符来完成。可以使用instanceof操作符判断是口一个类实现了某个接口,也可以用它来判断一个实例对象是否属于个类。
instanceof的语法格式如下: myobject instanceof ExampleClass
myobject: 某类的对象引用。
ExampleClass: 某个类。
使用instanceof操作符的表达式返回值为布尔值。如果返回值为true, 说明myobject对象对ExampleClas的实例对象;如果返回值为false, 说明myobjet对象不是ExmpleClas的实例对象。
注意:instanceof是java语言的关键字,在java语言中的关键字都为小写。
代码
改正
在本实例中将instanceof 操作符与向下转型操作结合使用。在程序中定义了两个子类,即平行四边形类和正方形类,这两个类分别继承四边形类。在主方法中首先创建四边形类对象,然后使用instanceof操作符判断四边形类对象是否为平行四边形类的一 个实例,是否为正方形类的一 个实例,如果判断结果为true,将进行向下转型操作。
五、方法的重载
知道构造方法的名称由类名决定,所以构造方法只有一一个名称,但如果希望以不同的方式来实例化对象,就需要使用多个构造方法来完成。由于这些构造方法都需要根据类名进行命名,为了让方法名相同而形参不同的构造方法同时存在,必须用到“方法重载”。虽然方法重载起源于构造方法,但是它也可以应用到其他方法中。本节将讲述方法的重载。
东法的重获就是在网个类中允许网时存在个以 上的同名方法,只要这些方法的参数个数或类型不同即可。
代码
结果
注意:虽然在方法重载中可以使两个方法的返回类型不同,但只有返回类型不同并不足以区分两个方法的重载,还需要通过参数的个数以及参数的类型来设置。
六、final 关键字
final是java语言中的一个关键字,凡是被final关键字修饰过的内容都是不可改变的。
1.final变量
final关键字可用于变量声明,一旦该变量被设定,就不可以再改变该变量的值。通常,由final定义的变量为常量。例如,在类中定义PI值,可以使用如下语句:
final double PI=3.14;
当在程序中使用PI这个常量时,它的值就是3.14,如果在程序中再次对定义为final的常量赋值,编译器将不会接受。
final 关键字定义的变量必须在声明时对其进行赋值操作。final 除了可以修饰基本数据类型的常量,还可以修饰对象引用。由于数组也可以被看作一个对象来引用, 所以final可以修饰数组。一旦一个对象引用被修饰为final 后,它只能恒定指向一个对象, 无法将其改变以指向另一个对象。一 个既是satic又是final的字段只占据段不能改变的存储空间。
fina1修饰变量一不能被改变(常量)fina1修饰方法-不能被重写fina1修饰类-不能被继承
instanceof对象名 instanceof 类名
判断对象是否属于该类或其子类
代码
在本实例中,被定义为final的常量定义时需要使用大写字母命名,并且中间使用下划线进行连接,这是Iana中的编码规则。同时,定义为fnal的数据无论是常量、对象引用还是数组,在主的数中都不可以被改变。
我们知道一个被定义为final 的对象引用只能指向唯一一个对象, 不可以将它再指向其他对象,但是一个对象本身的值却是可以改变的,那么为了使一个常量真正做到不可更改,可以将常量声明为static final。
2.final方法
首先,应该了解定义为final的方法不能被重写。
将方法定义为final 类型可以防止子类修改该类的定义与实现方式,同时定义final 的方法的执行效率要高于非final方法。在修饰权限中曾经提到过private修饰符,如果一个父类的某个方法被设置为private修饰符,子类将无法访问该方法,自然无法覆盖该方法, 所以一个定义为private的方法隐式被指定为final类型,这样无需将一个定义为private的方法再定义为final类型。例如下面的语句:
private final void test() {
...//省略些程序代码
}
但是在父类中被定义为private final的方法似乎可以被子类覆盖
代码
3.final类
定义为final的类不能被继承。
如果希望一个类不允许任何类继承,并且不允许其他人对这个类进行任何改动,可以将这个类设置为final形式。 final类的语法如下:
final class 类名{ }
如果将某个类设置为fnal 形式,则类中的所有方法都被隐式地设置为final 形式,但是final类中的成员变量可以被定义为final 或非final形式。
七、多态
多态意为一个名字可具有多种语义,在程序设计语言中,多态性是指“一种定义,多种实现”例如,运算符“+”作用于两个整型量时是求和,而作用于两个字符型量时则是将其连接在一起。利用多态可以使程序具有良好的扩展性,并可以对所有类对象进行通用的处理。类的多态性可以从两方面体现:一是方法的重载,二是类的上下转型。
代码
结果
八、抽象类与接口
通常可以说四边形具有4条边,或者更具体点,平行四边形是具有对边平行且相等特性的特殊四边形,等腰三角形是其中两条边相等的三角形,这些描述都是合乎情理的,但对于图形对象却不能使用具体的语言进行描述,它有几条边,究竟是什么图形,没有人能说清楚,这种类在Java中被定义为抽象类。
1、抽象类
在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承与多态处理。回想继承和多态原理,继承树中越是在上方的类越抽象,如鸽子类继承鸟类、鸟类继承动物类等。在多态机制中,并不需要将父类初始化对象,我们需要的只是子类对象,所以在Java语言中设置抽象类不可以实例化对象,因为图形类不能抽象出任何一种具体图形,但它的子类却可以。
Java 中定义抽象类时,需要使用 abstract 关键字,其语法如下:
[权限修饰符] abstract class 类名{
类体 }
使用absrnat关键字定义的类称为抽象类,而使用abtect关键字定义的方法称为抽象方法,抽象方法的定义语法如下:
从上面的语法可以看出,抽象方法是直接以分号结尾的,它没有方法体,抽象方法本身没有任何意义,除非它被重写, 而承载这 个抽象方法的抽象类必须被继承, 实际上,抽象类除了被继承之外没有任何意义。
继承抽象类的所有子类都需要将抽象类中的抽象方法进行覆盖,这样在多态机制中,就可以将父类修改为抽象类,将draw(方法设置为抽象方法,然后每个子类都重写这个方法来处理。
注意:构造方法不能定义为抽象方法。
结果
综上所述,使用抽象类和抽象方法时,需要遵循以下原则:
(1)在抽象类中,可以包含抽象方法,也可以不包含抽象方法,但是包含了抽象方法的类必须被定义为抽象类。
(2)抽象类不能直接实例化,即使抽象类中没有声明抽象方法,也不能实例化。
(3)抽象类被继承后,子类需要实现其中所有的抽象方法。
(4)如果继承抽象类的子类也被声明为抽象类,则可以不用实现父类中所有的抽象方法。
使用抽象类时,可能会出现这样的问题:程序中会有太多冗余的代码,同时这样的父类局限性很大,例如,上面的例子中,也许某个不需要shop()方法的子类也必须重写shop()方法。如果将这个shop()方法从父类中拿出,放在别的类里,又会出现新问题,就是某些类想要实现“买衣服”的场景,竟然需要继承两个父类。Java 中规定,类不能同时继承多个父类,面临这种问题时,接口的概念便出现了。
2、接口
使用抽象类时,可能会出现这样的问题:程序中会有太多冗余的代码,同时这样的父类局限性很大,例如,上面的例子中,也许某个不需要shop()方法的子类也必须重写shop()方法。如果将这个shop()方法从父类中拿出,放在别的类里,又会出现新问题,就是某些类想要实现“买衣服”的场景,竟然需要继承两个父类。Java 中规定,类不能同时继承多个父类,面临这种问题时,接口的概念便出现了。
接口使用interface 关键字进行定义,其语法如下:
[修饰符] interface 接口名[extends 父接口名列表] {
[public] [static] [final] 常量;
[public] [abstract] 方法;
}
修饰符:可选,用于指定接口的访问权限,可选值为public。 如果省略则使用默认的访问权限。
接口名: 必选参数,用于指定接口的名称,接口名必须是合法的Java标识符。一般情况下,要求首字母大写。 extends 父接口名列表:可选参数,用于指定要定义的接口继承于哪个父接口。当使用extends关键字时,父接口名为必选参数。
方法:接口中的方法只有定义而没有被实现。
一个类实现一个接口可以使用implements 关键字,代码如下:
public class Parallelogram extends Quadrangle implements drawTest{
...//}
接口是抽象类的延伸,可以将它看作是纯粹的抽象类,接口中的所有方法都没有方法体。对于1小节中遗留的问题,可以将draw()方法封装到一个接口中,这样可以让一个类既能继承图形类,又能实现draw()方法接口,这就是接口存在的必要性 。
代码
结果
在本实例中,正方形类与平行四边形类分别实现了drawTest接口,所以需要覆盖接口中的方法。在调用draw()方法时,首先将平行四边形类对象与正方形类对象向上转型为drawTest接口形式。这里也许很多读者会有疑问,接口是否可以向上转型?其实在Java 中无论是将一个类向上转型为父类对象,还是向上转型为抽象父类对象,或者向上转型为该类实现接口,都是没有问题的。然后使用d[i]数组中的每一个对象调用draw(), 由于向上转型,所以d[i]数组中的每一个对象分别代表正方形类对象与平行四边形类对象,最后结果分别调用正方形类与平行四边形类中覆盖的draw()方法。