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

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

    1。
 1.类CodeWalkFour {
 2. public static void main(String [] args){
 3. Car c = new Lexus();
 4. System.out.print(c.speedUp(30)+“”);
 5.雷克萨斯l =新雷克萨斯();
 6. System.out.print(l.speedUp(30,40,50));
 7.}
 8.}
 9.车级{
 10. private int i = 0;
 11. int speedUp(int x){
 12.返回i;
 13.}
 14.}
 15.雷克萨斯扩展汽车{
 16. private int j = 1;
 17. private int k = 2;
 18. int speedUp(int y){
 19.返回j;
 20.}
 21. int speedUp(int ... z){
 22.返回k;
 23.}
 24.}

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

    2。
 1. class StudentProb {
 2. private int studentId = 0;
 3. void setStudentID(int sid){
 4. student_id = sid;
 5. System.out.println(“学生ID已设置为”+ sid);
 6.}
 7. public static void main(String args []){
 8. int i = 420;
 9.对象ob1;
 10. StudentProb st1 = new StudentProb();
 11. ob1 = st1;
 12. st1.setStudentID(i);
 13.}
 14.}

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

    3。
 1. LectureHall lh = new LectureHall();
 2.礼堂a1;
 3.设施f1;
 4。
 5. f1 = lh;
 6. a1 = f1;

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

谢谢大家!

PS:代码格式化不适合我。 随意编辑。

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

也就是说,以你的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必须是雷克萨斯,但是编译器无法处理它有时或大部分时间可能知道的事情,它必须处理绝对值。)

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

问题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是合法的。 但是它不知道或关心你将获得哪种speedUpfunction。 也就是说,当你说car2.speedUp时,Java知道car2是Car,因为那是声明的类型。 它不知道 – 记得我们在编译时说,而不是在运行时 – 它不知道它是雷克萨斯还是Chyrsler,只是它是一辆汽车。 直到运行时它才知道它是哪种类型的汽车。

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

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

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

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

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

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

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

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

您可以稍后再将对象装扮成String。

你穿的衣服与物品的操作无关,只与外界如何与物体相互作用/观察物体有关。

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

 String s=new Object(); 

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

记住一件事。 子类引用不能包含超类对象。 class A{} class B extends A{}在这里你可以创建A a=new A(); A a=new B();B b=new B() A a=new A(); A a=new B();B b=new B()但是你不能创建B b=new A()