目录
一、多态的引入
二、方法的多态
一、重载
二、重写
三、对象的多态(核心)
四、应用实例
五、向上转型
六、向下转型
七、属性没有重写
八、练习题
第一题
第二题
通过主人给宠物喂食这个例子,说明多态的必要性:可以提高代码的复用性
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{
@Override
public void cry() {
System.out.println("Dog cry() 小狗汪汪叫...");
}
}
package com.hspedu.poly_.objectpoly_;
public class Cat extends Animal{
@Override
public void cry() {
System.out.println("Cat cry() 小猫喵喵叫...");
}
}
package com.hspedu.poly_.objectpoly_;
public class PolyObject {
public static void main(String[] args) {
//体验对象多态特点
//animal 编译类型就是 Animal , 运行类型 Dog
Animal animal = new Dog();
//因为运行时,执行到该行时,animal 运行类型是 Dog,所以 cry 就是 Dog 的 cry
animal.cry();//调用Dog的cry()方法
//运行类型发生了改变
//animal 编译类型 Animal,运行类型就是 Cat
animal = 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);//属性看编译类型,输出10
BB bb = new BB();
System.out.println(bb.count);//20
System.out.println(bb instanceof BB);
System.out.println(bb instanceof AA);
System.out.println(bb instanceof Object);
//编译类型是AA,运行类型是BB
AA 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);//false
String 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;
//强制类型转换,变成13
long l = (long)d;
System.out.println(l);//输出13
int in = 5;
//boolean和int不能转换,所以报错
//boolean b = (boolean)in;
//向上转型,编译类型是Object,运行类型是String
//父类Object的对象引用obj指向子类-String类的对象
Object obj = "Hello";
//向下转型,编译类型是String,运行类型是String
String objStr = (String)obj;
//输出Hello
System.out.println(objStr);
//向上转型,编译类型Object,运行类型Integer
Object objPri = new Integer(5);
//承接上句,父类引用objPri指向的是Integer类的对象
//所以目标类型应该是Integer类,而不是String类
//String str = (String)objPri;//编译没错,但是运行时会报错
//向下转型,编译类型Integer,运行类型Integer
Integer 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,运行类型是Sub
Sub s = new Sub();
//属性是没有重写的,看编译类型,输出20
System.out.println(s.count);
//方法的调用由运行类型决定的,运行类型是Sub,输出20
s.display();
//向上转型,父类引用b指向子类Sub类的对象
//编译类型是Base,运行类型是Sub
Base b = s;
System.out.println(b == s);//指向同一对象本身,true
//属性看编译类型,编译类型是Base,输出10
System.out.println(b.count);
//方法的调用由运行类型决定的,运行类型是Sub,输出20
b.display();
}
}