目录
一、多态的引入
二、方法的多态
一、重载
二、重写
三、对象的多态(核心)
四、应用实例
五、向上转型
六、向下转型
七、属性没有重写
八、练习题
第一题
第二题
一、多态的引入
通过主人给宠物喂食这个例子,说明多态的必要性:可以提高代码的复用性
二、方法的多态
一、重载
package com.hspedu.poly_;public class PolyMethod {public static void main(String[] args) {A a = new A();System.out.println(a.sum(10,20));System.out.println(a.sum(10,20,30));}
}
class B { //父类public void say() {System.out.println("B say() 方法被调用...");}
}
class A extends B {//子类public int sum(int n1, int n2){//和下面 sum 构成重载return n1 + n2;}public int sum(int n1, int n2, int n3){return n1 + n2 + n3;}public void say() {System.out.println("A say() 方法被调用...");}
}
二、重写
子类-B类重写了父类-A类的say()方法
package com.hspedu.poly_;public class PolyMethod {public static void main(String[] args) {A a = new A();System.out.println(a.sum(10,20));System.out.println(a.sum(10,20,30));System.out.println("==============");B b = new B();b.say();//调用父类的say()方法a.say();//调用子类的say()方法}
}
class B { //父类public void say() {System.out.println("B say() 方法被调用...");}
}
class A extends B {//子类public int sum(int n1, int n2){//和下面 sum 构成重载return n1 + n2;}public int sum(int n1, int n2, int n3){return n1 + n2 + n3;}public void say() {System.out.println("A say() 方法被调用...");}
}
三、对象的多态(核心)
四条必背知识点:
1)编译类型看对象的左边,运行类型看对象的右边
2)编译类型和运行类型可以不一致(当然也可以一致)
3)编译类型在定义对象时就已经确定了,不能改变
4)运行类型可以改变
eg:
Animal animal = new Dog();//
父类的对象引用指向子类的对象本身
animal 编译类型就是 Animal , 运行类型 Dog
animal = new Cat();
编译类型还是Anmal,运行类型从Dog变成了Cat
package com.hspedu.poly_.objectpoly_;public class Animal {public void cry(){System.out.println("Animal cry() 动物在叫....");}
}
package com.hspedu.poly_.objectpoly_;public class Dog extends Animal{@Overridepublic void cry() {System.out.println("Dog cry() 小狗汪汪叫...");}
}
package com.hspedu.poly_.objectpoly_;public class Cat extends Animal{@Overridepublic void cry() {System.out.println("Cat cry() 小猫喵喵叫...");}
}
package com.hspedu.poly_.objectpoly_;public class PolyObject {public static void main(String[] args) {//体验对象多态特点//animal 编译类型就是 Animal , 运行类型 DogAnimal animal = new Dog();//因为运行时,执行到该行时,animal 运行类型是 Dog,所以 cry 就是 Dog 的 cryanimal.cry();//调用Dog的cry()方法//运行类型发生了改变//animal 编译类型 Animal,运行类型就是 Catanimal = new Cat();animal.cry();//调用Cat的cry()方法}
}
披着羊皮的狼:外形是羊(编译类型),内在是狼(运行类型)
四、应用实例
宠物喂食的例子
Animal类和它的子类
package com.hspedu.poly_;public class Animal {private String name;public Animal(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
package com.hspedu.poly_;public class Cat extends Animal{public Cat(String name) {super(name);}}
package com.hspedu.poly_;public class Dog extends Animal{public Dog(String name) {super(name);}
}
Food和它的子类
package com.hspedu.poly_;public class Food {private String name;public Food(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
package com.hspedu.poly_;public class Fish extends Food{public Fish(String name) {super(name);}
}
package com.hspedu.poly_;public class Bone extends Food{public Bone(String name) {super(name);}
}
Master
package com.hspedu.poly_;public class Master {private String name;public Master(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}//父类的对象引用可以指向子类的对象本身//animal的编译类型是Animal,可以指向(接收) Animal 子类的对象//food编译类型是Food ,可以指向(接收) Food 子类的对象public void feed(Animal animal, Food food){System.out.println("主人" + name + "给" + animal.getName() + "喂" + food.getName());}//feed方法喂食物,每增加一种动物,就需要多写一个feed方法,十分麻烦
// public void feed(Dog dog, Bone bone){
// System.out.println("主人" + name + "给" + dog.getName() + "喂" + bone.getName());
// }
// public void feed(Cat cat, Fish fish){
// System.out.println("主人" + name + "给" + cat.getName() + "喂" + fish.getName());
// }
}
main方法
package com.hspedu.poly_;public class Poly01 {public static void main(String[] args) {Master master1 = new Master("sunny");Dog dog = new Dog("大黄");Bone bone = new Bone("大棒骨~");master1.feed(dog,bone);Cat cat = new Cat("多多");Fish fish = new Fish("鲱鱼罐头~");System.out.println("=================");master1.feed(cat,fish);}
}
控制台输出
为了更好的验证对象的多态,增加了一个Animal的子类Rabbit,Food的子类Carrot
package com.hspedu.poly_;public class Rabbit extends Animal{public Rabbit(String name) {super(name);}
}
package com.hspedu.poly_;public class Carrot extends Food{public Carrot(String name) {super(name);}
}
由于父类的对象引用可以指向子类的对象本身,所以可以直接在main方法内创建新对象
package com.hspedu.poly_;public class Poly01 {public static void main(String[] args) {Master master1 = new Master("sunny");Dog dog = new Dog("大黄");Bone bone = new Bone("大棒骨~");master1.feed(dog,bone);Cat cat = new Cat("多多");Fish fish = new Fish("鲱鱼罐头~");System.out.println("=================");master1.feed(cat,fish);Rabbit rabbit = new Rabbit("小白");Carrot carrot = new Carrot("胡萝卜");System.out.println("=================");master1.feed(rabbit,carrot);}
}
五、向上转型
父类的对象引用指向子类的对象本身
Animal animal = new Dog();
package com.hspedu.poly_.detail_;public class TopBase {public void hello() {System.out.println("TopBase hello");}
}
package com.hspedu.poly_.detail_;public class Base extends TopBase{public void hi() {System.out.println("Base hi");}
}
package com.hspedu.poly_.detail_;public class Animal extends Base {String name = "动物";int age = 10;public void sleep(){System.out.println("睡");}public void run(){System.out.println("跑");}public void eat(){System.out.println("吃");}public void show(){System.out.println("hello,你好");}
}
package com.hspedu.poly_.detail_;public class Cat extends Animal{String color = "白色";public void eat(){//方法重写System.out.println("猫吃鱼");}public void catchMouse() {//Cat特有方法System.out.println("猫抓老鼠");}
}
package com.hspedu.poly_.detail_;public class PolyDetail {public static void main(String[] args) {//向上转型Animal animal = new Cat();animal.eat();//在编译阶段,能调用哪些成员,是由编译类型来决定的,// 编译类型是animal,可以访问从Object类到Animal类的所有方法,// 不能访问子类独有的方法// animal.catchMouse();Object obj = new Cat();//编译类型是Object,在编译阶段只能访问Object类的方法//向上转型调用方法的规则如下://(1)可以调用父类中的所有成员(需遵守访问权限)//(2)但是不能调用子类的特有的成员//(#)因为在编译阶段, 能调用哪些成员,是由编译类型来决定的//animal.catchMouse();错误//(4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时, 按照从子类(运行类型)开始查找方法//,然后调用,规则跟前面学的方法调用规则一致。System.out.println("===================");animal.hello();//访问到TopBase类的hello()方法animal.hi();//访问到Base类的hi()方法animal.run();//访问到Animal类的run()方法animal.sleep();//访问到Animal类的sleep()方法animal.eat();//访问到Cat类的eat()方法animal.show();//访问到Animal类的show()方法}
}
六、向下转型
需要解决的问题:调用子类特有的方法
语法:子类类型 引用名 = (子类类型)父类引用名
eg:Cat cat = (Cat)animal;
在编译时没有错误,但是在运行时就会报错,出现以下错误
类型转换异常
七、属性没有重写
属性只看编译类型以及比较运算符instanceof
package com.hspedu.poly_.detail_;public class PolyDetail02 {public static void main(String[] args) {AA aa = new BB();//属性没有重写之说System.out.println(aa.count);//属性看编译类型,输出10BB bb = new BB();System.out.println(bb.count);//20System.out.println(bb instanceof BB);System.out.println(bb instanceof AA);System.out.println(bb instanceof Object);//编译类型是AA,运行类型是BBAA aa1 = new BB();System.out.println(aa1 instanceof BB);System.out.println(aa1 instanceof AA);Object obj = new Object();System.out.println(obj instanceof AA);//falseString str = "hello";//System.out.println(str instanceof AA);//字符串和AA没有任何关系System.out.println(str instanceof Object);//true}
}
class AA{int count = 10;
}
class BB extends AA{int count = 20;
}
八、练习题
第一题
考察向上转型和向下转型的知识点,其中还有之前讲过的变量的强制类型转换
double d = 13.4;
long l = (long)d;
package com.hspedu.poly_.exercise_;public class PolyExercise01 {public static void main(String[] args) {double d = 13.4;//强制类型转换,变成13long l = (long)d;System.out.println(l);//输出13int in = 5;//boolean和int不能转换,所以报错//boolean b = (boolean)in;//向上转型,编译类型是Object,运行类型是String//父类Object的对象引用obj指向子类-String类的对象Object obj = "Hello";//向下转型,编译类型是String,运行类型是StringString objStr = (String)obj;//输出HelloSystem.out.println(objStr);//向上转型,编译类型Object,运行类型IntegerObject objPri = new Integer(5);//承接上句,父类引用objPri指向的是Integer类的对象//所以目标类型应该是Integer类,而不是String类//String str = (String)objPri;//编译没错,但是运行时会报错//向下转型,编译类型Integer,运行类型IntegerInteger str1 = (Integer)objPri;System.out.println(str1);//输出5}
}
第二题
考察知识点:属性没有重写之说,对象的运行类型决定了调用哪个类中的方法(继承中的父类-子类)
package com.hspedu.poly_.exercise_;public class Base {int count = 10;public void display(){System.out.println(this.count);}
}
package com.hspedu.poly_.exercise_;public class Sub extends Base{int count = 20;public void display(){//方法重写System.out.println(this.count);}
}
package com.hspedu.poly_.exercise_;public class PolyExercise02 {public static void main(String[] args) {//编译类型是Sub,运行类型是SubSub s = new Sub();//属性是没有重写的,看编译类型,输出20System.out.println(s.count);//方法的调用由运行类型决定的,运行类型是Sub,输出20s.display();//向上转型,父类引用b指向子类Sub类的对象//编译类型是Base,运行类型是SubBase b = s;System.out.println(b == s);//指向同一对象本身,true//属性看编译类型,编译类型是Base,输出10System.out.println(b.count);//方法的调用由运行类型决定的,运行类型是Sub,输出20b.display();}
}