我有一个关于java中对象引用的概念性问题。
这里Num是一个界面
public interface Num {
void sum();
}
实现Num的Num2
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包含方法 sum 和 getRandom 。但是当我们尝试通过 n 引用访问方法时,我们只能得到 sum 方法。我的问题是指针如何知道Num中包含哪个方法。在对象初始化期间如何以及哪些信息存储在堆栈中以供参考。如果我有任何误解纠正我。
答案 0 :(得分: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();
}
}
答案 1 :(得分:2)
编译器(不在运行时)负责验证您将对象视为Num
而不是Num2
答案 2 :(得分:1)
您只能在编译时访问为变量定义的类型的方法。由于您的n
变量来自Num
类型,因此您只能使用Num
接口中定义的方法。请注意,这些方法的行为将由实际对象引用类型定义,在本例中为Num2
。
答案 3 :(得分:1)
我认为以下内容落后于它(如果我错了,请纠正我):
当你创建一个引用Num n
时,就会在内存中的某个地方创建它的属性。
因此必须定义方法以及可以使用此引用访问的内容。
现在,当您将其引用到对象时,该对象是内存中的单独实体。当您尝试使用引用进行访问时,编译器必须使用引用元数据来确定可以使用该引用调用哪个方法,依此类推。
答案 4 :(得分: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
。
答案 5 :(得分:0)
在java中,当Child extends Parent
(或implements
)并且您编写Parent object = new Child()
时,您已在内存中创建了对Parent
对象的Child
引用。
一旦你的代码被编译, JVM 将处理内存中的对象,它将知道引用变量object
实际上是指一个类型的对象内存中Child
(在您的情况下,n
类型为Num2
)。
但在此之前,您必须处理编译器。 编译器只关心引用的类型,在本例中为Parent
(或在您的情况下为Num
),因此只会让您调用Parent
(Num
)类中声明的方法。
绕过这个的一种方法是进行演员表,如下:
((Num2) n).getRandom();
如果您确定n
确实(或将会)实际指向内存中Num2
类型的对象,请务必执行此操作!否则,您将获得ClassCastException
在这里,你告诉编译器,“相信我。我知道这是一个Num2,所以请把它当成一个。”
总结一下:
Num n = new Num2()
声明一个引用变量并在内存中创建一个Object Num
,而且编译器都知道Num2
,JVM将知道此