常量和内部类

时间:2013-08-09 22:22:38

标签: java constants inner-classes

    内部类中的
  1. static个变量: 内部类不能包含static个字段。它不能包含static个成员,因为在哪里分配static成员会有问题。内部类与外部类相关联。我理解为什么它不包含static成员,但内部类可以包含static常量。为什么?它是否经过特殊处理?它是在特殊堆上吗?它仍然是静态成员,但是不变,所以它是经过特殊处理的吗?

    它可以包含:“final static int x”,但不包含“static int x”。

  2. 我想知道为什么本地类中使用的方法的变量应该是final。我的意思是:

    public void function1() {
      final int x; // value of x, which is used in class A have to be final
    
      class A {
        void function2() {
          //body of function
        }
      }
    }
    

    答案是:变量x id复制到A类。它无法更改,因为那时出现了不一致。那么为什么Java的架构师没有创建不复制变量x的语言呢?将传递变量的地址,然后更改变量x而不会出现不一致。有人可以举个例子来解释一下吗?

    或与同步相关的问题。两个变量应该是相同的,但是如果我们超出范围呢?其中一个变量没有退出,那么同步的是什么?

  3. 编辑: 为什么会这样:

    interface In
    {
        void f1();
    }
    
    class A
    {
        int variable = 3;
    
        In g()
        {
            return new In()
            {
                @Override
                public void f1()
                {
                    variable = 6;
    
                    System.out.println(variable);
                }
            };
        }
    }
    
    public static void main(String[] args)
        {
            In in1;
            {
                A a = new A();
                in1 = a.g();
            }
    
            in1.f1(); //class A does not exists any more, field 'variable' is changed, despite it does not exist
        }
    

    为什么没有同步问题?

3 个答案:

答案 0 :(得分:3)

回答你的第一个问题:

就个人而言,我还没有看到它为什么不接受内部类中的非最终静态成员。但是,我可以告诉你编译时会发生什么。声明static final 原语成员时,可以将其编译为使用此成员的代码。但是,当您尝试创建static final Object o = new Object();时,它无法在编译时知道o将指向的内容。并且显然会给出相同的编译器错误,就像在内部类中创建static int(非最终)一样。

回答第二个问题,原因如下:

x是一个局部变量,它被压入堆栈。当您在内部类中使用对x的引用时,您将遇到问题。因为内部类很可能比该函数的范围更长寿。一旦函数结束,x就会超出范围并从堆栈中删除。所以,现在,你的内部类仍然引用了一个不再存在的变量。所以,这是在编译器中实现的一个小技巧:如果你将x声明为final,编译器知道它不会改变,所以没有必要保留对它的引用。这允许编译器将x作为新成员复制到内部类实例中。因此,x将被复制到堆中。如果你没有将它标记为final,那么将它与堆栈的无效引用进行比较(当然不能编译,因为编译器会保护你犯这个错误。)

答案 1 :(得分:1)

在我看来,没有无法解决的问题,因为内部类不能有静态成员,匿名类中使用的变量只能是最终的。我认为这只是语言设计师的决定。

  1. 在complilation之后,内部类与顶级类没有区别,只有它的所有构造函数都获得了额外的参数 - 对外部类的引用。

    class X {     Y级{     } }

  2. 编译器将其转换为此

    class X {
    }
    
    class X$Y {
        private final X x;
        X$Y(X x) {
           this.x = x;
        }
    }
    

    没有理由说X $ Y不能拥有静态成员。语法也不是问题

    class X {
        class Y {
            static int x = 1;
        }
    
        void x() {
             Y.x = 2;
        }
    }
    

    关于static final int x = 1;static int x = 1;的不同之处 - 区别在于前者不需要初始化它是一个常量,而后者需要一个隐式静态初始化块来放置将分配的代码1到x。

    1. final var作为构造函数参数获取匿名类
    2. class X {     void x(){         最终整数x = 1;         new Runnable(){             public void run(){                 int y = x;             }         };     } }

      实际的匿名类

      class X$1 {
         private final Integer x;
         X$1(Integer x) {
            this.x = x;
         }
         ...
      

      外部变量必须是final的唯一原因是因为否则看起来我们可以从内部类代码中改变它

      void x() {
          Integer x = 1;
          new Runnable() {
            public void run() {
                x = 2;
            }
      ...
      

      但事实并非如此,因为内部类使用副本

答案 2 :(得分:1)

  1. 内部类背后的想法是它们属于包含类的实例。因此,如果我们忽略范围和类似的细节,则内部类的静态成员将等同于包含类的非静态成员。

    然而,随着内部类的实现方式,这是不可能实现的:内部类被编译成一个独立的类,如果它有静态成员,它们将在包含类的所有实例之间共享。这打破了语言试图创造的错觉。因此,完全禁止静态成员。

    此规则的唯一例外是声明为static final的字段,并使用编译时常量(如整数或字符串文字)进行初始化。这些是可以允许的,因为编译时时间常量将在包含类的所有实例之间共享。

  2. 一旦变量超出范围,局部变量的地址就没用了,例如当方法返回时。如果读取或写入变量会发生什么?现在地址指向非法的内存位置,甚至更糟糕的是:指向一个完全不相关的变量。