多态

来源:互联网 发布:ff14人女捏脸数据 编辑:程序博客网 时间:2024/06/16 19:48
Java引用变量有两个类型:一个是编译时的类型,一个是运行时的类型,编译时的类型由声明该变量时使用的类型决定。如果
编译时类型和运行时类型不一致,就会出现所谓的多态。

先看下面的程序:

[java] view plaincopy
  1. public class BaseClass {  
  2.   
  3.    
  4.       
  5.     public int book = 6;  
  6.       
  7.     public void base(){  
  8.         System.out.println("父类的普通方法");  
  9.     }  
  10.       
  11.     public void test(){  
  12.         System.out.println("父类被覆盖的方法");  
  13.     }  
  14.       
  15. }  

[java] view plaincopy
  1. public class SubClass extends BaseClass{  
  2.   
  3.     /** 重新定义一个book实例属性覆盖父类的book实例属性 */  
  4.     public String book = "JAVA开发";  
  5.   
  6.     public void sub(){  
  7.         System.out.println("子类的普通方法");  
  8.     }  
  9.       
  10.     public void test(){  
  11.         System.out.println("子类覆盖父类的方法");  
  12.     }  
  13.   
  14.     public static void main(String[] args) {  
  15.           
  16.         /**  下面编译时的类型和运行时类型完全一样,因此不存在多态 */  
  17.         BaseClass bc = new BaseClass();  
  18.         System.out.println(bc.book);  //6  
  19.         bc.base();  
  20.         bc.test();  
  21.           
  22.         /**  下面编译时的类型和运行时类型完全一样,因此不存在多态 */  
  23.         SubClass sb = new SubClass();  
  24.         System.out.println(sb.book);  
  25.         sb.sub();  
  26.         sb.test();  
  27.           
  28.         /**  下面编译时的类型和运行时类型完全不一样,因此不存在多态 */  
  29.         BaseClass ploymophicBc = new SubClass();  
  30.         //下面输出6  --表明访问的是父类属性  
  31.         System.out.println(ploymophicBc.book);  
  32.         //下面执行从父类继承到的base方法  
  33.         ploymophicBc.base();  
  34.         //下面将执行当前类的test方法  
  35.         ploymophicBc.test();  
  36.         //因为ploymophicBc的编译类型是BaseClass,BaseClass类型没有提供sub方法,所以下面会出现编译错误  
  37. //      ploymophicBc.sub();  
  38.           
  39.     }     
  40.       
  41. }  


上面程序的main方法中显示创建了3个引用变量,对于前两个引用变量bc和sb,他们编译时类型和运行时类型完全相同,因此
调用他们的属性和方法很正常,完全没有问题。但第三个引用变量ploymophicBc比较特殊,它的编译类型是BaseClass,而
运行时类型是SubClass,当调用该引用变量的test方法(BaseClass和SubClass中都定义了此方法),实际执行的SubClass
类中覆盖后的test方法,这就是多态。

因为子类其实是一种特殊的父类,因此Java允许把一个子类对象直接赋给父类引用变量,无须任何类型转换,或者成为向上
转型,向上转型由系统自动完成。

当把一个子类对象直接赋给父类引用变量,象上面的ploymophicBc引用变量编译时的类型是SubClass,而运行时的类型是
SubClass。当运行时调用该引用变量的方法时,其方法行为总是像子类方法的行为,而不是像父类方法的行为。

引用变量在编译阶段只能用其编译时类型所具有的方法,但运行时则执行它运行时类型所具有的方法。因此,编写Java代码
时,引用变量只能调用声明该变量时所用类里包含的方法。例如我们通过Object p = new Person();变量p只能调用Object
类的方法,而不能调用Person类里定义的方法。

与方法不同的是,对象的属性不具有多态性:如上面的ploymophicBc引用变量,程序中输出它的book属性时,并不是输出
SubClass类里定义的属性,而是输出BaseClass属性里的实例属性。


引用变量的强制类型转换

编写Java程序时,引用变量只能调用它编译时的类型的方法,而不能调用它运行时类型的方法,即使它实际引用对象确实包含
该方法。如果需要让这个引用变量来调用它运行时类型的方法,则必须把它强制类型转换成运行时类型,强制类型转换需要
借助于类型转换运算符。
类型转换运算符是小括号,类型转换运算符的用法如下:(type)variable,这种用法将variable变量转换成一个type类型的
变量。
类型转换运算符可以讲一个基本类型变量转换成另一个类型。除此之外,这个类型转换运算符还可以将一个引用类型转换成
其子类类型。这种强制类型转换不是万能的,当进行强制类型转换时需要注意:

== 基本类型之间的转换只能在数值类型之间转换,这里的数值类型包括整数型、字符型、和浮点型。但数值型不能和布尔
类型之间进行类型转换。
== 引用类型之间的转换只能把一个父类变量转换成子类类型,如果是两个没有任何继承关系的类型,则无法进行类型转换
,否则编译时就会出现错误。如果试图把一个父类实例转换成子类类型,则必须这个对象实际上是子类类型才行(即编译时
类型为父类类型,而运行时是子类类型),否则将在运行时引发ClassCastException异常。

下面是进行强制类型转换的示例程序,下面程序详细说明了哪些情况可以进行强制类型转换,哪些情况不可以进行强制类型
转换。

[java] view plaincopy
  1. public class TestConversion {  
  2.   
  3.     public static void main(String[] args) {  
  4.         double d = 3.14;  
  5.         long l = (long)d;  
  6.         System.out.println(l);  
  7.           
  8.         int i = 5;  
  9.         //下面代码出现编译错误,试图把一个数值类型转换成boolean类型  
  10.         //boolean b = (boolean)i;  
  11.           
  12.         //obj变量的编译类型是Object,是String类型的父类,可以进行强制类型转换  
  13.         //而且obj变量实际上类型是String类型,所以运行时也可以通过  
  14.         Object obj = "hello";  
  15.         String objStr = (String)obj;  
  16.         System.out.println(objStr);  
  17.           
  18.         //定义一个objPri变量,编译类型是Object,运行类型是Integer  
  19.         Object objPri = new Integer(5);  
  20.         /** 
  21.          * objPri的编译类型是Object,是String类型的父类,可以进行强制类型转换 
  22.          * 而objPri变量实际上类型是Integer类型, 
  23.          * 所以下面的代码会报ClassCastException异常,编译时是不会报错的 
  24.          */  
  25.         String str = (String)objPri;  
  26.         //下面这样进行强制类型转换就不会报异常,因为objPri的实际类型是Integer类型  
  27.         Integer a = (Integer)objPri;  
  28.         System.out.println(a);  
  29.     }  
  30. }  

考虑到进行强制类型转换时可能会出现异常,因此进行强制类型转换之前应该先通过instanceof运算符来判断是否可以成功
转换,例如上面的String str = (String)objPri;代码运行时会引发ClassCastException异常,这是因为objPri不可转换
成String类型,为了让程序更加健壮,可以将代码改为如下

[java] view plaincopy
  1. if(objPri instanceof String){  
  2.     String str = (String)objPri;  
  3. }  
当把子类对象赋给父类引用变量时,被成为向上转型,这种转型总是可以成功的,这也从另一个侧面证实子类是一种特殊的
父类。这种转型只是表面这个引用变量的编译类型是父类,但实际执行它的方法时,依然表现的是子类对象的行为方式。但
把一个父类对象赋给子类引用变量时,就需要强制类型转换,而且还可能在运行时产生ClassCastException,使用instanceof
运算符可以让强制类型转换更安全。
0 0
原创粉丝点击