代码之家  ›  专栏  ›  技术社区  ›  Dennis Vash

函数对象的内部类中的变量/对象会发生什么?

  •  3
  • Dennis Vash  · 技术社区  · 8 年前

    我有一个功能 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());
        }
    }
    
    5 回复  |  直到 8 年前
        1
  •  4
  •   Stephen C    8 年前

    在实现级别发生的是 a 保存在 合成实例变量 在的编译版本中声明 C.Inner 班级。

    价值 一 传递给已编译的 Inner 通过一个额外参数的构造函数。

    这个 C.Inner.hashCode 方法使用合成变量的值。访问 一 在源代码中 Inner.hashCode 转换为访问已编译代码中相应的合成变量。

    外部范围中的变量必须是 final 一 . 合成变量必须是 最终的 二 在 内 类。这保持了一种错觉,即 内 班级也看到了同样的情况 一 变量。(它们不是,但由于变量不能更改,所以内部类的代码不可能区分这些差异。)

    如果你使用 javap 要查看编译示例的字节码,您将看到用于在外部类和内部类中实现这一点的机制。


    1或有效的Java 8的最终结果。

    2如果 一 可能被一个 内 方法,然后两个 内 具有相同外部类的实例需要共享一个可变变量,该变量的生存期(现在)比stackframe长 multi2 呼叫。这需要某种方式的转变 一 从堆栈变量转换为生活在堆中的某个对象。这将是昂贵和复杂的。

        2
  •  2
  •   pramesh    8 年前

    您已经定义了 class Inner 在函数内部,因此类的作用域将 方法中的限制为。还有你的 函数是静态的 因此,只要加载类定义,它就会一直存在。您已覆盖 哈希代码 函数在内循环中,因此每次调用 multi2(param) 您正在创建 哈希代码 例如innerClass和返回 内环面 .

    所以对于你的问题,如果我错了请纠正我。

    A发生了什么? a 在静态方法的范围内,因此只要加载类定义,它就一直存在。

    谁分配了? 范围 一 在静态方法中受到限制,静态方法不需要实例来访问它,但是对于静态方法/变量分配,我认为它依赖于JVM。

    我可以访问吗? 不,你不能进入 一 从静态方法的外部来看,它在静态方法中受到限制。

        3
  •  0
  •   Dsenese1    8 年前

    由于“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;
      }
    }
    
        4
  •  0
  •   Clayton    8 年前

    我想知道实际发生了什么,所以我编译了您的代码并查看了字节码输出。

    基本上,编译器会将构造函数添加到类“inner”中。它还向接受“a”的构造函数添加了一个参数。如果multi2()方法不是静态的,那么可能还会有一个参数接受“this”,其中“this”是multi2()正在执行的“c”的实例。但由于我们是在静态环境中,所以没有“this”。

    编译器向类“inner”添加一个私有的final字段,并使用通过构造函数传递的值设置该私有字段。编译器还转换

        new Inner()
    

    进入之内

        new Inner(a)
    

    然后,哈希代码访问包含a的值的私有字段。

    如果“a”是一个对象而不是一个基元,那么它将是相同的方式,但引用将被传递,而不是实际的数值。

    如何访问这个变量?嗯,你可以通过反射来访问它,但是有很多问题:

    1)您不知道编译器生成的字段名,因此只能通过查看字节码来获取该名称。不要信任反编译器,因为它们可能会更改名称。你得自己看看字节码才能知道。

    2)编译器可能会将该字段标记为最终字段,这意味着即使您可以获得反射来访问该字段,也无法对其进行更新。

    3)完全由编译器来确定字段名。根据编译器和它的心情,字段名可以在构建之间更改。

        5
  •  -1
  •   Glains    8 年前

    内心是一种所谓的 本地类 . a 是传递给方法的参数 multi2 并且可以在该范围内访问。在该方法之外,您无法访问 一 .

    推荐文章