• 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 秒) - 正常终止
  • 相关阅读:
    程序员的一天
    美团点评云真机平台实践
    第5波:5本《自动化平台测试开发》+5本《软件自动化测试开发》又双叒叕来送书了...
    软件测试:管理篇
    软件测试:用例篇
    转载ASP.NET MVC 和ASP.NET Web Form简单区别
    转载ASP.NET 状态管理Application,Session,Cookie和ViewState用法
    转载 asp.net中ViewState的用法详解
    在ASP.NET中如何运行后台任务
    转载 .net中的dll.refresh文件和pdb文件
  • 原文地址:https://www.cnblogs.com/weststar/p/12369389.html
Copyright © 2020-2023  润新知