2

我有一个关于 java 中对象引用的概念性问题。

这里 Num 是一个接口

public interface Num {
    void sum();
}

Num2 实现了 Num

public class Num2 implements Num{
    @Override
    public void sum() {
        System.out.println(getRandom()+getRandom());
    }

    public int getRandom() {
        Random randomNumber = new Random();
        int number = randomNumber.nextInt(30);
        return number;
    }
}

以及主要功能

Num n = new Num2();
n.sum();

这里我知道n是对象Num2的引用,n是指向对象Num2的指针。Num2 包含方法sumgetRandom。但是当我们尝试通过n引用访问方法时,我们只能得到sum方法。我的问题是指针如何知道 Num 中包含哪些方法。在对象初始化期间如何以及将哪些信息存储在堆栈中以供参考。如果我有任何误解,请纠正我。

4

6 回答 6

3

您将变量定义为接口n类型Num,因此您只能调用在Num. 我相信这个决议是在编译时完成的。编译器通过使用基于类型的引用变量来确定可以访问哪些字段或方法。

但请记住,运行时将调用实际对象类型的方法,即实现接口的类。

类类型 T 的变量可以保存空引用或对类 T 的实例或作为 T 的子类的任何类的实例的引用。

看下面的代码:

interface A {
  void method1();
}
class B implements A {
  public void method1() {
  }
  public void methodB(){
  }
}
class C implements A {
  public void method1() {
  }
  public void methodC(){
  }
}
public class InterfaceTest {
   public void testMethod(A a){
       // this is safe because whatever may be the run time type of actual
       // object `a` is referring to , that object will always implement
       // method1.
      a.method1();
      // this cannot be allowed because compiler doesn't know 
      // what will be the actual run time object `a` will refer to
       // It may or may not be an object of B.
      a.methodB(); 
   }
}
于 2013-07-25T04:46:29.247 回答
2

编译器(不在运行时)负责验证您是否将对象视为 aNum而不是 aNum2

于 2013-07-25T04:45:42.127 回答
1

您只能访问在编译时定义给变量的类型的方法。由于您的n变量来自Num类型,因此您只能使用Num接口中定义的方法。请注意,这些方法的行为将由真实对象引用类型定义,在本例中为Num2.

于 2013-07-25T04:45:03.617 回答
1

我认为后面有以下内容(如果我错了,请纠正我):

当您创建一个引用Num n时,然后在内存中的某个地方使用它的属性创建它。

所以它必须定义方法和可以通过这个引用访问的东西。

现在,当您将它引用到一个对象时,该对象是内存中的一个单独实体。当您尝试使用引用进行访问时,编译器必须使用引用元数据来确定可以使用该引用调用哪个方法等等。

于 2013-07-25T04:49:09.147 回答
1
My question is that how can a pointer know which method are contained in Num?

在编译时它只会检查引用指针调用的函数或方法是否在引用指针的类中声明(不一定定义)。在运行时以自上而下的方式解析整个继承树,并选择正确的函数实现。

您提到的引用指针也在堆栈中,而实际对象在堆中。该对象具有其类信息。让我举个例子-

    Animal animal = new Dog();
    System.out.println(animal.getClass());

将打印class Dog不是 class Animal

于 2013-07-25T04:53:46.050 回答
0

在java中,当Child extends Parent(或implements)你写Parent object = new Child()时,你已经创建了一个对内存中对象的Parent引用。编译代码后,JVM将处理内存中的对象,它会知道引用变量实际上是指内存中类型的对象(在您的情况下,即type )。 但在那之前,你必须处理编译器编译器只关心引用的类型,在这种情况下是(或在你的情况下),因此只会让你调用在( ) 类中声明的方法。Child

objectChildnNum2

ParentNumParentNum

解决此问题的一种方法是进行强制转换,如下所示:

((Num2) n).getRandom();

确保仅在您确定它n(或将)实际指向Num2内存中类型的对象时才这样做!否则,您将获得一个ClassCastException.
在这里你告诉编译器,“相信我。我知道这是一个 Num2,所以把它当作一个。”

总结一下:

  • Num n = new Num2()声明一个引用变量并在内存中创建一个对象
  • 变量是 type Num,这就是编译器所知道的
  • 在内存中创建的对象是类型Num2的,JVM 会知道这一点
  • 要运行 JVM,您必须满足 Compiler
  • 在这种情况下,您可以通过强制转换来满足编译器。
于 2013-07-25T05:00:44.200 回答