Java 多态

写在开头

 Java语言的三大特点之一:多态
 多态是同一个行为具有多个不同表现形式或形态的能力。
 事物的多种形态,父类引用指向子类对象
 今天我们说一下这一个比较晦涩难懂的概念

概念

事物的多种形态

  • 举例:猫是猫,但猫也是动物,猫这个类就具有多种形态
  • 发生的前提 :必须有子父类继承关系或者接口实现关系;子类必须重写父类方法
  • 表现方法:父类引用指向子类对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  Animal 类,父类
Cat 类,子类
Dog 类,子类

class Cat extends Aniaml{
重写方法;
}
class Dog extends Animal{
重写方法;
}

class Test{
main(){
Animal a = new Cat();// 多态,父类的引用指向子类的对象
Animal b = new Dog();// 多态,父类的引用指向子类的对象
Cat c = new Cat();// 实例化对象
}
}

多态中成员变量的优先级

多态中成员变量的规则: 编译看=的左边(父类),运行看=的左边(父类)
编译: 指就是写代码的过程,如果父类中,没有这个成员变量,于是代码编译就会报错
运行: 指代码在内存中运行时,成员变量,调用的实际上是父类中的成员
(Animal a = new Cat(); 在调用成员变量时,a想象成super)

1
2
3
4
5
6
// 多态中的子类,Cat
public class Cat extends Animal
{
int a = 10;
int b = 77;
}
1
2
3
4
5
// 多态中的父类
public class Animal
{
int a = 99;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 测试多态
public class TestAnimal
{
public static void main(String[] args)
{
// 多态,父类的引用指向子类的对象
// 1. 多态中成员变量的特点,编译看父类,运行看父类
Animal c = new Cat();
System.out.println(c.a);// 99 调用父类的变量a
// System.out.println(c.b); 错误代码,原因是,b变量在Animal类中没有定义

Animal d = new Dog();
}
}

多态中的方法 ⭐

多态中方法的规则: 编译看=的左边(父类),运行看=的右边(子类重写)
编译: 如果父类中,没有这个方法,那么编译就会报错
运行: 运行子类中的重写方法(如果子类中没有重写,调用父类中的方法)

1
2
3
4
5
6
7
8
9
10
11
12
// 多态中的子类,Dog
public class Dog extends Animal
{
@Override
public void eat(){// Dog类重写了父类中的方法
System.out.println("狗吃狗粮");
}
public void drink(){// Dog类中特有的方法drink()
System.out.println("狗喝水");

}
}
1
2
3
4
5
6
7
8
9
10
// 多态中的父类
public class Animal
{
int a = 99;

public void eat(){
System.out.println("动物吃饭");
}

}
1
2
3
4
5
6
7
8
9
10
11
// 测试多态
public class TestAnimal
{
public static void main(String[] args)
{
// 2. 多态中成员方法的特点,编译看父类,运行看子类重写方法
Animal d = new Dog();
d.eat();// 狗吃狗粮
// d.drink(); 编译错误,原因是Animal中没有定义drink()方法
}
}

多态在内存中的运行

多态中的静态方法

多态中静态方法的规则: 编译看=的左边(父类),运行看=的左边(父类)
编译: 如果父类中,没有这个静态方法,那么编译就会报错
运行: 运行父类中的静态方法

原因: 静态属于类,不属于类的任何对象,因此Animal中的静态方法,与Animal等号右边的对象是谁,无关.

1
2
3
4
5
6
7
8
// 多态中的父类
public class Animal
{
public static void fun(){
System.out.println("动物玩");

}
}
1
2
3
4
5
6
7
8
// 多态中的子类,Dog
public class Dog extends Animal
{
public static void fun(){// fun不是方法重写,fun就是当前类Dog中的一个静态方法
System.out.println("Dog在玩");
}

}
1
2
3
4
5
6
7
8
9
10
// 测试多态
public class TestAnimal
{
public static void main(String[] args)
{
// 2. 多态中普通成员方法的特点,编译看父类,运行看子类重写方法
Animal d = new Dog();
d.fun();// 多态中调用的静态方法,为父类的方法,静态只属于类,与对象无关
}
}

多态的转型

向上向下转型

  1. 向上转型
    父类的引用指向子类的对象(Cat是Animal的子类)
    Animal a = new Cat();

    Cat小辈,转换成长辈的应用,年龄大了,向上转型(自动)
    局限: 只能调用子父类中,共有的成员变量或者成员方法

  2. 向下转型
    Cat c = (Cat)父类的引用;// 子类对象 变量名 = (子类对象) 父类变量
    举例: Cat c = (Cat)a;
    现在,由父类的引用转换成了子类自己的对象,c可以调用Cat类中的所有成员(包括与父类相同的方法,也包括子类特有方法)
    Animal长辈,转换成Cat小辈,年龄小了,向下转型(强制类型转换)

举一个比较好理解的栗子:

