目录

什么是多态呢?

多态的条件

演示说明

多态中成员访问的特点

成员变量

成员方法

多态的好处:

多态的缺点:

强制类型转换


什么是多态呢?

俗话说,龙生九子,各不相同。龙的九子都继承自龙,九子却有着各自的特点。在Java中,多态是指不同类的对象在调用同一个方法时,所呈现出的多种不同的行为。

例:

我们可以说猫是猫:Cat c = new Cat();

也可以说猫是动物:Animal a = new Cat();

在这里,猫在不同的时刻展现出不同的形态(猫也是猫,动物也是猫),这就叫做多态。

多态的条件

1.有继承或实现关系

        class Cat extends Animal

2.有方法重写

        @Override

3.有父类引用指向子类对象

        Animal a = Cat();

演示说明

如果只用概念来描述多态,就会不太好理解,下面我们就用代码来演示一下

下面有三段代码

1.父类Animal类

public class Animal {
	int age = 10;
	public void eat(){
		System.out.println("动物吃东西");
	}
}

2.子类Cat类 (继承关系、方法重写)

public class Cat extends Animal {
	int age = 20;
	int weight = 30;
	@Override
	public void eat() {
		System.out.println("猫吃鱼");
	}
    public void shout() {
		System.out.println("猫喵喵叫");
	}
}

3.测试类(父类引用指向子类对象)

public class Text {
	public static void main(String[] args) {
		Animal a = new Cat();
	}
}

像这样的特点,就叫多态。

多态中成员访问的特点

那多态又有什么不一样呢?

首先来看一下多态中成员变量的访问特点

成员变量

在测试类中,已经有一个父类引用指向子类对象,也就是说,对象的类型是父类类型,但是对象是子类的对象。那我们就用这个对象来调用一下成员变量,看看会发生什么。

Java多态的实现机制是什么知乎 java的多态的实现_开发语言

当我们去访问子类中成员变量时,发现weight居然报错了,这就说明,weight是通不过编译的。我们先暂时将weight成员变量注释掉,继续运行下去,看看会发生什么。

Java多态的实现机制是什么知乎 java的多态的实现_java_02

运行结果为10,也就是说age的变量为10。

但是在子类中age的变量是20,父类中的变量才是10。说明多态中成员变量的访问特点为:访问父类中的成员变量。那么刚才weight成员变量报错的原因,也就可以说得通了。多态中访问的是父类中的成员变量,而父类中并没有成员变量weight,所以才会报错。

小口诀

编译看左边,运行看左边

 左边指的是父类,编译时看左边有无对应的成员变量,运行的结果也是父类中的成员变量。

成员方法

 同样的,用这个对象来调用子类中的两个方法,一个子类中重写父类的eat方法,一个子类中特有的shout方法。

Java多态的实现机制是什么知乎 java的多态的实现_Java多态的实现机制是什么知乎_03

 我们发现,子类中特有的shout方法报错了。我们先暂时将shout方法注释掉,继续运行下去,看看运行的结果会是什么。

Java多态的实现机制是什么知乎 java的多态的实现_java_04

 这次的运行结果跟成员变量的运行结果不同。调用的成员方法是子类中的成员方法,而不是父类中的成员方法。

这说明多态中成员方法的访问特点为:访问子类中的成员方法。

小口诀

编译看左边,运行看右边 

 左边指的是父类,编译时成员方法如果在父类中找不到,就会报错。运行时的结果是右边子类中重写的成员方法。

成员变量和成员方法访问特点不同的原因:

成员方法可以重写,但是成员变量不可以重写。

多态的好处:

多态可以消除类之间的耦合关系,大大提高了程序的可扩展性和可维护性。

这样说会比较抽象,下面用一个例子来体现多态的好处

我们来新建一个这样的类

public class AnimalUse {
	public void animalUse(Cat c) {
		c.eat();
	}
}

这个类中提供了一个animalUse方法,需要传进去一个对象,然后就可以调出这个对象的eat方法

 再新建一个Dog类

public class Dog extends Animal{
	@Override
	public void eat() {
		System.out.println("狗吃肉");
	}
}

我们在不用多态的情况下完成以下操作:

以下为测试类:

public class Text {
	public static void main(String[] args) {
		AnimalUse an = new AnimalUse();
		Cat c = new Cat(); 
		an.animalUse(c);
	}
}

运行后即可调用Cat中的eat方法。

Java多态的实现机制是什么知乎 java的多态的实现_开发语言_05

 如果我还想调用一个Dog类中的eat方法,那么就要在AnimalUse类中,再增添一个方法,参数为Dog,如下所示

public class AnimalUse {
	public void animalUse(Cat c) {
		c.eat();
	}
	public void animalUse(Dog d){
		d.eat();
	}

 利用方法重载的方式,再写一个方法,在测试类中,再new一个Dog类的对象

 

Java多态的实现机制是什么知乎 java的多态的实现_java_06

 成功运行。

虽然运行起来没毛病,但是如果还要再添加其他类的话,需要修改的地方就会很多。

再来用多态来修改一下这些代码

public class AnimalUse {
	public void animalUse(Animal a) {
		a.eat();
	}
}

AnimalUse类我们来这样修改,将参数改为父类类型。那么我们以后要添加类,无论是Pig还是Duck还是Chicken,只要是继承自Animal类,都可以通过多态的方式,将参数传入animalUse方法。我们不修改测试类,再来运行一遍。

Java多态的实现机制是什么知乎 java的多态的实现_多态_07

运行成功。

那么Cat型的对象为什么可以填入Animal型方法参数呢?

这个地方做了个自动的类型提升

public class Text {
	public static void main(String[] args) {
		byte a=2;
		fun(a);
	}
	public static void fun(int a){
		a=a+1;
		System.out.println(a);
	}
}

在这里,byte型的变量也可以放入int型的参数里,这里就做了个自动的类型提升,将byte提升为int型。上面同理,Cat型继承自Animal,Cat型的对象放入Animal型的参数里,也会做一个自动类型提升。

回到整体,为了规范,我们在测试类中的类型也要修改,修改为父类引用指向子类对象。

Java多态的实现机制是什么知乎 java的多态的实现_Java多态的实现机制是什么知乎_08

 运行成功。

多态的缺点:

多态的缺点也非常明显,就是不能调用子类特有的方法。

如果想调用子类的shout方法(子类特有的方法),那我们应该如何调用呢?

Cat c = new Cat();
c.shout();

创建一个Cat类对象,这样当然是可以的,但我们要在这里提到的是,多态中的类型转换。

在我们的整数类型中,如果要将10转为byte型要怎么做呢?

byte b = (byte)10;

强制类型转换

在多态中,也是可以使用强制类型转换的。

我们在测试类中这样修改

public class Text {
	public static void main(String[] args) {
		AnimalUse an = new AnimalUse();
		
		Animal a1 = new Cat(); 
		Cat c = (Cat)a1;
		c.shout();
	}
}

Java多态的实现机制是什么知乎 java的多态的实现_父类_09

 子类中特有的shout方法调用成功。