做了那么多铺垫,现在我们理解OOP的最后一个特性多态。
那么什么是多态?有什么好处?
多态最简单的一句话说就是方法的多态
,属性没有多态
多态的好处 就是 提高了代码的扩展性下面会详细介绍
一、多态
同一个方法可以根据发送对象的不同而采区多种不同的行为方式、
一个对象的实际类型是确定的,但是可以指向对象的引用类型有很多
就比如遇到的报错:类型转换异常 ClassCastException!
static方法属于类,不属于实例。不能重写
final 常量;池里面的也不能重写
private私有的 也不能重新
1、对象的多态性:同一个对象,在不同的环境下,有不同的名称 (事物的多种形态)
如:你在教室 同学们叫你《小虫虫
》 回到了宿舍 你是他们的《室友
》 回到了家 你是爸爸妈妈的《儿子
》 狗蛋 😉
2、java中,同一个对象有不同类型的引用指向他,本质上,同一个事物具有不同的名称和描述
(1) MiddleStudent(中学生)
继承 Student(学生)
继承 Person(人)
(2) MiddleStudent ms = new MiddleStudent(); //初中生对象
(3) MiddleStudent ms2 = ms;
(4) Student ss = new MiddleStudent();
(5) Person p = ss;
(6) java中的多态指的是,父类的引用指向子类对象
3、多态的实现前提: (多态存在的条件)
(1) 必须要有父子类关系,没有继承,就没有多态!!
(有继承关系)
(2) 父类的引用指向子类对象 father F1 = new son();
(3)子类重写父类的方法
根据代码理解一下
public class Demo01 {
public static void main(String[] args) {
//new Student是创建的对象
//Student s就是学生类型的引用
Student s = new Student();
System.out.println(s.name);
s.speak();
s.hi();
//人类对象
Person p = new Person();
System.out.println(p.name);
p.speak();
p.hi();
//建立多态关系 父类的引用指向子类对象
Person p2 = new Student();
System.out.println(p2.ID);
//真正的对象是学生
//学生 通过不同的属性区分
//在人类的情况下 去访问姓名
p2.speak();
p2.hi();
// p2.study();
}
}
class Person{
String ID = "人";
public void speak(){
System.out.println("我是人");
}
public void hi(){
System.out.println("大家早上好");
}
}
class Student extends Person{
String ID = "学生";
@Override
public void speak() {
System.out.println("我是学生");
}
public void study(){
System.out.println("学习");
}
}
1.多态访问成员方法的特点
1、编译看左,运行看右
2、编译的时候看等号左边的类型中是否定义了该方法,如果有就编译通过。
3、运行的时候看等号右边的类型,对象所属的类中,如果对这个方法提供了新的实现,最终运行的就是子类对这个方法的实现,如果没有就使用父类中提供的实现
根据代码理解一下
public class Demo02 {
public static void main(String[] args) {
Zi z = new Zi();
//子类中既可以访问父类的也可以访问子类的
//建立多态关系
Fu f = new Zi();//父类的引用指向子类对象
//父类的引用 Fu f super表示本类当前对象父类的引用
//此时当前对象时候 子类对象 引用是父类引用
//子类的引用 Zi z this表示本类当前对象的引用
System.out.println(f.name);//如果访问属性,编译看左,运行看左
// f.age父类中没有
//行为 编译看左,运行看右
//编译看左,只能看父类中有没有定义该方法,不能出现使用父类中没有的方法
f.test();//父类有test。子类也有test子类的test和父类的关系是重写
//重写的本质是使用父类的方法定义修改为子类的实现,本质上方法还是属于父类的
f.test2();//编译看左,只要左边有就编译通过,运行看右,没有但是有继承关系
//此时继承父类的test2方法,所以能够运行
// f.test3();//编译看左,父类中此时没有该方法定义
}
}
class Fu{
String name = "张三";
public void test(){
System.out.println("我是父类的test方法");
}
public void test2(){
System.out.println("我是父类的test2方法");
}
}
class Zi extends Fu{
int age = 33;
public void test(){
System.out.println("我是子类的test方法,我更加优秀");
}
public void test3(){
System.out.println("我是子类的test3方法");
}
}
2.多态的向上向下转型
1、向上转型
:多态的体现,就是向上转型
(1) 原来是 子类的引用指向子类对象
(2) 向上转型就是 父类的引用指向子类对象
(3) 本质:缩小了对象本身的访问范围,减少了访问权限,只能访问父类中的内容
2、向下转型
:
(1) 前置条件:一定是一个经过向上转型的对象
(2) 子类类型 新的引用名称 = (子类类型)父类类型的引用;
(3) 本质:恢复子类本身的访问范围
3、注意
:不可以把父类对象进行向下转型,必须是父类的引用指向子类对象这种类型,才可以向下转型
根据代码理解一下
public class Demo03 {
public static void main(String[] args) {
//金龙是超人,隐藏身份和我们相处
//向上转型, 父类的引用指向子类对象
Man m = new SuperMan();//创建了一个多态关系,缩小了对象本身的访问范围,访问的都是父类的内容
System.out.println(m.name);
m.talkMoney();//男人都会谈生意,只是小虫虫牛了一点,都是几个亿的能接受
// m.fly();男人不会飞,要隐藏身份,否则真的解剖了
//向下转型 恢复子类本身的访问范围,既能访问子类的,也能访问父类的
//某一天,金龙遭遇了暴打,不装了,摊牌了,生气了,告诉你我是个超人
SuperMan s = (SuperMan)m;
System.out.println(s.name);
s.fly();
s.talkMoney();
}
}
//金龙 是一个隐藏了身份和大家相处的超人,需要挨打才能解锁变身
class Man{
String name = "小虫虫";
public void talkMoney(){
System.out.println("谈一谈不同的小生意几十块的");
}
}
class SuperMan extends Man{
String name = "金虫超人";
@Override
public void talkMoney() {
System.out.println("谈的都是几个亿的大生意");
}
public void fly(){
System.out.println("到处飞着倒垃圾");
}
}
3.多态的好处
上面说了多态的好处是:提高了代码的扩展性
根据上面的代码扩展理解
public class Demo03 {
public static void main(String[] args) {
//金龙是超人,隐藏身份和我们相处
//向上转型, 父类的引用指向子类对象
Man m = new SuperMan();//创建了一个多态关系,缩小了对象本身的访问范围,访问的都是父类的内容
System.out.println(m.name);
m.talkMoney();//男人都会谈生意,只是小虫虫牛了一点,都是几个亿的能接受
// m.fly();男人不会飞,要隐藏身份,否则真的解剖了
//要转换到SuperMan类型
//先判断这个对象能否转换到这个类型
//多态对象 m
//要转换的类型 SuperMan
// m instanceof SuperMan 对象m是SuperMan类型返回true不是返回false
/* if(m instanceof SuperMan){
//向下转型 恢复子类本身的访问范围,既能访问子类的,也能访问父类的
//某一天,金龙遭遇了暴打,不装了,摊牌了,生气了,告诉你我是个超人
SuperMan s = (SuperMan)m;
System.out.println(s.name);
s.fly();
s.talkMoney();
}*/
Man m2 = new OldMan();
if(m2 instanceof SuperMan) {//验证m2本质上是否是SuperMan类型
SuperMan s = (SuperMan) m2;
s.fly();
}
OldMan o = new OldMan();
test(o);
SuperMan s = new SuperMan();
test(s);
}
public static void test(Man m){//Man m = new SuperMan();
//原来只能传递Man 类型的引用 类型的对象
//现在可以传递不同的子类对象
m.say();
}
}
//金龙 是一个隐藏了身份和大家相处的超人,需要挨打才能解锁变身
class Man{
String name = "小虫虫";
public void talkMoney(){
System.out.println("谈一谈不同的小生意几十块的");
}
public void say(){
System.out.println("说男人");
}
}
class SuperMan extends Man{
String name = "金虫超人";
@Override
public void talkMoney() {
System.out.println("谈的都是几个亿的大生意");
}
public void fly(){
System.out.println("到处飞着倒垃圾");
}
public void say(){
System.out.println("说超人");
}
}
class OldMan extends Man{
@Override
public void say() {
System.out.println("说老男人");
}
}
-------------------------------------------------------------------------
package com.demo;
public class Demo04 {
public static void main(String[] args) {
Test t = new Test();
Fu f = new Zi1();
Fu f2 = new Zi2();
Fu f3 = new Zi3();
t.test(f);
t.test(f2);
t.test(f3);
}
}
class Test{
//能不能传递子类对象
public void test(Fu f){ //
f.say();
}
}
class Fu{
public void say(){
System.out.println("说");
}
}
class Zi1 extends Fu{
public void say(){
System.out.println("说天");
}
}
class Zi2 extends Fu{
public void say(){
System.out.println("说地");
}
}
class Zi3 extends Fu{
public void say(){
System.out.println("说虫虫");
}
}
练习
1、现在有男孩类(Boy),行为会喂养宠物feedPet
2、有宠物类Pet,有狗类Dog、猫类Cat、猴类Monkey,行为都会吃,只不过吃的内容不太一样
3、运用恰当的方式,使下方代码可以运行
(1) Boy b = new Boy();
(2) Dog d = new Dog();
(3) b.feedPet(d)
(4) Cat c = new Cat();
(5) b.feedPet©;
(6) Monkey m = new Monkey();
(7) b.feedPet(m);
public class Demo05 {
public static void main(String[] args) {
Boy b = new Boy();
Dog d = new Dog();
b.feedPet(d);
Cat c = new Cat();
b.feedPet(c);
Monkey m = new Monkey();
b.feedPet(m);
}
}
//1、现在有男孩类(Boy),行为会喂养宠物feedPet
// 2、有宠物类Pet,有狗类Dog、猫类Cat、猴类Monkey,行为都会吃,只不过吃的内容不太一样
// 3、运用恰当的方式,使下方代码可以运行
// (1)Boy b = new Boy();
// (2)Dog d = new Dog();
// (3)b.feedPet(d)
// (4)Cat c = new Cat();
// (5)b.feedPet(c);
// (6)Monkey m = new Monkey();
// (7)b.feedPet(m);
class Boy{
public void feedPet(Pet p){//形式参数是父类类型的,实际参数可以传递不同的子类对象
p.eat();
}
// public void feedDog(Dog d){
// d.eat();
// }
}
class Pet{
public void eat(){
System.out.println("吃");
}
}
class Dog extends Pet{
@Override
public void eat() {
System.out.println("吃狗粮");
}
}
class Cat extends Pet{
@Override
public void eat() {
System.out.println("吃猫粮");
}
}
class Monkey extends Pet{
@Override
public void eat() {
System.out.println("吃香蕉");
}
}
二、关键字instanceof
关键字instanceof来判断向下转型的类型是不是这个对象的类型
格式: 多态对象 instanceof 指定的数据类型 返回值是布尔类型的数据
System.out.println(X instanceof Y); 看XY有没有父子关系
1、向下转型,可能出现转为其他的子类类型,编译不会报错,但是运行时会发生类型转换异常
2、解决方案:在转换之前判断一下要转换的类型是不是多态对象之前的类型
import oop.Duotai.A;
import oop.Duotai.B;
public class Application {
public static void main(String[] args) {
// System.out.println(X instanceof Y); 看XY有没有父子关系
//Object>String
//Object>B>A
Object object = new A();
System.out.println(object instanceof A);//true
System.out.println(object instanceof B);//true
System.out.println(object instanceof Object);//true
System.out.println(object instanceof String);//false
System.out.println("=====================================");
B b = new A();
System.out.println(b instanceof A);//true
System.out.println(b instanceof B);//true
System.out.println(b instanceof Object);//true
// System.out.println(b instanceof String);//编译报错!
System.out.println("=====================================");
A a = new A();
System.out.println(a instanceof A);//true
System.out.println(a instanceof B);//true
System.out.println(a instanceof Object);//true
//System.out.println(b instanceof String);//编译报错
}
}
好啦最后对多态做一个总结:
1、多态关系的构建语法格式是? 父类的引用指向子类对象
2、多态的前提是? 先构建继承关系,没有继承就没有多态
3、多态访问成员方法的特点 编译看左运行看右
4、多态访问成员变量的特点 编译看左运行看左
5、多态的好处是什么 提高了代码的扩展性
6、什么是向上转型,什么是向下转型
多态的体现就是向上转型,父类的引用指向子类对象
恢复子类本身的访问范围,类型强制转换 子类类型 新对象名 = (子类类型)对象名;
7、抽象方法如何定义
public abstract 返回值类型 方法名称(参数列表);
8、抽象类和抽象方法有什么关系
抽象类中不一定有抽象方法,
抽象方法必须在抽象类中
9、抽象类和普通类有什么区别
多了抽象方法
不能创建对象
子类必须重写抽象类中的抽象方法
10、抽象类可以创建对象吗
不能
11、抽象类的子类一定能够创建对象吗
不一定
子类如果重写了所有的抽象方法 可以创建
如果没有全部重写父类的抽象方法,还是一个抽象类
12. 定义一个Man类, 属性有姓名, 方法: 开车
有一个公交车类Bus, 属性有:速度 方法有: 跑
有一个卡车类Truck, 属性有:速度 方法有: 跑
有一个跑车类SportsCar: 属性有:速度 方法有: 跑
要求:编写一个测试类: 创建一个Man对象,可以驾驭上面任意一款车型
Man m = new Man();
public class Demo01 {
public static void main(String[] args){
Man m = new Man();
m.name = "陈毅";
m.driverCar(new Bus(100));
m.driverCar(new SportsCar(30));
m.driverCar(new Truck(120));
}
}
//12. 定义一个Man类, 属性有姓名, 方法: 开车
// 有一个公交车类Bus, 属性有:速度 方法有: 跑
// 有一个卡车类Truck, 属性有:速度 方法有: 跑
// 有一个跑车类SportsCar: 属性有:速度 方法有: 跑
// 要求:编写一个测试类: 创建一个Man对象,可以驾驭上面任意一款车型
class Man{
String name;
public void driverCar(Car c){
c.run();
}
}
abstract class Car{
private int speed;
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public Car(int speed) {
this.speed = speed;
}
public abstract void run();
}
class Bus extends Car{
public Bus(int speed) {
super(speed);
}
@Override
public void run() {
System.out.println("摇摇晃晃" +getSpeed());
}
}
class Truck extends Car{
public Truck(int speed) {
super(speed);
}
@Override
public void run() {
System.out.println("横推一切" + getSpeed());
}
}
class SportsCar extends Car{
public SportsCar(int speed) {
super(speed);
}
@Override
public void run() {
System.out.println("风驰电掣" + getSpeed());
}
}
13.编一个Perosn, 包含属性姓名, 年龄, 行为:说话, 吃饭, 睡觉
Person有一个子类Student, 比Person多一个属性分数, 重写说话, 吃饭,睡觉的方法
Person有一个子类Teacher, 比Person多一个属性薪资, 重写说话, 吃饭,睡觉的方法
人类下每个子类对行为的实现均不相同
编写测试类Student和Teacher对象,并调用每个对象的方法.
public class Demo02 {
}
//13.编一个Perosn, 包含属性姓名, 年龄, 行为:说话, 吃饭, 睡觉
// Person有一个子类Student, 比Person多一个属性分数, 重写说话, 吃饭,睡觉的方法
// Person有一个子类Teacher, 比Person多一个属性薪资, 重写说话, 吃饭,睡觉的方法
// 人类下每个子类对行为的实现均不相同
// 编写测试类Student和Teacher对象,并调用每个对象的方法.
//
abstract class Person{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public abstract void say();
public abstract void eat();
public abstract void sleep();
}
class Student extends Person{
private int score;
public Student() {
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public Student(String name, int age, int score) {
super(name, age);
this.score = score;
}
@Override
public void say() {
System.out.println("谈天说地就是不聊学习");
}
@Override
public void eat() {
System.out.println("干饭人,干饭魂。吃最重要");
}
@Override
public void sleep() {
System.out.println("教室里睡的最香");
}
}
class Teacher extends Person{
private int salary;
public Teacher() {
}
public Teacher(String name, int age, int salary) {
super(name, age);
this.salary = salary;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
@Override
public void say() {
System.out.println("那叫一个说啊,一直说,一说六个小时");
}
@Override
public void eat() {
System.out.println("吃,也是最重要的,同是干饭人");
}
@Override
public void sleep() {
System.out.println("上班天天打瞌睡 不能睡");
}
}
14. 有如下代码,请问哪些是不正确的?( )
class ClassA{}
class ClassB extends ClassA{}
class ClassC extends ClassA{}
//BC是A的子类
ClassA p0 = new ClassA();//A类引用指向A类对象
ClassB p1 = new ClassB();//B类引用指向B类对象
ClassC p2 = new ClassC();//C类引用指向C类对象
ClassA p3 = new ClassB();//A类的引用指向B类的对象
ClassA p4 = new ClassC();//A类的引用指向C类的对象
// A类的引用 = B类的对象
p0 = p1; //合理
//sp1 = p2;//b类的引用 = C类的对象 BC都是子类 没有直接关系 所以不可以
//必须是父类的引用指向子类对象才可以
p1 = (ClassB)p3;//B类的引用 向下转型 将P3转为B类的引用B类的对象
p2 = (ClassC)p4;//同上