Java第七章课堂总结

继承和多态是面向对象开发中非常重要的一组概念。继承和多态使用得当,整个程序的架构将变得非常有弹性,同时可以减少代码的冗余性。继承机制下,用户可以复用一些定义好的类,减少重复代码的编写。多态机制下,用户可以动态调整对象的调用,降低对象之间的依存关系。为了优化继承与多态,一些类除了可继承父类,还需要使用接口的形式。Java 中的类可以同时实现多个接口,接口被用来建立类与类之间关联的标准。正因为具有这些灵活、高效的机制,Java 语言才更具有生命力。
本章的知识架构及重难点如下。      

一 , 类的继承
继承在面向对象开发思想中是一个非常重要的概念,它使整个程序架构具有一定的弹性。在程中复用一些已经定义完善的类,不仅可以减少软件开发周期,也可以提高软件的可维护性和可扩展性本节将详细讲解类的继承。
在Java 语言中,一个类继承另一个类需要使用关键字 extends,关键字extends 的使用方法如下

class Child extends Parent {}


因为Java 只支持单继承,即一个类只能有一个父类,所以类似下面的代码是错误的!class Child

extends Parent1, Parents2 {}

子类在继承父类之后,创建子类对象的同时也会调用父类的构造方法。
[例7.1]创建子类对象,观察构造方法执行顺序(实例位置:资源包TMSI71)
父类 Parent 和子类 Child 都各自有一个无参的构造方法,在 main0方法中创建子类对象时,Java虚拟机会先执行父类的构造方法,然后再执行子类的构造方法。

子类继承父类之后可以调用父类创建好的属性和方法。

[例7.2]在电话类基础上衍生出手机类(实例位置:资源包ITMSI72)Telephone 电话类作为父类衍出 Mobile 手机类,手机类可以直接使用电话类的按键属性和拨打电话行为。

子类 Mobile 类仅创建了一个显示屏属性,剩余的其他属性和方法都是从父类 Telephone 类中继承的。
Java 虽然不允许同时继承两个父类,但不代表没有多继承的关系,可以通过类似“祖父>父>儿子孙子”的方式实现多代继承。
例如,绝大多数动物有眼睛、鼻子和嘴,犬类继承动物类,所以犬类也有眼睛、鼻子和嘴,哈士奇是犬类的一个品种,犬类有的特性哈士奇类都有。但哈士奇的眼睛、鼻子和嘴并不是从犬类继承的,而是从动物类继承的。

二,Object类

在开始学习使用 class 关键字定义类时,就应用到了继承原理,因为在 Java 中所有的类都直接或间接继承了java.lang.Object 类。Object 类是比较特殊的类,它是所有类的父类,是 Java 类层中的最高层类。用户创建一个类时,除非已经指定要从其他类继承,否则它就是从java.lang.Object 类继承而来的Java中的每个类都源于java.lang.Object 类,如 String类、Integer类等都是继承于 Object 类。除此之外,自定义的类也都继承于 Object 类。由于所有类都是Object 类的子类,所以在定义类时可省略 extends Object。

在 Obiect 类中,主要包括 clone0、finalize0、equals0、toString0等方法,其中常用的两个方法为equals0和 toString0方法。由于所有的类都是 Object类的子类,所以任何类都可以重写 Object 类中的方法

注意:Object 类中的 getClass0、notify0、notifyAll0、wait0等方法不能被重写,因为这些方法被定义为final类型

下面详细讲述 Object 类中的几个重要方法
1.getClass()方法
getClass0方法是 Object 类定义的方法,它会返回对象执行时的 Class 实例,然后使用此实例调用getName0方法可以取得类的名称。语法如下:

getClass().getname();

可以将 getClass0方法与 toString0方法联合使用

2.toString()方法

toString0方法的功能是将一个对象返回为字符串形式,它会返回一个 String 实例。在实际的应用中通常重写 toString0方法,为对象提供一个特定的输出模式。当这个类转换为字符串或与字符串连接时,将自动调用重写的 toString0方法。

3.equals()方法
在Java 语言中,有两种比较对象的方式,分别为“==”运算符与 equals0方法。两者的区别在于”=”比较的是两个对象引用内存地址是否相等,而 equals0方法比较的是两个对象的实际内容。来看面的实例。
[例7.4]根据身份证号判断是否为同一人(实例位置:资源包TMSI74)为People 类创建身份证号和姓名两个属性,重写 equals0方法,仅以身份证号作为区分条件。创建个People 对象,用equals0方法和“--”运算符来判断是否存在多个对象代表同一个人。

 从这个结果可以看出,“tom”和“汤姆”虽然名字不同,但是两者的身份证号相同,所以 equals(方法判断出了这两个对象实际上是同一个,而“=”运算符无法做出有效判断。如果两个对象的身份证号不同,或者两个对象类型都不同,equals0方法就会认为两者不是同一个人。

三,对象类型的转换

1,向上转型

向上转型可以被理解为将子类类型的对象转换为父类类型的对象,即把子类类型的对象直接赋值给父类类型的对象,进而实现按照父类描述子类的效果。

[例7.5]tom是谁?(实例位置:资源包TMSI75)
使用向上转型模拟如下场景:这里有一个人,他叫 tom,他是一名教师。

在上述代码中,“People tom = new Teacher0;”运用了向上转型的语法

向上转型结合实例的说明
综上所述,因为人类(People) 是教师类 (Teacher)的父类,所以通过向上转型,能够把教师类(Teacher)类型的对象 (new Teacher0:)直接赋值给人类 (People) 类型的变量(tom)。也就是说,进行向上转型,父类类型的对象可以引用子类类型的对象。而且,向上转型是安全的,因为向上转型是将一个较具体的类的对象转换为一个较抽象的类的对象。例如,可以说平行四边形是四边形,但不能说四边形是平行四边形。 

那么,使用向上转型的过程中,父类的对象是否可以调用子类独有的属性或者方法呢?下面以父类四边形类的对象调用子类平行四边形类独有的属性为例,阐述这一问题。

例如,四边形类是平行四边形类的父类,用 Quadrangle 表示四边形类、用 Parallelogram 表示平行四边形类;在 Parallelogram 类中,定义一个值为 4 的表示底边长度的变量 edges;

在 Parallelogram 类的主方法中,运用向上转型,把平行四边形类(Parallelogram)类型的对象直接赋值给四边形类(Quadrangle)类型的对象。人为强制四边形类(Quadrangle) 类型的对象可以调用变量 edges,并将 edges 的值修改为6,Eclipse 能通过编译么? Eclipse 的效果图如图 7.3 所示,从图中可以看出,Eclipse 在相关代码处显示波浪线等错误标志,说明代码有误。

 综上所述,在运用向上转型的过程中,父类的对象无法调用子类独有的属性或者方法
2,向下转型
向下转型可以被理解为将父类类型的对象转换为子类类型的对象。但是,运用向下转型,如果把一个较抽象的类的对象转换为一个较具体的类的对象,这样的转型通常会出现错误。例如,可以说某只鸽子是一只鸟,却不能说某只鸟是一只鸽子。因为鸽子是具体的,鸟是抽象的。一只鸟除了可能是鸽子,还有可能是老鹰、麻雀等。因此,向下转型是不安全的。[例7.6]谁是鸽子?(实例位置:资源包TMSI76)编写代码证明“可以说某只鸽子是一只鸟,却不能说某只鸟是一只鸽子”:鸟类是鸽子类的父类,用Bird 表示鸟类,用 Pigeon 表示鸽子类。

本例在运行之前,Eclipse 会报出如图所示的编译错误,这是因为父类对象不能直接赋值给子类对象。

如果想要告诉编译器“某只鸟就是一只鸽子”,应该如何修正?答案就是强制类型转换。也就是说,要想实现向下转型,需要借助强制类型转换。语法如下:

子类类型 子类对象 =(子类类型)父类对象;

因此,要想实现把鸟类对象转换为鸽子类对象(相当于告诉编译器“某只鸟就是一只鸽子”),需要将图7.4中第8行代码修改为:

Pigeon pigeon =(Pigeon) bird;//通过强制类型转换,告诉编译器“某只鸟就是一只鸽子”

注意:(1)两个没有继承关系的对象不可以进行向上转型或者向下转型。(2)父类对象可以强制转换为子类对象,但有一个前提: 这个父类对象要先引用这个子类对象

如果把上述实例中的代码:

Bird bird = new Pigeon();//某只鸽子是一只鸟

Pigeon pigeon =(Pigeon) bird;//通过强制类型转换,告诉编译器“某只鸟就是一只鸽子”

修改为如下代码:

Bird bird = new Bird();//某只鸽子是一只鸟

Pigeon pigeon = (Pigeon) bird;//通过强制类型转换,告诉编译器“某只鸟就是一只鸽子”

虽然 Eclipse 没有提示编译错误,但运行程序后,控制台将输出如下错误信息:

Exception in thread "main" javalang.ClassCastException: class Bird cannot be cast to class Pigeon

四,使用 instanceof 关键字判断对象类型 

在程序中执行向下转型操作时,如果父类对象不是子类对象的实例,就会发生 ClassCastExceptio异常,所以在执行向下转型之前需要养成一个良好的习惯,就是判断父类对象是否为子类对象的实例这个判断通常使用 instanceof关键字来完成。可以使用 instanceof关键字判断是否一个类实现了某个,也可以用它来判断一个实例对象是否属于一个类。

 本实例在运行之前,Eclipse 就会报出编译错误,因为四边形类与圆形类没有继承关系,因此两者不能使用 instanceof 关键字进行比较,否则会发生“不兼容”错误。

五,方法的重载

方法的重载就是在同一个类中允许存在一个以上的同名方法,只要这些方法的参数个数或类型不同即可。

 

虽然在方法重裁中可以使两个方法的返回类型不同,但只有返回类型不同并不足以区分两个方法的重载,还需要通过参数的个数以及参数的类型来设置。

六,

final关键字

1.关键字

    final 关键字可用于交量声明,一旦该变量被设定,就不可以再改变该变量的值。通常,由 fnal定义的变量为常量。例如,在类中定义 PI值,可以使用如下语句:

final double PI = 3 14;

当在程序中使用到 PI 这个常量时,它的值就是 3.14。如果在程序中再次对定义为 fnal 的常量赋值编译器将不会接受。

 

 final关键字定义的变量必须在声明时对其进行赋值操作。final 除了可以修饰基本数据类型的常量还可以修饰对象引用。由于数组也可以被看作一个对象来引用,所以 fnal 可以修饰数组。一旦一个对象引用被修饰为 final后,它就只能恒定指向一个对象,无法将其改变以指向另一个对象。一个既是 static又是 final 的字段只占据一段不能改变的存储空间。

2.final方法

将方法定义为 final 类型,可以防止子类修改父类的定义与实现方式,同时定义为final的方法的执行效率要高于非final方法。在修饰权限中曾经提到过 private 修饰符,如果一个父类的某个方法被设置为 private,子类将无法访问该方法,自然无法覆盖该方法。也就是说,一个定义为 private 的方法隐式被指定为final类型因此无须将一个定义为 private 的方法再定义为 final类型。

private final void test(){

···//省略一些程序代码

}

3.final类

定义为final 的类不能被继承。如果希望一个类不被任何类继承,并且不允许其他人对这个类进行任何改动,可以将这个类设置为final类。final类的语法如下:

final 类名{}

如果将某个类设置为 final类,则该类中的所有方法都被隐式设置为 fnal方法,但是 fnal类中的成员变量可以被定义为final或非final形式。

七,多态

假如现在要编写一个绘制图形的方法 draw(),如果传入正方形对象就绘制正方形,如果传入圆形对象就绘制圆形,这种场景可以使用重载来实现,定义如下:

public void draw(Square s){ //绘制正方形的方法

}

public void draw(Circular c){ //绘制圆形的方法

}

但是这种写法有个问题:正方形和圆形都是图形,这场景细分的重载方式不仅增加了代码量,还降低了“易用度”。如果定义一个图形类,让它处理所有继承该类的对象,根据“向上转型”原则可以使每个继承图形类的对象作为 draw()方法的参数,然后在 draw()方法中做一些限定就可以根据不同图形类对象绘制相应的图形。这样处理能够很好地解决代码冗余问题,同时程序也易于维护。

八,抽象类与接口

 1,抽象类

在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承与多态处理。 在多态机制中,并不需要将父类初始化为对象,我们需要的只是子类对象,所以在Java语言中设置抽象类不可以实例化为对象。

要想了解抽象类先看一下抽象方法,抽象方法是一个特殊的方法,他只有声明没有具体的实现。抽象方法用abstract关键字修饰。有抽象方法的类就就是抽象类,抽象类也有abstract关键字修饰。

需要注意的是,

1)抽象类的存在就是为了继承,所以用private方法来修饰抽象方法。

