---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
面向对象编程有三个特征,即封装、继承和多态。
封装隐藏了类的内部实现机制,从而可以在不影响使用者的前提下改变类的内部结构,同时保护数据。
继承是为了重用父类代码,同时为实现多态性做准备。
下面我们介绍多态。
多态的体现:
父类型的引用可以指向子类型的对象。如,
Parent p=new Child(); //Child类继承了Parent类
大大提高程序的扩展性。因为java只允许单继承,这样虽然保证了继承关系的简单明了,但也势必在功能上有很大的限制。多态的引入用于弥补这点不足,另外,抽象类和接口也是解决单继承规定限制的重要手段。
多态的弊端:
虽然提高了扩展性,但是只能使用父类的引用访问父类中的成员。
下面用例子来讲解多态中几个重要的概念
例1:
public class PolyDemo
{
public static void main(String[] args)
{
Animal a1=new Cat(); //自动向上类型转换
Animal a2=new Dog();
//调用子父类共有方法
a1.eat(); //动态绑定
a2.eat();
//调用子类特有方法
//a1.catchMouse(); //错误,父类中没有catchMouse()方法
Cat c=(Cat)a1; //强制向下类型转换
Dog d=(Dog)a2;
c.catchMouse();
d.guardHouse();
//Animal a3 = new animal();
//Cat c2 = (Cat)a3; //错误,不能将父类对象转成子类类型
}
}
class Animal
{
public void eat()
{
System.out.println("Animal is eating!");
}
}
class Dog extends Animal
{
public void eat()
{
System.out.println("Dog is eating!"); //Dog类重写了eat()方法
}
public void guardHouse() //Dog类特有的方法
{
System.out.println("Dog is guarding house!");
}
}
class Cat extends Animal
{
public void eat()
{
System.out.println("Cat is eating!");
}
public void catchMouse()
{
System.out.println("Cat is catching mouse!");
}
}
输出结果:
Cat is eating!
Dog is eating!
Cat is catching mouse!
Dog is guarding house!
由此例,我们讲解以下几个概念:
1、类型转换
1)自动向上类型转换(Upcast):将子类型转为父类型
对于向上的类型转换,不需要显示指定转换类型,类型自动提升。
2)强制向下类型转换(Downcast):将父类型转化为子类型
对于向下的类型转换,必须要显示指定转换类型,即必须使用强制类型转换。
注意:由上例可知,我们能转换的是父类引用指向子类对象时,该引用既可以被提升,也可以被强制转换。多态自始至终都是子类对象在做着变化。
2、多态时成员的特点
1)成员函数:
当父类引用指向子类对象时,该引用变量可以调用子父类共有的成员方法,而不能调用子类中定义但父类中没有的成员方法(否则会编译失败)。
同时,对于父类中定义的成员方法,如果子类重写了该方法,那么父类引用将会调用子类中的该方法,而这就是动态绑定。
简单总结:成员方法在多态调用时,编译时期参阅引用变量所属类中是否有调用的方法,如果有则通过,否则编译失败;在运行时期参阅被引用对象所属的类中的成员方法。
2)成员变量:
无论编译时期还是运行时期,都参阅引用变量所属类中的成员变量。
3)静态成员方法:
无论编译时期还是运行时期,都参阅引用变量所属类中的静态成员方法。
下面我们再来看一个例子,以便更好的理解java的多态性
例2:
class PolyDemo
{
public static void main(String[] args)
{
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println(a1.show(b));
System.out.println(a1.show(c));
System.out.println(a1.show(d));
System.out.println(a2.show(b));
System.out.println(a2.show(c));
System.out.println(a2.show(d));
System.out.println(b.show(b));
System.out.println(b.show(c));
System.out.println(b.show(d));
}
}
class A
{
public String show(D obj)
{
return ("A and D");
}
public String show(A obj)
{
return ("A and A");
}
}
class B extends A
{
public String show(B obj)
{
return ("B and B");
}
public String show(A obj)
{
return ("B and A");
}
}
class C extends B{}
class D extends B{}
输出结果:
A and A
A and A
A and D
B and A
B and A
A and D
B and B
B and B
A and D
在分析结果前,我们先讲一下 方法调用的优先问题,优先级由高到低依次为:
this.show(O)>super.show(O)>this.show((super)O)>super.show((super)O)
现在我们来分析结果。1~3的结果应该很好理解。
以4为例,引用变量为a2,类型是A,则this是a2。于是它先在类A中寻找show(B obj)方法,未找到;然后到A的super类(父类)中找,然而A没有父类;于是它转到第三优先级this.show((super)obj),此时this为a2,而O(即B)的父类为A,故在A中寻找show(A obj),类A有此方法。但因为a2引用的是B的一个对象b,而B中重写了A的show(A obj)方法,根据动态绑定,故最终调用的是B中的show(A obj)方法,输出“B and A”。
例5~9同理可得。
下面讲一些多态的应用
需求:电脑运行实例
/*未考虑多态的思考方式(不好)*/
class MainBoard
{
public void run()
{
System.out.println("主板运行!");
}
public void useNetCard(NetCard c) //加载一个网卡,则添加一段调用网卡的代码;若还要加载声卡等,
{ //则再添加调用声卡的代码。扩展性极差。
c.open();
c.close();
}
}
class NetCard
{
public void open()
{
System.out.println("网卡打开!");
}
public void close()
{
System.out.println("网卡关闭!");
}
}
/*考虑多态的思考方式*/
class MainBoard
{
public void run()
{
System.out.println("主板运行!");
}
public void usePCI(PCI p)
{
if (p!=null)
{
p.open();
p.close();
}
}
}
interface PCI
{
public abstract void open();
public abstract void close();
}
class NetCard implements PCI
{
public void open()
{
System.out.println("网卡打开!");
}
public void close()
{
System.out.println("网卡关闭!");
}
}
---------------------- ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------详细请查看:www.itheima.com