第七章继承、多态、抽象类与接口

继承和多态是面向对象开发中非常重要的一组概念念。继承和多态使用得当,整个程序的架构将变得非常有弹性,同时可以减少代码的冗余性。继承机制别下,用户可以复用一些定义好的类,减少重复代码的编写。多态机制下,用户可以动态调整对象的话调用,降低对象之间的依存关系。为了优化继承与多态,一些类除了可继承父类,还需要使用接口的形形式。Java 中的类可以同时实现多个接口,接口被用来建立类与类之间关联的标准。

一、类的继承
继承在面向对象开发思想中是一个非常重要的概念,它使整个程序架构具有一定的弹性。在程序中复用一些已经定义完善的类,不仅可以减少软件开发发周期,也可以提高软件的可维护性和可扩展性。

在Java语言中,一个类继承另一个类需要使用关键字 extends,关键字extends的使用方法如下:

class Child extonds Parent { }

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

class Child extende Parent1,Paronts2{ }

子类在继承父类之后,创建子类对象的同时也会调用父类的构造方法。

【例7.1】创建子类对象,观察构造方法执行顺序 

父类 Parent 和子类Child都各自有一个无参的构造方法,在 main()方法中创建子类对象时,Java虚拟机会先执行父类的构造方法,然后再执行子类的松购造方法。

33e70de6df0747f081a5e5b8202560e5.png

c3deafb0f85c4d42b6caba3c364a4138.png 

 7fc9eee090f348aab787ff23226ed097.png

 

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

【例7.2】在电话类基础上衍生出手机类

Telephone电话类作为父类衍出Mobile手机类,手机类可以直接使用电话类的按键属性和拨打电话行为。

ada851d3ab29416894ecb9e75dc768dd.png

89370875791f40578ee077f17abd2c73.png 

 

 

 子类Mobile 类仅创建了一个显示屏属性生,剩余的其他属性和方法都是从父类Telephone 类中继承的。

Java虽然不允许同时继承两个父类,但不不代表没有多继承的关系,可以通过类似“祖父>父>儿子>孙子”的方式实现多代继承。

例如,绝大多数动物有眼睛、鼻子和嘴, 犬类继承动物类,所以犬类也有眼睛、鼻子和嘴,哈士 奇是犬类的一个品种,犬类有的特性哈士奇头本都有。但哈士奇的眼睛、鼻子和嘴并不是从犬类继承的,而是从动物类继承的。

二、Object类
object是对象数据类型。

Object类是所有Java类的祖先。每个类都使用Object作为超类。所有对象(包括数组)都实现这个类的方法。

在不明确给出超类的情况下,Java会自动把Object作为要定义类的超类,可以使用类型为Object的变量指向任意类型的对象。

Object类有一个默认构造方法pubilc Object(),在构造子类实例时,都会先调用这个默认构造方法。Object类的变量只能用作各种值的通用持有者。要对他们进行任何专门的操作,都需要知道它们的原始类型并进行类型转换。

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

getClass().getname();

可以将getClass()方法与toString0)方法联合使用。

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

【例7.3】让学生自我介绍

创建 Student类,重写toString()方法,使该类的对象可以自定义输出自己的姓名和年龄。

c1ec96eb53ea42b18ff057131673c65c.png

 

 3.equals()方法
在Java语言中,有两种比较对象的方式,分别为“==”运算符与equals0方法。两者的区别在于:“”比较的是两个对象引用内存地址是否相等,而equals()方法比较的是两个对象的实际内容。

【例7.4】根据身份证号判断是否为同一人

为People类创建身份证号和姓名两个属性, 重写equals()方法,仅以身份证号作为区分条件。创建 n个People对象,用equals()方法和“一”运算行守来判断是否存在多个对象代表同一个人。

619689fdee8f4f5693afab166321acd7.png

 

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

 

三、对象类型的转换
对象类型的转换在Java编程中经常遇到,主要包括向 上转型与向下转型操作。

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

【例7.5】tom是谁?

使用向上转型模拟如下场景:这里有一个人,他叫ton n,他是一名教师。

1531d07c4f9140b1952747a6b0cfbc61.png

 

 当我们用父类(包括直接父类和间接父类)凭证去指向子类的对象实例时,就叫做向上转型。

 

(2)向下转型

向下转型可以被理解为将父类类型的对象转换为子类类型的对象。但是,运用向下转型,如果把一个较抽象的类的对象转换为一个较具体的类的对象,这样的转型通常会出现错误。例如,可以说某只鸽子是一只鸟,却不能说某只鸟是一只鸽子。因为鸽子是具体的,鸟是抽象的。一只鸟除了可能是鸽子,还有可能是老鹰、麻雀等。因此,向下转型是不不安全的。

 

【例7.6】谁是鸽子?

 

编写代码证明“可以说某只鸽子是一只鸟,却不能能说某只鸟是一只鸽子”:鸟类是鸽子类的父类,用Bird表示鸟类,用Pigeon表示鸽子类。

8a61d4bd6b7644cfb942ffbbd5b71378.png

 

 

 

 注意:

①两个没有继承关系的对象不可以进行向上转型或者向下转型。

 

②父类对象可以强制转换为子类对象,但有一个前提 这个父类对象要先引用这个子类对象。

 

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

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

 

instanceof的语法格式如下:

myobject instanceof ExampleClass

myobject:某类的对象引用。

ExampleClass:某个类。

使用instanceof关键字的表达式返回值为布尔值。如果返回值为 true,说明 myobject 对象为 ExampleClass 的实例对象;如果返回值为 alse,说明myobject 对象不是ExampleClass的实例对象。

【例7.7】分析几何图形之间的继承关系

创建 Quadrangle 四边形类、Square 正方形类和Circular 圆形类。其中,Square 类继承 Quadrangle类,在主方法中分别创建这些类的对象,然后使用 instanceof关键字判断它们的类型并输出结果。

5d33bf7812424183bf467bc2db362ee4.jpg

 

五、方法的重载

如果希望以不同的方式来实例化对象,就需要使用多个构造方法来完成。由于这些构造方法都需要根据类名进行命名,为了让方法名相同而形参不同的构造方法同时存在,必须用到方法重载。虽然方法重载起源于构造方法,但它也可以应用到其他方法中。

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

【例7.8】编写不同形式的加法运算方法

创建 OverLoadTest类,在该类中编写add()7方法的多个重载形式,然后在主方法中分别输出这些方法的返回值。

d58af9362f1249388a3b4d26574a6d3f.png

 

 代码中分别定义了5个方法,在这5个方法中,前两个方法的参数类型不同,并且方法的返回值类型也不同,所以这两个方法构成重载关系; 前两个方法与第3个方法相比,第3个方法的参数 个数少于前两个方法,所以这3个方法也构成了重载关系;最后两个方法相比,发现除了参数的出现顺序不同,其他都相同,同样构成了重载关系。

【例7.9】使用不定长参数重载加法运算方法

创建OverLoadTest2类,在该类中编写add)方江去的多种重载形式,并编写该方法的不定长参数形式。然后在主方法中调用这些重载方法,并输出返回值。

124bc72f893f4bcb8aa856dd91f3cb7b.png

 六、final关键字

final被译为“最后的”“最终的”,final是Java语言中的一个关键字,凡是被final关键字修饰过的内容都是不可改变的。

(1)final变量

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

final double PI = 3.14;

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

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

【例7.10】定义不允许被修改的常量π

创建FinalData 类,在该类中定义表示圆周率的常量 PI,并尝试修改 PI的值。

de499b7b1b0d407eaba04000a3e4cf18.jpg

 

此代码在运行之前,Eclipse就会报错,常量PI不允许被修改。

 

(2)fibal方法

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

