Java之多态


1. 思考

描述狗

class Dog 
{
	public void eat()
	{
		System.out.println("骨头");
	}
	public void lookHome()
	{
		System.out.println("看家");
	}
}

实现狗eat功能

// 直接new就可以了
class DuoTaiDemo
{
	public static void main(String[] args) 
	{
		Dog d = new Dog();
		d.eat();
	}

那如果有很多狗呢?每次都要d.eat(),怎么做比较合适呢?

//接收Dog,让dog做事。
public static void method(Dog d)
{
    d.eat();
}

接下来又来了个猫,描述猫

class Cat 
{
	public void eat()
	{
		System.out.println("鱼");
	}
	public void catchMouse()
	{
		System.out.println("抓老鼠");
	}
}

如果让猫也吃饭呢?同样也可以如此

//接收Cat,让cat做事。
public static void method(Cat c)
{
    c.eat();
}

发现,每多一个动物,都需要为这个动物单独定义一个功能,让这个动物的对象去做事。这个程序扩展性就很差。如何提高这个扩展性呢?发现既然是让动作去eat,无论是dog,还是cat,eat是它们共性,干脆,将eat进行抽取。抽取到父类Animal中。

//进行抽取。将共性的功能抽取到父类Animal中。
//由于怎么吃不知道,应该定义为抽象方法
abstract class Animal
{
	public abstract void eat();
}

class Dog extends Animal 
{
	public void eat()
	{
		System.out.println("骨头");
	}
	public void lookHome()
	{
		System.out.println("看家");
	}
}

class Cat extends Animal 
{
	public void eat()
	{
		System.out.println("鱼");
	}
	public void catchMouse()
	{
		System.out.println("抓老鼠");
	}
}

那怎么解决上面的问题呢?

//只要建立animal的引用就可以接收所有的dog cat对象进来。让它们去eat。
//提高了程序的扩展性。
public static void method(Animal a)
{
    a.eat();
}
Dog是Animal中的一种。
Dog d = new Dog();
Animal a = new Dog();
Cat c = new Cat();
Animal aa = new Cat();

这就是多态。多态技术的引出。解决什么问题?程序扩展性的问题。

2. 什么是多态

2.1 体现

父类的引用或者接口的引用指向了自己的子类对象。

Dog d = new Dog(); //Dog对象的类型是Dog类型。
Animal a = new Dog(); //Dog对象的类型右边是Dog类型,左边Animal类型。
2.2 好处

提高了程序的扩展性。

2.3 弊端

通过父类引用操作子类对象时,只能使用父类中已有的方法,不能操作子类特有的方法。

2.4 使用的前提
  • 必须有关系:继承,实现。
  • 通常都有重写操作。
2.5 子类的特有方法如何调用呢?
Animal a = new Dog(); //Animal是父类型,new Dog()是子对象。

但是父类型引用指向子类对象时,这就是让子类对象进行了类型的提升(向上转型)。

向上转型好处:

提高了扩展性,隐藏了子类型。

弊端:

不能使用子类型的特有方法。
如果要想使用子类的特有方法,只有子类型可以用。可以向下转型,强制转换。

Animal a = new Dog();
a.eat();
Dog d = (Dog)a; //将a转型为Dog类型。向下转型。
d.lookHome();
2.6 向下转型什么时候用?

当需要使用子类型的特有内容时。

注意:无论向上还是向下转型,最终都是子类对象做着类型的变化。

2.7 向下转型的注意事项
Animal a = new Dog();
//Cat c = (Cat)a; 向下转型因为不明确具体子类对象类型,所以容易引发ClassCastException异常。所以为了避免这个问题,需要在向下转型前,做类型的判断。
判断类型用的是关键字 instanceof
if(a instanceof Cat) //a指向的对象的类型是Cat类型。
{
	//将a转型Cat类型。
	Cat c = (Cat)a;
	c.catchMouse();
}
else if(a instanceof Dog)
{
	Dog d = (Dog)a;
	d.lookHome();
}
2.6 向上转型什么时候用?

提高程序的扩展性,不关系子类型(子类型被隐藏)。

3. 多态中,成员调用的特点

3.1 成员变量。

当子父类中出现同名的成员变量时。

多态调用该变量时:
    编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。
    运行时期:也是调用引用型变量所属的类中的成员变量。
简单记:编译和运行都参考等号的左边。(编译运行看左边。)

参考:

class Fu
{
	int num = 3;
}
class Zi extends Fu
{
	int num = 5;
}
class DuoTaiDemo3 
{
	public static void main(String[] args) 
	{
		//测试成员变量的多态调用。
		Fu f = new Zi();
		System.out.println(f.num); //3
		Zi z = new Zi();
		System.out.println(z.num); //5
	}
}

运行结果:

在这里插入图片描述

3.2 成员函数。
编译,参考左边,如果没有,编译失败。
运行,参考右边的对象所属的类。
简单记:编译看左边,运行看右边。

参考:

class Fu
{
	void show()
	{
		System.out.println("fu show run");
	}
}
class Zi extends Fu
{
	void show()
	{
		System.out.println("zi show run..");
	}
}
class DuoTaiDemo3 
{
	public static void main(String[] args) 
	{
		//测试成员函数的多态调用。
		Fu f = new Zi();
		f.show();
	}
}

运行结果:

在这里插入图片描述
对于成员函数是动态绑定到对象上。

3.3 静态函数。

编译和运行都参考左边。

静态函数是静态的绑定到类上。

参考:

class Fu
{
	static void method()
	{
		System.out.println("fu static method run");
	}
}
class Zi extends Fu
{
	static void method()
	{
		System.out.println("zi static method run");
	}
}

class DuoTaiDemo3 
{
	public static void main(String[] args) 
	{
		//测试静态函数的多态调用。
		Fu f = new Zi();
		f.method();
		// 注意:真正开发静态方法是不会被多态调用的,因为静态方法不所属于对象,而是所属于类。
		Fu.method();
		Zi.method();
	}
}

运行结果:

在这里插入图片描述
结论:

  • 对于成员变量和静态函数,编译和运行都看左边。
  • 对于成员函数,编译看左边,运行看右边。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值