java多态性浅析

什么是多态?

所谓多态,是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。

多态性的描述形式:

多态性严格来讲,有两种描述形式:

一、方法的多态性:

① 方法的重载:同一个方法名称,会根据传入参数的类型及个数不同执行不同的方法体;

② 方法的覆写: 同一个方法,会根据子类的不同,实现不同的功能。

二、对象的多态性(指的是发生在继承关系之中,子类和父类之间转换问题)

① 向上转型(自动完成):父类 父类对象 = 子类实例  <new 实现>

② 向下转型(强制完成):子类 子类对象 = (子类)父类实例 <new 实现>

    如何记住:向上转型,向上肯定是子类实例向父类转,所以左边是父类 右边是子类实例;

向下转型,是父类向子类转,所以左边是子类,右边是父类及强制转换

三、转型的意义:

① 向上转型的真正意义在哪呢?

由于所有的子类都可以向上转型为其父类,一个父类可以有很多的子类。而且其操作形式都一样,所以其真正意义在于参数的统一化

如:

class A
{
	public void print(){
		System.out.println("A、print()");
	}
	public void fun(){
		System.out.println("MM");
	}
	protected String getName(){
		return "Aha";
	}
	int getAge(){
		return 12;
	}
}
class B extends A{
	public void print(){
		System.out.println("B、print()");
	}
}
class C extends A{
	public void print(){
		System.out.println("C、print()");
	}
}
public class Test{
	public static void main(String[] args){
		A a1 = new B();  //向上转型为父类,所以对象只能调用父类中存在的public和protected修饰的成员,以及default类型成员;
						 //但是如果被调用的函数被子类覆写,就调用覆写后的函数
		A a2 = new C();  //向上转型
		a1.print();  
		a2.print();		 //向上转型可以调用不同子类的覆写函数,使得同一函数可以有不同的实现;
//所以,我们可以利用值传递(java也就只有值传递)的方法,创建一个函数,形参为A a,
//然后在main函数中使用不同的子类实例对象作为实参传给形参这样就实现了参数的统一化管理了
	}
}

由上面可知,发生向上转型,则对象不能调用子类中的特殊方法(没有覆写父类同名函数的方法),但是向下转型(将父类对象转化为子类对象)可以满足这个需求。

下面来讲讲向下转型:

class M
{
	public void print(){
		System.out.println("M、print()");
	}
}
class N extends M{
	public void print(){
		System.out.println("N、print()");
	}
	public void funN(){
		System.out.println("执行N中的特殊方法");
	}
}
class P extends M{
	public void print(){
		System.out.println("P、print()");
	}
}
public class Testt{
	public static void main(String[] args){
	    A a = new B();
		fun(new N());
		B b = (B) a; //向下转型
		b.funb(); 
	}
	public static void fun(M m){ //统一参数
		m.print();  
		N n = (N) m;
		n.funN();
	}
}

但是以上代码如果不使用向上转型,而是直接使用子类对象进行实例化后也可以调用子类B中的特殊方法
     为什么还要向下转型呢?

因为在某些情况下,虽然达到的效果是一样的,但是向下转型更带有一定的目的性:调用子类中的特殊方法

对于对象的转型,我们给出总结:
        80%的情况下都只会使用向上转型,因为可以得到参数类型的统一,方便于我们的程序设计
        5%的情况下会使用向下转型,目的是调用子类的特殊方法
        15%的情况下不会转型,例如:String
        在标准的开发之中,子类中扩充的方法应该尽量少出现,因为对象的转型操作里面毕竟有了强制问题,容易带来安全隐患

向下转型为什么会带来安全隐患呢?

我们看以下代码:

class A
{
	public void print(){
		System.out.println("A、print()");
	}
}
class B extends A
{
	public void print(){
		System.out.println("B、print()");
	}
}
public class TestDemo
{
	public static void main(String[] args) {
		A a = new A();  
		B b = (B) a;  
/*
由于发生向下转型,是明确了子类和父类是哪一个的;此行代码说明了B是对象a的子类,同时对于A而言,A是B的父类,也就是知己知彼;
但是对于A而言,作为父类是不知道谁它的子类的,因为它只是使用了A a = new A(),并不涉及其他类,也就是与外界隔绝了联系,
外界的事情它都不清楚,所以会发生编译错误
*/
		b.print();	
	}
}

编译错误信息如下:


        此时出现的异常(解释见代码注释)(ClassCastException)表示的是类转换异常,指的是两个没有关系的类对象强制发生向下转型时发生向下转型时所带来的异常;
        所以向下转型是会存在风险的。

为了保证转型的顺利进行,在Java里面提供有一个关键字:instanceof。此关键字的用法是:对象 instanceof 类/接口,返回boolean型,如果前面的对象是后面的类、或者是后面的 类的子类、(接口或抽象类)的实现类(implements)的实例(对象的具体例子),那么就返回true,否则就返回false


所以在向下转型之前,我们可以先判断对象a是不是B的实例,即

if(a instanceof B)  //如果A a = new B()成立
    B b = (B) a; 

要注意:我们说猫是动物,相当于把猫赋予了动物的这个名词,就相当于猫=动物;那么,如果a对象是B类的实例 ,即 A a = new B()成立;

我们完善代码如下:

class A
{
	public void print(){
		System.out.println("A、print()");
	}
}
class B extends A
{
	public void print(){
		System.out.println("B、print()");
	}
}
public class TestDemo
{
	public static void main(String[] args) {
		A a = new B();   
		System.out.println(a instanceof A);  //true
		System.out.println(a instanceof B);  //true
		if(a instanceof B){ 
			B b = (B) a; 
			b.print(); 
		}
	}
}

所以,在发生向下转型之前,一定要首先发生对象的向上转型,建立关系后才可以进行。

猜你喜欢

转载自blog.csdn.net/DYD850804/article/details/81076794