好的,菜鸟问题。我正在为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.返回我;
13.}
14.}
15.雷克萨斯(Lexus)类扩展了汽车{
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方法。事实证明是后者。


2。


1.类StudentProb {
2. private int studentId = 0;
3.无效的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 =新的StudentProb();
11. ob1 = st1;
12. st1.setStudentID(i);
13.}
14.}


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


3。


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


设施是一个接口。 ClassRoom类实现设施,而礼堂和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是一辆汽车,因此它不知道speedUp是有效的函数。

即使您写了:

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


你会得到一个错误。作为阅读代码的人员,您可以轻松地看到mycar必须是雷克萨斯,因此必须是Car,但是编译器只是看到mycar被声明为Object,而Object没有speedUp函数。 (我想,一个足够聪明的编译器可以在这个简单的示例中得出结论,mycar必须是雷克萨斯,但是编译器有时或大部分时间都无法处理可能知道的内容,因此必须处理绝对值。)

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

RE问题3:我知道您在这里感到困惑。您需要将“编译时间”与“运行时间”区分开来。

在对象上执行功能时,将获得该功能的“版本”,该版本特定于该对象的“实际”类。就像你写:

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


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

10-07 20:29