2010-09-01 54 views
1

好的,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.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成为Object,而不是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:代码格式化对我来说不起作用。随意编辑。

+0

练习1:编译器是否会在问题1的第18行的'Lexus#speedUp()'上接受'@ Override'注释? – trashgod 2010-09-01 17:08:59

回答

2

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

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

基本上,把它想象成每个对象都有一个隐藏变量来保存它自己的类类型,这就是它用来找到要执行的函数的东西。

在编译时,编译器不知道任何给定对象的类。喜欢如果你写:

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

编译器不知道汽车这里是​​雷克萨斯还是本田还是什么。它只知道它是一辆汽车,因为它不知道这个功能在哪里或如何被调用。直到运行时才会知道实际的汽车类型。

这方面的一个含义是,如果你试着写:

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

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

即使你写道:

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

你会得到一个错误。作为一个人阅读代码,你可以很容易地看到mycar必须是雷克萨斯,因此是汽车,但编译器只是看到mycar被声明为Object,而Object没有加速功能。 (我想,一个足够聪明的编译器可以找出这个微不足道的例子,即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的事情。在编译时,所有编译器都知道是保存引用的变量的类型。这可以与对象的“真实”类相同,或者它可以是任何直到对象的超类。在上述两种情况下,它都是Car。由于Car定义了speedUp函数,因此对car1.speedUp的调用是合法的。但是这里有一个启发:在编译时,Java知道Car有一个speedUp函数,所以对car对象调用speedUp是合法的。但它不知道或关心你会得到哪些speedUp功能。也就是说,当你说car2.speedUp时,Java知道car2是Car,因为这是声明的类型。它不知道 - 记住我们在编译时说,而不是在运行时 - 它不知道它是雷克萨斯还是Chyrsler,只是它是一辆汽车。直到运行时,它才知道它是哪种类型的汽车。

+0

因此,您提到的“真实类型”是该对象首先声明的类?无论您将对象重新分配给其他类型的其他对象的频率如何,这将始终使用这个类? – 2010-09-01 19:09:54

+0

另外,请帮我澄清一下:你说: “如果你写”Car mycar = new Lexus(); mycar.speedUp();“执行什么仍然是Lexus.speedUp” 因此,一直留在mycar中的类是“new”关键字之后的类。 – 2010-09-01 19:12:27

+0

但是你说: 对象mycar = new Lexus(); mycar.speedUp(1); “编译器只是看到mycar被声明为对象,而Object没有加速功能” mycar在这里被声明为雷克萨斯就像前面的引用,但现在编译器(或JVM?)不会不知道那是雷克萨斯。我能看到的两个引号的唯一区别在于第一种情况是Car car mycar,第二种是Object mycar。这是如何改变的?谢谢! PS:对不起,由于字符限制,不得不分手评论。 – 2010-09-01 19:13:20

1

对象始终保持不变。如果将其分配给其他类型的其他变量,则不能访问特殊成员而不将其重新转换为其原始类型。

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

2

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

在你的第3段,五号线是无效的,因为设施是礼堂的超类,所以你可以将一个礼堂实例分配给一个设施变量,而不是相反。

+0

所以这是正确的:ob1包含对st1的引用,所以ob1指向一个StudentProb,但不会使ob1成为StudentProb - ob1仍然只是一个Object?那么转换的效果是什么 - 它只是将ob1中的引用更改为指向st1?如果我使用明确的铸造,例如ob1 =(StudentProb)st1? ob1实际上是一个StudentProb,那么我能否调用ob1.setStudentID? 谢谢! – 2010-09-01 16:57:02

+0

ob1始终是'StudentProb'实例,但您只能以Object的形式迭代它(例如调用'toString()','hashCode()'等)。您不会更改实例的类,只有你如何引用它作为一个变量。 – 2010-09-01 17:13:46

+0

因此,一旦ob1被声明为一个对象,没有办法让它ob1.setStudentID运行? – 2010-09-01 19:01:32

1

这可以帮助我 - 它可能不会帮助你,但我会把它扔在那里。

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

例如,如果您采用String并将其转换为Object,它不会成为对象 - 但它会穿着对象的衣服。您不能在对象上调用.length,因为该方法不存在于“对象”的衣服中,但是如果您调用.equals或。hashCode会得到与你在字符串上调用它们时相同的确切答案,因为它们通过衣服调用底层对象。

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

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

你穿的衣服与对象的操作无关,只与外界相互作用/看对象有关。

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

String s=new Object(); 

由于对象不能填写字符串的西装,字符串的诉讼中是否有“长度”和“追加”,而对象根本无法填补许多其他方法孔 - 就像有人没有手试图戴手套。

0

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

相关问题