4.7 多态

java引用变量有两个类型:一个是编译型类型,一个是运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就可能出现所谓的多态(Polymorphism).

变态: 同一个类型的实例、在执行同一个方法,个别对象存在着变异的行为特征。
多态:同一个类型的实例、在执行同一个方法,呈现出多种的行为特征。

一、多态性

例子:

 1 class BaseClass 
 2 {
 3     public int book=6;
 4     public void base()
 5     {
 6         System.out.println("父类中的普通方法");
 7     }
 8     public void test()
 9     {
10         System.out.println("父类的被覆盖的方法");
11     }
12 }
13 
14 public class SubClass extends BaseClass
15 {
16     //重新定义一个book实例变量,隐藏了父类的book实例变量
17     public String book="疯狂Java";
18     //重写父类方法
19     public void test()
20     {
21         System.out.println("子类覆盖父类的方法");
22     }
23     public void sub()
24     {
25         System.out.println("子类普通方法");
26     }
27     public static void main(String[] args)
28     {
29         //创建父类对象,下面编译时的类型和运行时完全一样不存在多态
30         BaseClass bc=new BaseClass();
31         System.out.println(bc.book);
32         //调用BaseClass的实例方法
33         bc.base();
34         bc.test();
35 
36         //创建子类对象,下面编译时的类型和运行时完全一样不存在多态
37         SubClass sc=new SubClass();
38         System.out.println(sc.book);
39         //调用SubClass的实例方法
40         sc.base();
41         sc.test();    
42         
43         //下面编译时的类型和运行时完全不一样,多态发生
44         BaseClass polyBc=new SubClass();//子类变量赋给父类变量,小到大,自动转换
45         System.out.println(polyBc.book);//表示访问的是父类对象的实例变量
46 
47         polyBc.base();
48         polyBc.test();
49 
50         //由于polyBc的编译时类型为BaseClass,BaseClass类中没有sub()方法,下面编译将出错
51         //polyBc.sub();
52     }
53 }

运行结果:

---------- 运行Java捕获输出窗 ----------
6
父类中的普通方法
父类的被覆盖的方法
疯狂Java
父类中的普通方法
子类覆盖父类的方法
6
父类中的普通方法
子类覆盖父类的方法

输出完成 (耗时 0 秒) - 正常终止

对于前两个引用变量bc、sc,它们编译时的类型与运行时的类型相同,因此可以正常的访问成员变量和方法。

对于polyBc引用变量,编译时类型为BaseClass,运行时类型为SubClass,当调用test()方法时,实际上是调用的SubClass的test()方法,这就可能出现多态。

▲向上转型:子类是一种特殊的父类,因此子类对象可以直接赋给父类变量
自动完成
▲向下转型:父类变量赋给子类变量
强制转换 (类型)变量名

值得注意的是,对象的实例变量和方法不同,它不具有多态性。换句话说,通过引用变量访问其包含的实例变量时,系统总是试图访问它编译时类型所定义的成员变量,而不是它运行时类型所定义的成员变量。比如上面的polyBc引用变量,访问它的book变量时,输出的并不是SubClass类中的实例变量,而是输出BaseClass类中的实例变量。

二、引用变量的强制转换

  编写Java程序时,引用变量只能调用它编译时的类型的方法,而不能调用它运行时类型的方法,即使它实际所引用的对象确实包含该方法。如果要让这个引用变量调用它运行时类型的方法,则必须把它强制类型转换成运行时类型,强制类型转换需要借助于类型转换符。

类型转换符用法:

1 (type) variable;
2 //将variable变量转换成一个type类型的变量

类型转换符的两个用法:

★将一个基本类型变量转换成另一个类型。

  ▲基本类型之间转换只能在数值类型之间转换,这里数值类型包括整数型、字符型和浮点型。但数值类型和布尔类型之间不能进行转换。

★将一个引用变量转换成其子类类型。

  ▲引用类型之间的转换只能在具有继承关系的两个类型之间进行,如果两个没有任何继承关系的类型,则无法进行类型转换,否则出现编译错误。

 1 public class ConversionTest 
 2 {
 3     public static void main(String[] args) 
 4     {
 5         var d=13.4;
 6         var l=(long)d;
 7         System.out.println(l);
 8         //下面试图将数值类型转换为布尔类型
 9         //var b=(boolean)d;// 错误: 不兼容的类型: double无法转换为boolean
10 
11         Object obj="Hello";
12         //obj的编译类型为Object,运行时类型为String
13         var objStr=(String)obj;
14         System.out.println(objStr);
15         
16         //objPri编译时类型为Object,运行时类型为Integer
17         //Object和Integer存在着继承关系
18         Object objPri=Integer.valueOf(5);
19         
20         //Integer引用变量和String引用变量之间不存在继承关系
21         var str=(String) objPri;
22         //Exception in thread "main" java.lang.ClassCastException: class java.lang.
23         //Integer cannot be cast to class java.lang.String 
24     }
25 }

  考虑到进行强制类型转换时可能出现异常,因此进行类型转换之前先通过instanceof运算符来判断是否可以成功转换。例如上面报错代码可以进行改为:

if(objPri instanceof String)
{
    var str=(String) objPri;  
}

三、instanceof运算符

  instanceof运算符的前一个操作数是一个引用类型变量,后一个操作数通常是一个类(也可以是一个接口,接口可以理解是一种特殊的类),它用于判断前面对象是否是后面的类,或者子类、实现类的实例。如果是返回true,否则返回false。

注意:instance运算符前面操作数的编译类型要么与后面的类型相同,要么与后面的类具有父子继承关系,否则会出现编译错误。

 1 public class InstanceTest
 2 {
 3     public static void main(String[] args)
 4     {
 5         //Object是所有类的父类,但hello的实际类型是String
 6         Object hello="Hello";
 7         System.out.println("字符串是否是Object的实例:"+(hello instanceof Object));
 8         System.out.println("字符串是否是String的实例:"+(hello instanceof String));
 9         System.out.println("字符串是否是Math的实例:"+(hello instanceof Math));
10     }
11 }
12 ---------- 运行Java捕获输出窗 ----------
13 字符串是否是Object的实例:true
14 字符串是否是String的实例:true
15 字符串是否是Math的实例:false
16 
17 输出完成 (耗时 0 秒) - 正常终止

猜你喜欢

转载自www.cnblogs.com/weststar/p/12369389.html
4.7