程序员面试必考题(二十九)--Java中转换对象的基本概念


问题:给出以下三个类的定义:


class parentclass {}

class subclass1 extendsparentclass {}

class subclass2 extendsparentclass {}


并分别定义三个对象:


parentclass a = newparentclass ();

subclass1 b = newsubclass1();

subclass2 c = new subclass2();


当执行下面的语句:


a = b;

b = a;

b = (subclass1)c;

时,会有什么结果?分别从下面的选项中选择正确的答案。


(1) 编译时出错。

(2) 编译时正确,但执行时出错。

(3) 执行时完全正确。


解:三行类定义分别定义了三个类,一个父类parentclass及它的两个子类subclass1和subclass2。后面的三行则分别为每个类说明了一个实例,其中a是parentclass类的实例,b是subclass1类的实例,c是subclass2类的实例。由于subclass1和subclass2都是派生于parentclass的子类,所以b和c也同时是parentclass类的实例。


Java中允许使用对象之父类类型的一个变量指示该对象,称为转换对象(casting)。关于转换对象的使用,遵从对象引用的赋值兼容原则。所谓对象引用的赋值兼容原则是指允许把子类的实例赋给父类的引用,但不允许把父类的实例赋给子类的引用。实际编程时,可以使用instanceof运算符来判明一个引用指向的是哪个类的实例。如果父类的引用指向的是子类实例,就可以转换该引用,恢复对象的全部功能。


本题中,可以进行下面的测试:


boolean tagb1 = b instanceofparentclass;

boolean tagc1 = cinstanceof parentclass;


tagb1和tagc1的值都是true,表明它们是子类实例的同时,也是父类的实例。反过来,父类的实例不是子类的实例,例如下面的测试:


boolean taga2 = ainstanceof subclass1;

boolean taga3 = ainstanceof subclass2;


taga2和taga3的值都是false。b和c是不同子类的实例,所以如下的测试:


boolean tagb3 = binstanceof subclass2;

boolean tagc2 = cinstanceof subclass1;


将出现编译错误。下面针对题目中的三条语句分别进行测试。


(1) 执行a = b;时,a指向父类的实例,b指向子类的实例。由于是将子类实例赋给父类实例,因此编译及执行都是正确的。该语句将执行子类中的方法,如果子类中没有重写父类中的方法,则将执行父类中的方法。例如下面的测试程序中,父类和子类中都定义了value成员和getValue()方法,将子类的实例赋给父类引用后,此时a的值是子类的实例,再执行语句:


taga2 = a instanceofsubclass1;


则taga2的值应为true。在给a分配的内存中既包括子类中value的值,也含有父类中value的值。调用a.getValue()方法时,先在子类中查找这个方法是否存在,如果有,则返回子类中value的值1;若没有,则查找父类中的同名方法,并返回父类中的值0。


测试代码如下:


import java.util.*;

 

class ParentClass

{    public ParentClass() //父类的构造方法

     {    value = 0;

     }

     public int getValue()  //父类的求值方法,返回父类中value的值

     {    return value;

     }

     public void setValue(int y) //给父类的属性value赋值

     {    value = y;

     }

 

     private int value;

}

class SubClass1 extends ParentClass

{    public SubClass1() //子类1的构造方法,value值为1

     {    value = 1;

     }

     public int getValue() 

     //子类的同名求值方法,返回子类1中value的值

     {    return value;

     }

     public int getClassValue1() 

     //子类的特殊求值方法,返回classvalue1的值

     {    return classvalue1;

     }

     private int value;

     private int classvalue1 = 11;

}

class SubClass2 extends ParentClass

{    public SubClass2() 

//子类2的构造方法,value值为2

     {    value = 2;

     }

     public int getValue()  

//子类的同名求值方法,返回子类2中value的值

     {    return value;

     }

     public int getClassValue2() 

//子类的特殊求值方法,返回classvalue2的值

     {    return classvalue2;

     }

     private int value;

     private int classvalue2 = 22;

}

 

public class Test2

{    public static void main(String[] args)

     {    ParentClass a = new ParentClass();  // 父类实例

           SubClass1 b = new SubClass1();   // 子类1的实例

           SubClass2 c = new SubClass2();   // 子类2的实例

    

           a = b;   

           //a指向子类1的实例

           System.out.println("a = " + a.getValue()); 

   // 返回子类1中的属性值

     }

}

程序的执行结果如下:

a = 1

 

(2) 执行b = a;时,由于是将父类的实例赋给子类的变量,因此会出现编译错误,错误类型是变量类型不匹配。

 

(3) 执行b = (subclass1)c;时,由于b和c是不同类的实例,因此也会出现编译错误,错误类型是变量类型不能转换。

 

总结:一般地,要转换对象引用时须做下列检查:


  • 沿类层次向“上”转换总是合法的,例如,把 subclass1 引用转换为 parentclass 引用。实际上此种方式下不需要转换运算符,只用简单的赋值语句就可完成。

  • 对于向“下”替换,只能是父类到子类转换,其他类之间是不允许的。例如,把 subclass1 引用转换为 subclass2 引用肯定是非法的,因为 subclass2 不是 subclass1 。这两个类之间没有继承关系。要替换的类(赋值号右侧)必须是当前引用类型(赋值号左侧)的某个子类。

  • 编译器检查正确后,需在运算时检查引用类型。如果源程序中忘记进行 instanceof 检查,要替换的对象不是目标类型的对象,则引发一个异常。

《横扫offer---程序员招聘真题详解700题》,开点工作室著,清华大学出版社出版,天猫、京东等各大网上书店及实体书店均已开始发售。
全部评论

相关推荐

10-17 23:18
已编辑
西北农林科技大学 Web前端
独行m:给25可以试试,但他只能给12,那就是纯纯的事精
秋招,不懂就问
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务