超类引用和子类对象

如你所知,Java是一种类型严格的语言.除了用于基本类型的标准转换和自动升级,类型兼容也是被严格执行的.因此,一个类类型的引用变量通常不能引用一个其他类型的对象.例如,考虑下面的程序:
public class test2
{
    // @param args
    public static void main(String args[])
    {
     X x = new X(10);
     X x2;
     Y y = new Y(5);
     
     x2 = x;//ok, both of same type
     x2 = y;//error not of same type
    }
}
class X
{
    int a;
    X(int i){ a = i;}
}
class Y
{
    int a;
    Y(int i){ a = i;}
}
这里,尽管X类和Y类本质上是一样的,但是不能把一个Y对象赋值给一个X引用,因为他们的类型不同.总之,对象引用变量只能引用自己类型的对象.
然而,Java的严格类型却有一个例外,即可以把对超类派生子类对象的引用赋给一个超类的引用变量.换句话说,超类引用可以引用子类对象,如下所示:
public class test2
{
    // @param args
    public static void main(String args[])
    {
     X x = new X(10);
     X x2;
     Y y = new Y(5);
     
     x2 = x;//ok, both of same type
     x2 = y;//still ok,because y is derived form  x
     // x2.b = 1; // error x doesn't have a b member
    }
}
class X
{
    int a;
    X(int i){ a = i;}
}
class Y extends X
{
    int b;
    Y(int i)
    {
        super(i);
        b = i;
    }
}
这里,X派生了Y,所以允许赋给x2一个对Y对象的引用.
哪些成员可以访问是由引用变量的类型(而不是他引用的对象类型)决定的,理解这一点十分重要.也就是说,当一个子类对象的引用被赋给一个超类引用变量时,只有权访问对象的那些超类定义的部分.这就是为什么即使x2引用了一个Y对象后,还不能访问b的原因.考虑一下就会发现这是合理的,因为超类不知道子类增加了什么,这也就是程序中代码的最后一行被注释调的原因.
尽管前面的讨论看似深奥,但却有一些重要的实际用途.我们这里就其中一点进行介绍,其他的将在后面设计方法重写时讨论.
把子类引用赋给超类变量常用于在类层次结构中调用构造函数时.如你所知,对于类而言,定义一个把类的对象作为形参的构造函数是很常见的.这就允许类构造对象的副本.这种类的子类就可以利用这一功能,例如,考虑下面的X和Y.这两个类都添加了把对象作为形参的构造函数.
public class test2
{
    // @param args
    public static void main(String args[])
    {
     Y one = new Y(10);
     Y two = new Y(one);
     
     System.out.println("one.a="+one.a+"  one.b="+one.b);
     System.out.println("two.a="+two.a+"  two.b="+two.b);
     
    }
}
class X
{
    int a;
    X(int i){ a = i;}
    X(X obj)
    {
        a = obj.a;
    }
}
class Y extends X
{
    int b;
    Y(int i)
    {
        super(i);
        b = i;
    }
    
    Y(Y obj)
    {
        super(obj.a);
        b = obj.b;
    }
}
输出:
one.a=10 one.b=10
two.a=10 two.b=10
该程序中,通过one构造了two,因此他们是一样的.