铸造后目标对象采用什么类?

时间:2010-09-01 16:35:06

标签: java inheritance interface casting scjp

好的,noob问题。我正在为SCJP学习并得到3个关于对象引用错误的问题,这些问题似乎都表明了同样的误解。只想确认正确的见解应该是什么。对,这是问题:

    1。
    1. class CodeWalkFour {
    2.    public static void main(String[] args){
    3.       Car c = new Lexus();
    4.       System.out.print(c.speedUp(30) + " ");
    5.       Lexus l = new Lexus();
    6.       System.out.print(l.speedUp(30, 40, 50));
    7.     }
    8.  }
    9.  class Car {
    10.     private int i=0;
    11.    int speedUp(int x){
    12.        return i;
    13.    }
    14. }
    15. class Lexus extends Car {
    16.     private int j = 1;
    17.     private int k = 2;
    18.       int speedUp(int y){
    19.       return j;
    20.     }
    21.     int speedUp(int... z){
    22.         return k;
    23.      }
    24.  }
    

    我认为在第3行之后,c将是Car而不是Lexus,因此将调用Car.speedUp方法,而不是Lexus.speedUp方法。事实证明,后者被称为。

      2。
      1. class StudentProb {
      2.   private int studentId  =  0;
      3.   void setStudentID(int sid) {
      4.      student_id = sid;
      5.      System.out.println("Student ID has been set to " + sid);
      6.   }
      7.   public static void main(String args[]) {
      8.       int i = 420;
      9.       Object ob1;
      10.      StudentProb st1 = new StudentProb();
      11.       ob1 = st1;
      12.       st1.setStudentID(i);
      13.   }
      14. }
      

      同样的问题。我认为第11行会使st1成为一个对象,而不再是一个StudentProb。编译器如何知道在哪里找到setStudentID?

        3。
        1.    LectureHall lh = new LectureHall();
        2.   Auditorium a1;
        3.   Facilities f1;
        4.
        5.    f1 = lh;
        6.    a1 = f1;
        

        设施是一个界面。 ClassRoom类实现了Facilities,Auditorium和LectureHall是ClassRoom的子类。同样的问题:我想在第5行之后,f1和lh都是LectureHall。但是f1仍然是设施。那么铸造到底在做什么呢?

        全部谢谢!

        PS:代码格式化对我来说不起作用。随意编辑。

5 个答案:

答案 0 :(得分:2)

对象始终是特定类的实例,您可以使用任何超类引用实例,但实例不会更改。我认为第二个剪辑最好地说明了这一点,你不能写ob1.setStudentID(i);因为ob1是一个Object变量,即使实际的类是StudentProb

在您的第3个片段中,第5行无效,因为Facilities是Auditorium的超类,因此您可以将Auditorium实例分配给Facilities变量,但不能反过来。

答案 1 :(得分:2)

在运行时,每个对象都知道它自己的类是什么,也就是它实际创建的类。它可以分配给该类的变量或任何超类。当你执行一个函数时,你得到该对象被创建的类的“版本”,而不是那个持有对象引用的变量被声明为的类。

也就是说,以你的Car / Lexus为例。如果你写“Lexus mycar = new Lexus(); mycar.speedUp();”,执行的是Lexus.speedUp,而不是Car.speedUp。也许这很明显。但即使你写“Car mycar = new Lexus(); mycar.speedUp();”执行的仍然是Lexus.speedUp,因为那是实际对象的类。您可以将对象重新分配给不同类的不同变量,对象仍然知道它的“真实”类。

基本上,只需将其视为具有隐藏变量的每个对象,该隐藏变量拥有自己的类类型,这就是它用于查找要执行的函数的内容。

在COMPILE时,编译器不知道任何给定对象的类。就像你写的那样:

void speed1(Car somecar)
{
  somecar.speedUp(1);
}

