您的位置:首页 > 编程语言 > Java开发

Java中的多态性

2016-05-12 15:58 357 查看
Java有三大特性:封装、继承和多态

所谓多态就是指有多个状态,比如“食物”这个概念,它可以是肉、鱼和蔬菜,因此“食物”具有多态性。在程序的运行时有多个状态,也就是父类的引用变量被哪个子类所实例化,这就是向上转型。多态实现的三个必要条件:继承、重写、向上转型。

继承:继承就是一个父类有多个子类,比如一个Food类可以有Meet类和Fish类。

重写:重写很关键,就是子类中必须重写父类的方法,这样在向上转型后的调用中才能调用不同子类中的方法。

向上转型:父类的引用变量被子类所实例化,如Food f = new Meet。向上转型会造成一些方法和属性的丢失,他可以调用所有父类中的的属性和方法,但不能调用只存在于子类中的类型和方法。

【1】多态的使用规则

【2】基于继承的多态

【3】基于接口的多态

【4】多态的扩展

多态的使用规则

子类的父类引用向上转型,只能访问父类的属性和方法,不能访问只存在于子类的方法,重载也不可以,必须重写才可以调用子类中方法和属性,(重载是返回相同,参数个数不同。重写是返回和参数都相同)也就是子类中重写了父类中的方法时,调用时肯定调用子类中的方法。

基于继承的多态

基于继承的多态表现为一个或者多个子类对父类方法的重写,子类对象向上转型之后可以调用子类中这些重写的方法。下面是基于继承的例子:

package TwoWeek;

public class Extend1{
public static void main(String args[]){
Food f1 = new Food();
f1.Eat();
Food f2 = new Meet();
f2.Eat();
Food f3 = new Fish();
f3.Eat();
//f3.HaveBone();// 使用Food中没有的方法会出错
}
}

class Food{
Food(){
System.out.println("这是Food的构造方法");
}
public void Eat(){
System.out.println("吃食物");
}
}

class Meet extends Food{
public void Eat(){
System.out.println("我们逮肉");
}
}

class Fish extends Food{
public void Eat(){
System.out.println("我们吃鱼");
}

public void HaveBone(){
System.out.println("鱼是有刺的");
}
}


输出结果为:

这是Food的构造方法
吃食物
这是Food的构造方法
我们逮肉
这是Food的构造方法
我们吃鱼


在向上转型的之后,调用各自子类中的Eat()方法。

基于接口的多态

接口的多态和集成的多态是相似的,用几个不同的类来实现接口并覆盖接口中的同一方法,继承只有单继承,而接口不同,可以实现多继承,所以其有更高的灵活性。

package TwoWeek;

public class Extend2{
public static void main(String args[]){
Meet1 f2 = new Meet1();
f2.Eat();
Fish1 f3 = new Fish1();
f3.Eat();
f3.HaveBone();// 我们发现这个是可以不会报错的
}
}

interface Food1{
public void Eat();
}

class Meet1 implements Food1{
public void Eat(){
System.out.println("我们逮肉");
}
}

class Fish1 implements Food1{
public void Eat(){
System.out.println("我们吃鱼");
}

public void HaveBone(){
System.out.println("鱼是有刺的");
}
}


输出结果为:

<span style="font-size:18px;">这是Food的构造方法
吃食物
这是Food的构造方法
我们逮肉
这是Food的构造方法
我们吃鱼</span><span style="color:#3333ff;font-size:24px; font-weight: bold;">
</span>


多态的扩展
当父类对象的引用变量引用子类对象时,被应用对象而不是引用变量决定了调用谁的成员方法,但是这边被调用的方法是在子类中定义过的,也就是被子类重写的方法,根据调用链中调用方法的优先级来确认方法。下面是引用自大神博客http://blog.csdn.net/chenssy/article/details/12786385的经典继承例子:

public class A {
public String show(D obj) {
return ("A and D");
}

public String show(A obj) {
return ("A and A");
}

}

public class B extends A{
public String show(B obj){
return ("B and B");
}

public String show(A obj){
return ("B and A");
}
}

public class C extends B{

}

public class D extends B{

}

public class Test {
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("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));
System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));
System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
}
输出结果为:

1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and B
9--A and D
确实是从第四个开始就迷糊了,下面来捋一捋。

下面我们来分析第四个a2.show(b),其中a2是A类型的引用变量,也就是父类中的引用变量,引用的是B类型的对象,也就是应该调用b类中的show(B obj)方法,出现"B and B"的结果,当不能忘了一件事,该show()方法必须在类中也存在且被子类覆写才可以,但是父类中没有存在。那就只能按照继承链总的的调用优先级来执行了,优先级是这样的(this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O))所有才会在A类中找到show(A
obj),又因为B类中覆写了show(A obj),所以最终调用B类中的方法。

在分析一下第五个,便于加深理解,a2.show(c),a2是A类型,所以this = A,因为引用的也是B类型对象,所以首先也在B里边找,没有找到。然后到this也就是A里边找,发现又没有找到,然后按照规定就到A的super类里边找,但是A没有超类,那就按照第三继承规则this.show((super)O),C的超类为A,且this是A,所以a.show(a),有因为B中覆写了它,所以结果还是B
and A.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: