多态
来源:互联网 发布:ff14人女捏脸数据 编辑:程序博客网 时间:2024/06/16 19:48
编译时类型和运行时类型不一致,就会出现所谓的多态。
先看下面的程序:
- public class BaseClass {
- public int book = 6;
- public void base(){
- System.out.println("父类的普通方法");
- }
- public void test(){
- System.out.println("父类被覆盖的方法");
- }
- }
- public class SubClass extends BaseClass{
- /** 重新定义一个book实例属性覆盖父类的book实例属性 */
- public String book = "JAVA开发";
- public void sub(){
- System.out.println("子类的普通方法");
- }
- public void test(){
- System.out.println("子类覆盖父类的方法");
- }
- public static void main(String[] args) {
- /** 下面编译时的类型和运行时类型完全一样,因此不存在多态 */
- BaseClass bc = new BaseClass();
- System.out.println(bc.book); //6
- bc.base();
- bc.test();
- /** 下面编译时的类型和运行时类型完全一样,因此不存在多态 */
- SubClass sb = new SubClass();
- System.out.println(sb.book);
- sb.sub();
- sb.test();
- /** 下面编译时的类型和运行时类型完全不一样,因此不存在多态 */
- BaseClass ploymophicBc = new SubClass();
- //下面输出6 --表明访问的是父类属性
- System.out.println(ploymophicBc.book);
- //下面执行从父类继承到的base方法
- ploymophicBc.base();
- //下面将执行当前类的test方法
- ploymophicBc.test();
- //因为ploymophicBc的编译类型是BaseClass,BaseClass类型没有提供sub方法,所以下面会出现编译错误
- // ploymophicBc.sub();
- }
- }
上面程序的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异常。
下面是进行强制类型转换的示例程序,下面程序详细说明了哪些情况可以进行强制类型转换,哪些情况不可以进行强制类型
转换。
- public class TestConversion {
- public static void main(String[] args) {
- double d = 3.14;
- long l = (long)d;
- System.out.println(l);
- int i = 5;
- //下面代码出现编译错误,试图把一个数值类型转换成boolean类型
- //boolean b = (boolean)i;
- //obj变量的编译类型是Object,是String类型的父类,可以进行强制类型转换
- //而且obj变量实际上类型是String类型,所以运行时也可以通过
- Object obj = "hello";
- String objStr = (String)obj;
- System.out.println(objStr);
- //定义一个objPri变量,编译类型是Object,运行类型是Integer
- Object objPri = new Integer(5);
- /**
- * objPri的编译类型是Object,是String类型的父类,可以进行强制类型转换
- * 而objPri变量实际上类型是Integer类型,
- * 所以下面的代码会报ClassCastException异常,编译时是不会报错的
- */
- String str = (String)objPri;
- //下面这样进行强制类型转换就不会报异常,因为objPri的实际类型是Integer类型
- Integer a = (Integer)objPri;
- System.out.println(a);
- }
- }
考虑到进行强制类型转换时可能会出现异常,因此进行强制类型转换之前应该先通过instanceof运算符来判断是否可以成功
转换,例如上面的String str = (String)objPri;代码运行时会引发ClassCastException异常,这是因为objPri不可转换
成String类型,为了让程序更加健壮,可以将代码改为如下
- if(objPri instanceof String){
- String str = (String)objPri;
- }
父类。这种转型只是表面这个引用变量的编译类型是父类,但实际执行它的方法时,依然表现的是子类对象的行为方式。但
把一个父类对象赋给子类引用变量时,就需要强制类型转换,而且还可能在运行时产生ClassCastException,使用instanceof
运算符可以让强制类型转换更安全。