Java 创建对象的引用

Java 创建对象的引用,java,Java,我有一个关于java中对象引用的概念性问题 这里是一个接口 public interface Num { void sum(); } Num2,它实现Num public class Num2 implements Num{ @Override public void sum() { System.out.println(getRandom()+getRandom()); } public int getRandom() {

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

这里是一个接口

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中包含哪些方法。如何以及哪些信息存储在堆栈中以供对象初始化时参考。如果我有任何误解,请纠正我。

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

(不在运行时)负责验证您是否将对象视为
Num
而不是
Num2
您正在将变量
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(); 
   }
}

我认为有以下原因(如果我错了,请纠正我):

当您创建一个引用
Num n
时,就会在内存中的某个地方创建它的属性

因此,它必须定义方法,以及可以通过该引用访问的东西

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

My question is that how can a pointer know which method are contained in Num?
在编译时,它将只检查引用指针调用的函数或方法是否在引用指针类中声明(不一定定义)。在运行时,将以自顶向下的方式解析整个继承树,并选择正确的函数实现

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

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

将打印
类狗
类动物

在java中,当<代码>子对象扩展父对象(或<代码>实现)并编写<代码>父对象=新子对象(),您就创建了对内存中<代码>子对象的<代码>父对象引用。
一旦编译了代码,JVM将处理内存中的对象,它将知道引用变量
object
实际上是指内存中类型为
Child
的对象(在您的情况下,
n
是类型为
Num2
)。

但在此之前,您必须处理编译器。编译器只关心引用的类型,在本例中是
Parent
(或者在您的例子中是
Num
),因此只允许调用在
Parent
Num
)类中声明的方法 解决这个问题的一种方法是做一个演员,就像这样:

((Num2)n).getRandom()

只有当您确信
n
实际上(或将要)指向内存中
Num2
类型的对象时,才能确保执行此操作!否则,您将得到一个
ClassCastException

在这里,您告诉编译器,“相信我,我知道这是一个Num2,所以请像对待Num2一样对待它。”

总而言之:

  • Num n=new Num2()
    声明一个引用变量并在内存中创建一个对象
  • 该变量的类型为
    Num
    ,这是编译器所知道的全部内容
  • 在内存中创建的对象的类型为
    Num2
    ,JVM将知道这一点
  • 要运行JVM,您必须满足编译器的要求
  • 在这种情况下,您可以通过强制转换来满足编译器的要求

编译器如何从引用的角度看待引用?@新Idiot@DiptopolDam正如我在上面强调的JLS一样,编译器检查引用变量的类型,并确定可以使用该引用变量调用哪些方法。@Abu引用的创建和获取属性是否在编译时发生?您能详细描述一下这些分配是如何在编译和运行时发生的吗。