目录
第2章 多态
2.1 多态概述
多态是继封装、继承之后,面向对象的第三大特性。
如:张三,既是学生也是人,即出现多种形态:一个对象多种形态
Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
Java中多态的代码体现在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类(实现类对象)的父类(接口)变量赋值。
如:Student类可以为Person类的子类。那么一个Student对象既可以赋值给一个Student类型的引用,也可以赋值给一个Person类型的引用。
最终多态体现为父类引用变量可以指向子类对象。
多态的前提是必须有子父类关系或者类实现接口关系,否则无法完成多态。
在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
2.2 多态的定义与使用格式
多态的定义格式:就是父类的引用变量指向子类对象
父类类型 变量名 = new 子类类型();
变量名.方法名();
(1)普通类多态定义的格式
父类 变量名 = new 子类();
(2)抽象类多态定义的格式
抽象类 变量名 = new 抽象类子类();
如:abstract class Fu {
public abstract void method();
}
(3)接口多态定义的格式
接口 变量名 = new 接口实现类();
如: interface Fu {
public abstract void method();
}
class Zi implements Fu {
public void method(){
System.out.println(“重写接口抽象方法”);
}
}
//接口的多态使用
Fu fu = new Zi();
注意事项:
同一个父类的方法会被不同的子类重写。在调用方法时,调用的为各个子类重写后的方法。
如 Person p1 = new Student();
Person p2 = new Teacher();
p1.work(); //p1会调用Student类中重写的work方法
p2.work(); //p2会调用Teacher类中重写的work方法
当变量名指向不同的子类对象时,由于每个子类重写父类方法的内容不同,所以会调用不同的方法。
2.3 多态-成员的特点
成员变量:
编译的时候, 参考父类中有没有这个变量,如果有,编译成功,没有编译失败
运行的时候, 运行的是父类中的变量值
编译运行全看父类
成员方法:
编译的时候, 参考父类中有没有这个方法,如果有,编译成功,没有,编译失败
运行的时候, 运行的是子类的重写方法
编译看父类,运行看子类
//Fu.java
package cn.itcast.demo06;
public class Fu { int a = 1;
public void show() { System.out.println("父类方法"); } } | package cn.itcast.demo06;
public class Zi extends Fu { int a = 2;
public void show() { System.out.println("子类方法"); } }
|
//Test.java
public class Test {
public static void main(String[] args) {
Fu f= new Zi();//多态调用
System.out.println(f.a);//父类的变量值
f.show();
Zi f1=new Zi();//子类自己定义的对象,没涉及多态调用
f1.show();
}
}
运行结果:
1
子类方法
子类方法
2.4 instanceof关键字
我们可以通过instanceof关键字来判断某个对象是否属于某种数据类型。如学生的对象属于学生类,学生的对象也属于人类。
使用格式:
boolean b = 对象 instanceof 数据类型;
Person p1 = new Student(); // 前提条件,学生类已经继承了人类
boolean flag = p1 instanceof Student; // flag结果为true
boolean flag2 = p1 instanceof Teacher; // flag2结果为false
//Test.java
package cn.itcast.demo07;
/*
* 比较运算符,结果真假值
* 关键字,instanceof,比较引用数据类型(以前是== <>)
*
* Person p = new Student();
* p = new Teacher()
*
*
* 关键字 instanceof 比较, 一个引用类型的变量,是不是这个类型的对象
* p变量,是Student类型对象,还是Teacher类型对象
*
* 引用变量 instanceof 类名
* p instanceof Student 比较,p是不是Student类型的对象,如果是,intanceof返回true
*
*/
public class Test {
public static void main(String[] args) {
Person p = new Student();
// Person p=new Teacher();
boolean b = p instanceof Student;
System.out.println(b);
// b和Animal没有关系
// b = p instanceof Animal;
// System.out.println(b);
p.sleep();
}
}
运行结果:
true
学生在睡觉
2.5 多态-转型
多态的转型分为向上转型与向下转型两种:
向上转型:当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程。
使用格式:
父类类型 变量名 = new 子类类型();
如: Person p = new Student();
向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。如果是直接创建父类对象,是无法向下转型的!
使用格式:
子类类型 变量名 = (子类类型) 父类类型的变量;
如: Student stu = (Student) p; //变量p 实际上指向Student对象
2.6 多态的好处与弊端
总结:
- 什么时候使用向上转型:
当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作,这时就可以使用向上转型。
如:Animal a = new Dog();
a.eat();
- 什么时候使用向下转型:
当要使用子类特有功能时,就需要使用向下转型。
如:Dog d = (Dog) a; //向下转型
d.lookHome(); //调用狗类的lookHome方法
向上转型的好处:隐藏了子类类型,提高了代码的扩展性。
向上转型的弊端:只能使用父类共性的内容,而无法使用子类特有功能,功能有限制
向下转型的好处:可以使用子类特有功能。
向下转型的弊端:需要面对具体的子类对象;在向下转型时容易发生ClassCastException类型转换异常。在转换之前必须做类型判断。
如:if( !a instanceof Dog){…}
package cn.itcast.demo08;
/*
* 测试类
* 1. 实现动物和Cat,Dog多态调用
* 2. 做类型的强制转换,调用子类的特有功能
*/
public class Test {
public static void main(String[] args) {
// 两个子类,使用两次多态调用
Animal a1 = new Cat();
Animal a2 = new Dog();
// a1,a2调用子类父类共有方法,运行走子类的重写
a1.eat();
a2.eat();
// a1.CatchMouse(); 使用Cat类特有的方法,需要向下转型,不能直接使用
// 类型向下转型,强制转换,调用子类的特有
// 防止发生异常: a1属于Cat对象,转成Cat类, a2属于Dog对象,转成Dog
// instanceof判断
if (a2 instanceof Cat) {
Cat c = (Cat) a2;
c.CatchMouse();
}
if (a2 instanceof Dog) {
Dog c = (Dog) a2;
c.LookHome();
}
}
}
总结下面向对象的三大特征,封装、继承、多态的作用:
- 封装:把对象的属性与方法的实现细节隐藏,仅对外提供一些公共的访问方式
- 继承:子类会自动拥有父类所有可继承的属性和方法。
- 多态:配合继承与方法重写提高了代码的复用性与扩展性;如果没有方法重写,则多态同样没有意义。
第3章 笔记本电脑案例
3.1 案例介绍
定义USB接口(具备开启功能、关闭功能),
笔记本要使用USB设备,即笔记本在生产时需要预留可以插入USB设备的USB接口,即就是笔记本具备使用USB设备的功能,但具体是什么USB设备,笔记本并不关心,只要符合USB规格的设备都可以。
鼠标和键盘要想能在电脑上使用,那么鼠标和键盘也必须遵守USB规范,不然鼠标和键盘的生产出来无法使用.
进行描述笔记本类,实现笔记本使用USB鼠标、USB键盘
- USB接口,包含开启功能、关闭功能
- 笔记本类,包含运行功能、关机功能、使用USB设备功能
- 鼠标类,要符合USB接口
- 键盘类,要符合USB接口
3.2 案例需求分析
阶段一:
使用笔记本,笔记本有运行功能,需要笔记本对象来运行这个功能
阶段二:
想使用一个鼠标,又有一个功能使用鼠标,并多了一个鼠标对象。
阶段三:
还想使用一个键盘 ,又要多一个功能和一个对象
问题:每多一个功能就需要在笔记本对象中定义一个方法,不爽,程序扩展性极差。
降低鼠标、键盘等外围设备和笔记本电脑的耦合性。
//USB.java
package cn.itcast.demo09;
/*
* 自定义的USB接口类
* 规范,就是抽象方法
* 开,关
*/
public interface USB {
public abstract void open();
public abstract void close();
}
//Computer.java
package cn.itcast.demo09;
/*
* 定义笔记本类
* 功能: 开机,关机,使用USB设备
*/
public class Computer {
public void openComputer(){
System.out.println("笔记本开机");
}
public void closeComputer(){
System.out.println("笔记本关机");
}
//使用USB设备方法,哪个设备
//方法的参数,就是USB设备
public void useUSB(USB usb){// USB usb = new Mouse()
usb.open();
usb.close();
}
}
//Mouse.java
package cn.itcast.demo09;
/*
* 自定义的鼠标类
* 满足USB接口规范
* 实现USB接口,重写抽象方法
*/
public class Mouse implements USB{
public void open(){
System.out.println("开启鼠标");
}
public void close(){
System.out.println("关闭鼠标");
}
}
//Keyboard.java
package cn.itcast.demo09;
public class Keyboard implements USB{
public void open(){
System.out.println("开启键盘");
}
public void close(){
System.out.println("关闭键盘");
}
}
// Test.java
package cn.itcast.demo09;
public class Test {
public static void main(String[] args) {
//创建笔记本对象,调用笔记本的功能
Computer com = new Computer();
com.openComputer();
//调用笔记本使用USB设备的方法
//参数,是USB接口类型,接口不能建立对象
//调用方法,传递USB接口的实现类的对象
//Mouse m = new Mouse();
com.useUSB(new Mouse());
//使用USB设备,使用键盘
com.useUSB(new Keyboard());
com.closeComputer();
}
}