private final void test(){

…//省略一些程序代码

}

【例7.11】使用final关键字为电视机上儿童锁

创建Dad爸爸类,给Dad类定义一个打开电视机的方法,该方法使用 final 关键字修饰。创建 Dad类的子类Baby类,让Baby类尝试自己重写打开电视的方法。

4e2b74e97cd84c86a88128b4c5e665b8.jpg

 

 代码在运行之前,Eclipse会报错,因为打开电视这个方法是由final修饰的,子类无法打开,所以baby想要打开电视,就只能找爸爸来打开了。

(3)final类

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

final 类名{ }

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

七、多态

利用多态可以使程序具有良好的扩展性,并可以对所有类对象进行通用的处理。在73已经习过子类对象可以作为父类的对象实例使用,这种将子类对象视为父类对象的做法称为“向上转型”。

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

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

}

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

}

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

【例7.12】万能的绘图方法

创建Shape 图形类,作为Square正方形类和Cir ccular 圆形类的父类。创建 Demo6 类,并在该类中创建一个绘图用的draw0方法,参数为Shape类型, 任何Shape类的子类对象都可以作为方法的参数, 并且方法会根据参数的类型绘制相应的图形。

51e44ad79c0841628cba4cbd2b38f20f.png

 

 从代码的运行结果中可以看出,以不同类对象为参数调用draw()方法,可以处理不同的图形绘制问题。使用多态节省了开发和维护时间,因为程序员无须在所有的子类中定义执行相同功能的方法,避免了大量重复代码的编写。同时,只要实例化一个继承父类的子类对象,即可调用相应的方法,如果需求发生了变更,只需要维护一个draw()方法即可。

八、抽象类与接口

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

需要注意的是,

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

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

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

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

再来看一下接口:

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

接口需要注意的是:

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

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

重点来了,看完上面就可以看出接口和抽象类的区别了。

主要区别:

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

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

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

【例7.13】将绘图方法设为接口方法

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

b486d5d0d3fe495680709c7fd827b142.png

 5f25674f6e1248b48302c9b837ffd350.png

 

 

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

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

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

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

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

interface intf1 { }

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 面向对象编程的特点: - 封装:将数据和方法组合成一个,并对外提供接口,隐藏内部实现细节,保证数据安全性和代码的可维护性。 - 继承:通过子继承的属性和方法,实现代码的复用和扩展。 - 多态:同一种行为在不同情境下的不同表现形式,可以通过重载、重写和接口实现。 2. 和对象: - 是一种抽象的概念,是描述对象具有的属性和方法的模板。 - 对象是的一个实例,具有这个所描述的属性和方法。 3. 成员变量和成员方法: - 成员变量是描述对象状态的数据,可以是基本型、对象型或数组型。 - 成员方法是描述对象行为的操作,可以是构造方法、普通方法、静态方法和抽象方法。 4. 构造方法和析构方法: - 构造方法是创建对象时调用的特殊方法,用于初始化对象的成员变量。 - 析构方法是销毁对象时调用的特殊方法,用于释放对象占用的资源。Java中不需要显式地调用析构方法,由垃圾回收器自动回收。 5. 访问控制: - 访问控制可以限制的成员变量和成员方法的访问范围,保证数据的安全性和代码的可维护性。 - Java中有四种访问控制修饰符:public、protected、default、private。 6. 静态变量和静态方法: - 静态变量属于,不属于对象,被所有对象共享。 - 静态方法可以通过名调用,不需要创建对象。 7. final关键字: - final可以修饰、成员变量和成员方法。 - final修饰的不能被继承,修饰的变量是常量,修饰的方法不能被重写。 8. 抽象类接口: - 抽象类是一种不能被实例化的,只能作为父继承,可以包含抽象方法和普通方法。 - 接口是一种完全抽象的型,只包含抽象方法和常量,用于定义规范和约束。可以实现多个接口

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值