2)包含抽象方法的类叫做抽象类,但并不是抽象类里只有抽象方法。

3)子类继承了抽象类必须实现父类的抽象方法,如果没有实现,那么子类也将会是抽象类。

4)抽象类不能用来创建对象。

2,接口

接口用关键字interface来实现,接口指的是调用别人的方法或者函数。接口可以看出java是一种对行为的抽象。

接口需要注意:

1)接口中可以有变量和方法,并且接口中的变量会被隐式的被public static final来修饰(并且只能用public static final来修饰),方法会隐式的被public abstract来修饰,并且只能用来public abstract来修饰。也就是说接口中所有的方法不能有实现方法,也就是说接口中的方法都是抽象方法。

2)如果要写这个接口的实现方法,需要定义一个实现类,并且通过implements来实现接口中的方法。

3,接口和抽象类的区别:

1)抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。

2)设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。

3)抽象类是一个类,而接口不是类。

例题:

将图形对象的绘图方法剥离出来,作为 Paintable可绘制接口中的抽象方法。创建四边形类作为平行四边形类和正方形类的父类,同时让这两个子类实现 Paintable接口。创建圆形类实现Paintable接口,但不继承四边形类。

 运行结果如下:

从这个结果可以看出,“绘制”这个行为可不是四边形独有的,圆形也能绘制,所以draw0方法被独立封装在了可绘制接口中。

正方形类与平行四边形类分别继承了四边形类并实现了可绘制接口,所以正方形类与平行四边形类既可以调用绘制方法,又可以调用四边形提供的 dloAnything0)方法。但是,圆形不属于四边形,且可以绘制,所以最后圆形对象只调用了 draw()方法。

Java 中不允许出现多重继承,但使用接口就可以实现多重继承。一个类可以同时实现多个接口,因此可以将所有需要继承的接口放置在implement s关键字后并使用逗号隔开。实现多个接口的语法如下:

class 类名 implements 接口 1,接口 2,…,接口n

但这可能会在一个类中产生庞大的代码量,因为继承一个接口时需要实现接口中所有的方法。一个接口可以继承另一个接口,其语法如下:

interface intf1 { }
interface intf2 extends intf1 {} //接口继承接口

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值