 我和我的父亲,我是一名java老师,我的父亲也是一名老师,但是父亲讲的是社会学理论
 我和父亲之前存在着子父类关系,我继承了父亲的技能,但是又重写了父亲的技能
 有一天父亲的学生来找父亲去演讲,父亲不在家,我假扮成父亲代父亲去参加演讲,这就是向上转型
我只能使用我和父亲相同的功能,父亲的演讲的技能我不能够使用
 回到家后,我的朋友来找我,想约我去看电影,但是父亲的技能里没有看电影这个技能,然后我就脱掉衣服转成我自己原本的形态这就是向下转型

1
2
3
4
5
6
7
8
9
10
11
// 多态中的子类,Dog
public class Dog extends Animal
{
@Override
public void eat(){// Dog类重写了父类中的方法
System.out.println("狗吃狗粮");
}
public void drink(){// Dog类中特有的方法
System.out.println("狗喝水");
}
}
1
2
3
4
5
6
7
8
// 多态中的父类
public class Animal
{
int a = 99;
public void eat(){
System.out.println("动物吃饭");
}
}
1
2
3
4
5
6
7
8
9
10
11
// 测试多态
public class TestAnimal
{
public static void main(String[] args)
{
// 3. 多态中的向下转型
Dog dog = (Dog)d;
dog.eat();// 调用Dog类中的重写方法,狗吃狗粮
dog.drink();// 调用Dog类中的特有方法,狗喝水
}
}

向下转型的注意事项

多态中进行向下转型之前,先判断,父类的引用是否可以转换成子类的对象

instanceof :
instanceof 用法 : 父类的引用 instanceof 子类的对象
判断这个父类的引用,能否转换成对应的子类
instanceof 结果: boolean 类型,如果返回true,证明可以转型;返回false,不能转型
为什么使用instanceof ,因为向下转型时,如果类型不一致,编译不会报错,运行时会报出异常,运行时期的异常属于代码隐患,需要在编译环节尽量祛除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 测试多态
public class TestAnimal
{
public static void main(String[] args)
{
Animal c = new Cat();
Animal d = new Dog();
// 4. 多态向下转型时的注意事项 instanceof
// Dog dd = new Cat(); 编译错误,因为Cat和Dog之间没有子父类的关系
/*Exception in thread "main" java.lang.ClassCastException: Dog cannot be cast to Cat
Dog无法转换成Cat类型
*/
//Cat cc = (Cat)d;//向下转型,编译没有报错,运行异常

/* instanceof 用法 : 父类的引用 instanceof 子类的对象:
判断这个父类的引用,能否转换成对应的子类
instanceof 返回值结果: boolean , 如果返回true,证明可以转型;返回false,不能转型
*/
if(c instanceof Cat){
Cat cc = (Cat)c;
cc.catchMouse();//猫抓老鼠
}else{
System.out.println("类型不一致,无法转换");
}
}
}

多态的好处

  1. 多态代码的扩展性很好
  2. 定义一个方法时,将方法的参数,设置成一个父类(形式参数),方法实际调用的时候所传的参数,可以是父类类型,也可以是任意的子类类型(多态的使用)
  3. 定义一个方法时,将方法的返回值,设置成一个父类,方法实际返回的时候,可以返回父类类型,也可以是任意的子类类型(多态的使用)

Animal父类 eat();
Cat 子类 重写eat();
Dog子类 重写eat();

定义一个方法:
方法的要求: 传入不同的参数,能让不同的动物吃不同的食物
public void animalEat(Animal a){// 实际调用 new Cat(); new Dog();

a.eat();
}

1
2
3
4
5
6
7
8
// 多态中的子类,Cat
public class Cat extends Animal
{
@Override
public void eat(){// Cat类重写了父类中的方法
System.out.println("猫吃鱼");
}
}
1
2
3
4
5
6
7
8
// 多态中的子类,Dog
public class Dog extends Animal
{
@Override
public void eat(){// Dog类重写了父类中的方法
System.out.println("狗吃狗粮");
}
}
1
2
3
4
5
6
7
// 多态中的父类
public class Animal
{
public void eat(){
System.out.println("动物吃饭");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 多态的好处
public class AnimalEat
{
// 参数,类Animal,Animal是一个父类
/* animalEat功能: 打印动物吃饭的动作,要求,猫吃鱼,狗吃狗粮...,即不同的动物吃不同的东西*/
public void animalEat(Animal a){
// 1. Animal a = new Animal(); 实例化对象
// 2. Animal a = new Cat();多态
// 3. Animal a = new Dog();多态
// 4. Animal a = new Cat();多态

a.eat();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 测试多态
public class TestAnimal
{
public static void main(String[] args)
{
// 5. 多态的好处,扩展性好
AnimalEat ae = new AnimalEat();
Animal ani = new Animal();
Animal ani1 = new Cat();
Animal ani2 = new Dog();
Cat ccc = new Cat();

// animalEat 需要一个Animal类型的参数

ae.animalEat(ani);//动物吃饭
ae.animalEat(ani1);//猫吃鱼
ae.animalEat(ani2);//狗吃狗粮
ae.animalEat(ccc);// 猫吃鱼
}
}

晚安

 今天就到这里了,明天见,加油!

-------------本文结束感谢您的阅读-------------
0%