编译器不知道这里的Car是雷克萨斯还是本田或者是什么。它只知道它是一辆汽车,因为它不知道这个函数的调用位置和方式。直到运行时才会知道实际的汽车类型。

这意味着如果你试着写:

void speed1(Object somecar)
{
    somecar.speedUp(1);
}

编译器会给出错误。它无法知道Object是Car,因此它不知道speedUp是一个有效的函数。

即使你写了:

Object mycar=new Lexus();
mycar.speedUp(1);

你会收到一个错误。作为人类阅读代码,您可以很容易地看到mycar必须是雷克萨斯,因此是汽车,但编译器只是看到mycar被声明为Object,而Object没有speedUp函数。 (我认为,一个聪明的编译器可以在这个简单的例子中弄清楚mycar必须是雷克萨斯,但是编译器无法处理它有时或者大多数时候可能知道的事情,它必须处理绝对值。)

编辑:在评论中回答问题。

RE问题3:我看到你在这里感到困惑的地方。您需要将COMPILE TIME与RUNTIME区分开来。

当您在对象上执行函数时,您将获得该特定于该对象的“真实”类的该函数的“版本”。就像你写的那样:

Car car1=new Lexus();
Car car2=new Chrysler(); // assuming you defined this, of course
car1.speedUp(1);  // executes Lexus.speedUp
car2.speedUp(2);  // executes Chrysler.speedUp

但这是一个RUNTIME的事情。在COMPILE时,所有编译器都知道是保存引用的变量的类型。这可以与对象的“真实”类相同,也可以是对象的任何超类。在上面两种情况下,都是Car。由于Car定义了speedUp函数,因此调用car1.speedUp是合法的。但这里是踢球者:在编译时,Java知道Car有一个speedUp函数,所以对Car对象调用speedUp是合法的。但是它不知道或关心你将获得什么speedUp功能。也就是说,当你说car2.speedUp时,Java知道car2是Car,因为那是声明的类型。它不知道 - 记得我们在编译时说,而不是在运行时 - 它不知道它是雷克萨斯还是Chyrsler,只是它是一辆汽车。直到运行时它才知道它是哪种类型的汽车。

答案 2 :(得分:1)

对象始终保持不变。如果将其分配给其他类型的其他变量,则无法在不将其转换回原始类型的情况下访问特殊成员。

您只能访问正在使用的变量类型的成员和方法。但是,此变量引用的对象可以具有更专用的类型。该对象存储自己的类型,因此运行时能够识别其真实类型。

答案 3 :(得分:1)

这对我很有帮助 - 它可能对你没有帮助,但我会把它扔出去。

可视化将对象投射为在该对象上放置新衣服。它有不同的外观,但在你穿上它的任何衣服下面,它仍然是同一个物体。

例如,如果你取一个String并将它强制转换为Object,它就不会成为一个对象 - 但它会穿一件物体的衣服。你不能在一个对象上调用.length,因为该方法在“对象”的衣服中不存在,但是如果你调用.equals或.hashCode,你会得到与你在字符串上调用它们时相同的答案。他们把衣服叫做底层物品。

衣服只是隐藏了班级无法使用的方法。 Object o = new String();在新衣服下隐藏String中但不是Object的所有方法,使其看起来像一个Object。

您可以稍后将对象装扮成字符串。

你穿的衣服与物品的操作无关,只与外界交互/观察物体的方式有关。

这可能是类比较远,但也要注意,如果你的“衣服”有你方体中不存在的方法,试穿这些衣服没有任何意义,所以你可以不做:

String s=new Object();

因为一个对象无法填写String的套装,所以String的套装上有“长度”和“附加”的洞以及对象根本无法填充的许多其他方法 - 就像没有人试图戴手套的人一样。 / p>

答案 4 :(得分:0)

还要记住一件事。子类引用不能包含超类对象。class A{} class B extends A{}您可以在此处创建A a=new A(); A a=new B();B b=new B()但是您无法创建B b=new A()