第十六章 多态与final关键字
提纲
- 16.1 多态
- 16.1.1 什么是多态
- 16.1.2 满足多态的条件
- 16.1.3 多态的利弊
- 16.1.4 举例
- 16.2 final关键字
- 16.2.1 什么是final关键字
- 16.2.2 final变量
- 16.2.3 final方法
- 16.2.4 final类
- 16.2.5 final总结
- 16.3 作业
16.1 多态
- 16.1.1 什么是多态:所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
- 16.1.2 满足多态的条件(必须):只有满足了以下三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。
- 继承:在多态中必须存在有继承关系的子类和父类。
- 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
- 向上转型:在多态中需要父类的引用指向子类的对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。
- 16.1.3 多态的利弊
- 好处:提高了程序的扩展性。
- 弊端:当父类引用指向子类对象时,虽然提高了扩展性,但是只能访问父类中具备的方法,不可以访问子类中特有的方法。(前期不能使用后期产生的功能,即访问的局限性)
- 16.1.4 举例:汽车的例子
16.2 final关键字
- 16.2.1 什么是final关键字:final在Java中是一个保留的关键字,可以声明成员变量、方法、类以及局部变量。一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如果你试图将变量再次初始化的话,编译器会报编译错误。
- 16.2.2 final变量:
-
什么是final变量:凡是对成员变量或者本地变量(在方法中的或者代码块中的变量称为本地变量,
又叫局部变量)声明为final的都叫作final变量。
final变量经常和static关键字一起使用,作为常量。 -
举例1:
public class Person { public static final String NAME_1 = "张三"; public static final String NAME_2 = "Rose"; } public static void main(String[] args) { //代码错误,final变量是不可修改的 Person.NAME_1 = "李四"; }
**结论:1.final变量是只读的。2.final变量命名为全大写。
3.单词之间用下划线连接_。这是Java中的规则。 -
举例2:定义的时候不赋值,一定不能加static关键字
public class Person { public static final String NAME = "张三"; public final String SEX; //当有多个构造方法时,每个构造方法必须初始化一次 public Person() { SEX = "女"; } public Person() { //空白的final变量必须在构造方法中进行赋值,每个构造方法必须赋值且只能赋值一次,后面不能再改变该值 SEX = "男"; } public int getSum(final int x){ //错误,不能改变final类型参数的值 //x++; return x+1; } public void doIt(){ final int a = 5;//局部final变量,必须赋值 } }
-
- 16.2.3 final方法
-
什么是final方法:final也可以声明方法。方法前面加上final关键字,代表这个方法不可以被子类的方法重写。
如果你认为一个方法的功能已经足够完整了,子类中不需要改变的话,你可以声明此方法为final。
final方法比非final方法要快,因为在编译的时候已经静态绑定了,不需要在运行时再动态绑定。 -
举例:
//父类 public class Parents { //final类似于private public final void doIt(){ System.out.println("父类.doIt()方法"); } private void doIt2(){ System.out.println("父类.doIt2()方法"); } public void doIt3(){ System.out.println("父类.doIt3()方法"); } } //子类 public class SonOne extends Parents{ @Override public void doIt3() { System.out.println("子类重写doIt3()方法"); } } //测试 public class Test { public static void main(String[] args) { SonOne so = new SonOne(); so.doIt();//final方法可以调用,但是不能被重写 //so.doIt2();私有方法不能重写,也不能调用 so.doIt3();//可以重写与调用 } } //执行结果 父类.doIt()方法 子类重写doIt3()方法
-
- 16.2.4 final类
-
什么是final类:使用final来修饰的类叫作final类。final类中的所有方法都被隐式的设置为final形式,但是成员变量可以定义为final类型和非final类型。final类通常功能是完整的,它们不能被继承。Java中有许多类是final的,譬如String, Interger以及其他包装类。
-
语法:final 类名{}
-
举例:
public final class Parents { int a = 3; } //这样继承是错误的 public class SonOne extends Parents{ } //创建book类 public final class Book{ int a = 3; public void doIt(){ System.out.println("Book类中的doIt()方法!"); } } //测试类 public class Test { public static void main(String[] args) { Book book = new Book(); book.a++; System.out.println(book.a); } } //执行结果 4
-
- 16.2.5 final总结
- final关键字可以用于成员变量、本地变量、方法以及类。
- final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误。
- 本地变量必须在声明时赋值。
- final变量不能再次赋值。
- final方法不能被重写。
- final类不能被继承。
- 将类、方法、变量声明为final能够提高性能,这样JVM就有机会进行估计,然后优化。
- 按照Java代码惯例,final变量就是常量,而且通常常量名要大写。多个单词之间用下划线连接‘_’。
16.3 作业
- 宠物管理题:
- 定义一个宠物类(Pet),它有两个方法:叫cry(),吃东西eat()。定义宠物的子类狗(Dog),猫(Cat),覆盖父类的cry(),eat()方法,
里面写System.out.println(“猫吃了鱼”)这样的打印语句,另外狗有自己的方法看门guardEntrance(),猫自己独有的方法捉老鼠huntMice()。
定义一个Test类,在main中定义两个Pet变量,pet1,pet2,采用引用转型实例化Dog,Cat,分别调用Pet的cry(),eat();。 - 将Pet引强制转换为具体的Dog,Cat,在调Dog的guardEntrance(),Cat的huntMice()(提示:先用instanceof进行类型判断)Pet p = new Dog(),p.guradEntrance()。
- (思考)编写PetPark类,添加喂养宠物feedPet(Pet pet)的方法,在feedPet中调cry(),eat()方法,
实例化PetPark类,再测试类中实例化狗 Dog dog = new Dog(), 猫 Pet cat = new Cat(),
PetPark分别调feedPet()方法分别传参数cat,dog。
- 定义一个宠物类(Pet),它有两个方法:叫cry(),吃东西eat()。定义宠物的子类狗(Dog),猫(Cat),覆盖父类的cry(),eat()方法,
- 编写 电话 移动电话 固定电话 体现三者之间的继承关系:
- 电话,包含:
- 属性:品牌,号码。
- 方法:打电话 接电话。
- 手机:
- 重写父类中 打电话 接电话。
- 方法 :接收短信。
- 固定电话:
- 重写父类中 打电话 接电话。
- 方法:接宽带。
- 电话厂:
- 方法:测试电话 testPhone(Phone p) ,能在方法内调用电话的打电话与接电话方法。
- 生产电话 getPhone(int type),当 type 为1 则生成一个固定电话,Type为2 则生成一个手机,其他 则生成一个 电话。注意考虑该方法的返回类型。
- 电话,包含:
- 请编码实现如下需求:
- 乐器(Instrument)分为:钢琴(Piano)、小提琴(Violin),各种乐器都具有弹奏( play )方法。但输出效果各不相同。每个类中的属性与方法请自行设计,但要体现继承与多态。
- 编写一个测试类InstrumentTest,要求:编写方法testPlay,对各种乐器进行弹奏测试。要依据乐器的不同,进行相应的弹奏。在main方法中进行测试。
- 设计几个传奇角色(道士,法师,武士),然后有一个Play类,其中有一个方法,用来调用传入角色的打怪行为(一个角色打怪都不一样,道士飞是灵魂火符,法师是雷电术,武士是烈火术),每个类中的属性与方法请自行设计,但要体现继承与多态。
- 编写多种飞机类:每种飞机的起飞方式是不一样,比如直升机是垂直起飞,战斗机是弹射起飞,客机是滑行起飞。所有飞机都继承与父类,都包含起fly()方法。每个类中的属性与方法请自行设计,但要体现继承与多态。编写一个机场类,具备一个方法叫起飞飞机,这个方法可以起飞任何飞机。
- 思考题:利用封装、构造、重载编写传奇。
- 编写传奇角色类,拥有属性:等级(level)、装备(数组 arms)、物理攻击力(attack)、角色名(name)、性别(sex)。编写toString()方法,用来打印出角色的信息。
- 等级属性可设置,可以查看,请为等级属性编写get/set方法。物理攻击属性只能查看,不可修改,请为物理攻击编写get方法。
- 由于性别和角色名是新建角色时指定的,请编写构造函数,初始化角色名和性别。同时,性别和角色名能被其它玩家看见,请为性别和角色名编写get方法。
- 由于装备是个人私有的,并且装备件数不能超过10件,请编写方法为角色增加装备,保证装备不会超过指定件数。
- 在Role类中添加一个表示装备的装备数组属性。
- 该属性设置一个setter方法,方法内判断传入的数组若长度超过10则退出否则把传入的数组赋值给该数组属性。
- 如果玩家被其它玩家杀死,装备可能会随机掉落。请编写death方法,此方法有返回值,返回掉落的物品名。注意:1.如果角色没有装备,则不可能掉落装备。2.如果随机的装备不存在,则再次随机,直到有装备掉落为止。
- 若装备数组的每个元素都是空则不掉落装备,显示无装备可掉落。
- 若可掉落装备表示将装备数组的某个元素值赋值为null,过程为:由随机数确定需要将哪个元素设置为null,且随机到的元素本身就是null的话则继续再随机一次。
- 在游戏中,如果你拿的是刀,攻击操作就成为杀怪或杀人;但如果你的拿是的锄头,攻击。编写攻击的方法,循环装备数组,如果发现装备是刀,则打印出杀人。如果装备是锄头,则挖矿。
- 编写move方法,此方法表示人物的移动。人物移动有几种方式:一、跑动 二、传送 三、回城。如果方法没有接收参数,人物则跑动,如果方法接收了两个数字,则人物传送到两个数字代表的坐标,如果方法接收了“回城”,则返回新手村。请用方法重载来实现这个方法。
- 阅读作业:
- 第一题:
-
写一个父类:
public class A{ private int a; public A(int a){ System.out.println("生成一个A的对象"); this.a=a; } }
-
再写一个类继承A类:
public class B extends A{ //在此处修改 public B(int a){ //加上super(a); } }
-
现写一个测试类:
public class Test{ public static void main(String[] args){ A a=new B(1);//要求此处不报错,问在B类中如何修改 } }
-
- 第二题:
-
A类:
class A{ public void show(int a){ System.out.println("a.show(int a)=" + a); } }
-
B类:
class B{ public void show(double a){ System.out.println("a.show(double a)=" + a); } }
-
测试类:
class Test{ public static void main(String[] args){ B b=new B(); b.show(5);//此处的两个方法调用要求是方法的重载,请更改以上类代码 b.show(5.8); } }
-
- 第三题:
-
A类:
class A{ public void a(){ System.out.println("A.a()"); } }
-
B类:
class B extends A{ private void a(){ System.out.println("A.a()"); } }
-
测试类:
class Test{ public static void main(String[] args){ A a=new B();//此处报错,应如何修改以上代码 } }
-
- 第四题:
-
A类:
package wh; class A{ //如何修改此类,让本程序正确运行 A(){ System.out.println("A生成"); } }
-
B类:
package ww; import wh.A; class B{ public static void main(String[] args){ A a=new A();//此处错误,应如何修改A类的代码 } }
-
- 第一题: