11

我知道这种行为,但我不能 100% 确定为什么会发生这种情况。
我知道java中的实例变量没有多态性。变量由编译器静态解析。
但是在下面我对一些事情感到困惑:

class Animal{  
   String name = "Animal";  
   public void display(){  
    System.out.println("My name is "+ name);  
   }  
}  

public class Dog extends Animal {   
   String name = "Dog";   

   public static void main(String[] args) {  
        Animal a = new Dog();  
        Dog d = new Dog();  
        System.out.println(a.name);//Line 1  
        a.display();//Line 2   
        d.display();//Line 3  
   }  
}  

我知道Line 1它会显示Animal为静态类型a(由编译器解析)。
让我困惑的是为什么Line 3还会显示My name is Animal
将尝试调用该方法,Dog因为这是运行时的实际对象,并且由于它没有被覆盖,因此将在父类中找到该方法Animal
我不明白的是namedisplay如果实际操作的对象是Dog. 它不会隐藏父母的name变量吗?在我看来,它似乎不是静态解析的,因为类型是Dog. 它不是对象内存布局的一部分吗?
就像里面display只有父变量是可见的。为什么?

更新:

@Razvan 和 @LouisWasserman 的回答很有帮助。
在这些之后我有最后一个问题:
两者的观点似乎如下:
来自@Razyan
System.out.println("My name is "+ this.name); //<-- note the this
来自@Louis
that the refer thisto Animaland that the implementation of display() is in the Animal class

到目前为止还可以。display()但是这些点如何与我修改如下 的事实一致:

class Animal{  
   String name = "Animal";  
   public void display(){  
    System.out.println("Current class is "+ this.getClass().getName());  
    System.out.println("My name is "+ name);  
   }  
}  

那么结果:

 Dog d = new Dog();  
 d.display();  

当前班级是
我的名字是动物

我期待this里面display会是Animal我在这里理解的答案。但事实并非如此。为什么?

4

4 回答 4

13

当您调用d.display()Animal.display()调用时,因为您没有在 Dog 类中覆盖它。

所以一个想象的实现Dog.display()将是这样的:

 public void display(){  
    super.display();
 }

Animal.display() 的实现是:

 public void display(){  
    System.out.println("My name is "+ this.name); //<-- note the this
 } 

Animal.display()方法甚至不知道对象的存在,Dog因此也不知道它的name变量

于 2012-08-20T22:01:54.783 回答
2

这可能是一种更有用的思考方式:变量具有相同名称的事实一点也不重要。此代码的行为

class Animal{  
   String foo = "Animal";  
   public void display(){  
    System.out.println("My name is "+ foo);  
   }  
}  

public class Dog extends Animal {   
   String bar = "Dog";   

   public static void main(String[] args) {  
        Animal a = new Dog();  
        Dog d = new Dog();  
        System.out.println(a.foo);//Line 1  
        a.display();//Line 2   
        d.display();//Line 3  
   }
}

关键是,类中的字段name被视为与中Dog的字段完全分开,就哪些方法可以看到。当你直接引用时,它只知道那是一个,所以它使用类中的字段。nameAnimala.nameaAnimalnameAnimal

于 2012-08-20T22:12:53.113 回答
2

在这里,我将所有其他人的观点总结在一个答案中,以便人们可以毫不费力地清楚地了解它。

从概念上讲,代码可以被认为是

class Animal{  
   String name = "Animal";  
   public void display(){  
    System.out.println("My name is "+ this.name);  
   }  
}  

public class Dog extends Animal {   
   String name = "Dog";  
   public void display(){  
    super.display();
 } 

   public static void main(String[] args) {  
        Animal a = new Dog();  
        Dog d = new Dog();  
        System.out.println(a.name);//Line 1  
        a.display();//Line 2   
        d.display();//Line 3  
   }  
} 

在 Dog 的类中,我们隐藏了 Animal 类的 name 字段,即。Dog 的实例将有两个实例变量,名为name一个是Animal.name(来自保存值“Animal”的继承)第二个是Dog.name(保存值“Dog”)。Dog.name躲在课堂上Animal.name。在第 3 行,在.Dog 的实例上调用 display() 方法不会覆盖 display() 方法,因此最终会调用方法。在此方法中,指的是and ,其中有两个,一个在打印的范围内。(因为作为超类不知道类和)Dog
DogAnimalAnimal.display()thisDogDognameAnimalAnimalDogDog.name

于 2015-09-17T21:55:10.627 回答
1

在调试模式下运行的简单 Junit 测试表明 Dog 对象中确实有两个“名称”变量。现在,回答 OP 最新更新中提出的问题:

 class Animal{  
   String name = "Animal";  
   public void display(){  
    System.out.println("Current class is "+ this.getClass().getName());  
    System.out.println("My name is "+ name);  
   }  
} 

Dog d = new Dog
d.display();

这里,'d' 包含两个变量 -> 一个是继承的,另一个是定义在 dog 类中的变量,两者具有相同的名称。现在,当在 Dog 对象上调用 display() 时,由于它没有覆盖 Animal 类的 display() 方法,JVM 会运行 Animal 类的 display() 方法。

System.out.println("Current class is "+ this.getClass().getName());  

将打印 Dog 因为它是调用该方法的实际对象。

System.out.println("My name is "+ name);  

将打印 'My name is Animal' ,因为如前所述,Dog 的对象中包含两个变量,但是一旦在 Animal.display() 方法中,变量 Animal.name 的值由于范围而被选择。

于 2016-04-01T18:46:43.693 回答