我有一个函数multi2
,它将内部类Inner
作为Object
返回。
a
会发生什么-保存在哪里以及如何访问它?
public class C {
private static Object multi2(final int a) {
class Inner {
public int hashCode() {
return 2*a;
}
}
return new Inner(); // What happens to a?
// Who allocates a?
// Can I Access a?
}
public static void main(String[] args) {
Object o = multi2(6);
System.out.println("o.hashCode() = " + o.hashCode());
o = multi2(4);
System.out.println("o.hashCode() = " + o.hashCode());
}
}
答案 0 :(得分:4)
在实现级别发生的事情是,将a
的值的副本保存在C.Inner
类的编译版本中声明的综合实例变量中。
a
的值通过一个额外的参数传递给已编译的Inner
构造函数。
C.Inner.hashCode
方法使用综合变量的值。在a
的源代码中访问Inner.hashCode
转换为在编译后的代码中访问相应的综合变量。
外部作用域中的变量必须为final
1 。在final
类中,合成变量必须为Inner
2 。这维持了这样的错觉:Inner
类的多个实例(可能)正在看到相同的a
变量。 (它们不是,但是由于不能更改变量,因此内部类的代码无法分辨出区别。)
如果使用javap
查看已编译示例的字节码,则将在外部类和内部类中看到用于实现此目的的机制。
1-或从Java 8开始有效的最终版本。
2-如果a
可以通过Inner
方法进行突变,则具有相同外部类的两个Inner
实例需要共享一个生存期为(现在)比multi2
调用的堆栈帧长。这需要以某种方式将a
从堆栈变量转换为驻留在堆中的内容。这将是昂贵和复杂的。
答案 1 :(得分:2)
您已经在函数内部定义了class Inner
,因此该类的范围将是
在方法中受限制。而且您的函数是静态的,因此只要加载了类定义,它就将处于活动状态。您已覆盖了InnerClass内部的 hashCode 函数,因此,每次调用multi2(param)
时,您都将为InnerClass实例创建 hashCode 并返回该实例内部类。
关于您的问题,如果我错了,请纠正我。
一个会发生什么?
a
在您的静态方法范围内,因此只要加载了类定义,它就将处于活动状态。
谁分配一个?
a
的范围被限制在静态方法内部,并且静态方法不需要实例来访问它,但是对于静态方法/变量分配,我认为它取决于JVM。
我可以访问吗?
不,您不能从外部的静态方法访问a
,它受您的静态方法的限制。
答案 2 :(得分:0)
由于“ a”是局部参数,您可以使用其他方法来读取“ a”值:
public class C {
public static Object multi2(final int a) {
return new Inner(a);
}
public static void main(String[] args) {
Object o = multi2(6);
System.out.println("o.hashCode() = " + o.hashCode());
System.out.println("o.getA() = " + ((Inner) o).getA());
o = multi2(4);
System.out.println("o.hashCode() = " + o.hashCode());
System.out.println("o.getA() = " + ((Inner) o).getA());
}
}
class Inner{
public int valueA;
public Inner(int a)
{
valueA = a;
}
public int getA() {
return valueA;
}
public int hashCode() {
return 2*valueA;
}
}
答案 3 :(得分:0)
我想知道实际发生了什么,所以我编译了您的代码并查看了字节码输出。
基本上,编译器会向您的“ Inner”类添加一个构造函数。它还向采用“ a”的构造函数添加单个参数。如果您的multi2()方法不是静态的,那么可能还会有一个参数采用“ this”,其中“ this”是multi2()在其上执行的“ C”的实例。但是,因为我们处于静态环境中,所以没有“ this”。
编译器将一个私有的final字段添加到类“ Inner”中,并使用通过构造函数传递的值来设置该私有字段。编译器还会转换
new Inner()
进入
new Inner(a)
然后,哈希码访问包含a值的私有字段。
如果“ a”是对象而不是原始对象,则将采用相同的方式,但是将通过引用而不是实际的数字值。
如何访问此变量?好吧,您可以通过反思来访问它,但是有很多问题:
1)您不知道编译器生成的字段的名称,因此只能通过查看字节码来获取名称。不要信任反编译器,因为它们可能会更改名称。您必须自己查看字节码才能发现。
2)编译器可能会将字段标记为最终字段,这意味着即使您可以通过反射来访问该字段,也无法对其进行更新。
3)完全由编译器来确定字段名称。字段名称可能会在版本之间更改,具体取决于编译器及其心情。
答案 4 :(得分:-1)
Inner是所谓的 local class 。 a
是传递给方法multi2
的参数,可在该范围内访问。在该方法之外,您无法访问a
。