static
个变量:
内部类不能包含static
个字段。它不能包含static
个成员,因为在哪里分配static
成员会有问题。内部类与外部类相关联。我理解为什么它不包含static
成员,但内部类可以包含static
常量。为什么?它是否经过特殊处理?它是在特殊堆上吗?它仍然是静态成员,但是不变,所以它是经过特殊处理的吗?
它可以包含:“final static int x”,但不包含“static int x”。
我想知道为什么本地类中使用的方法的变量应该是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而不会出现不一致。有人可以举个例子来解释一下吗?
或与同步相关的问题。两个变量应该是相同的,但是如果我们超出范围呢?其中一个变量没有退出,那么同步的是什么?
编辑: 为什么会这样:
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
}
为什么没有同步问题?
答案 0 :(得分:3)
回答你的第一个问题:
就个人而言,我还没有看到它为什么不接受内部类中的非最终静态成员。但是,我可以告诉你编译时会发生什么。声明static final
原语成员时,可以将其编译为使用此成员的代码。但是,当您尝试创建static final Object o = new Object();
时,它无法在编译时知道o
将指向的内容。并且显然会给出相同的编译器错误,就像在内部类中创建static int
(非最终)一样。
回答第二个问题,原因如下:
x
是一个局部变量,它被压入堆栈。当您在内部类中使用对x
的引用时,您将遇到问题。因为内部类很可能比该函数的范围更长寿。一旦函数结束,x
就会超出范围并从堆栈中删除。所以,现在,你的内部类仍然引用了一个不再存在的变量。所以,这是在编译器中实现的一个小技巧:如果你将x
声明为final
,编译器知道它不会改变,所以没有必要保留对它的引用。这允许编译器将x
作为新成员复制到内部类的实例中。因此,x
将被复制到堆中。如果你没有将它标记为final,那么将它与堆栈的无效引用进行比较(当然不能编译,因为编译器会保护你犯这个错误。)
答案 1 :(得分:1)
在我看来,没有无法解决的问题,因为内部类不能有静态成员,匿名类中使用的变量只能是最终的。我认为这只是语言设计师的决定。
在complilation之后,内部类与顶级类没有区别,只有它的所有构造函数都获得了额外的参数 - 对外部类的引用。
class X { Y级{ } }
编译器将其转换为此
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。
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)
内部类背后的想法是它们属于包含类的实例。因此,如果我们忽略范围和类似的细节,则内部类的静态成员将等同于包含类的非静态成员。
然而,随着内部类的实现方式,这是不可能实现的:内部类被编译成一个独立的类,如果它有静态成员,它们将在包含类的所有实例之间共享。这打破了语言试图创造的错觉。因此,完全禁止静态成员。
此规则的唯一例外是声明为static final
的字段,并使用编译时常量(如整数或字符串文字)进行初始化。这些是可以允许的,因为编译时时间常量将在包含类的所有实例之间共享。
一旦变量超出范围,局部变量的地址就没用了,例如当方法返回时。如果读取或写入变量会发生什么?现在地址指向非法的内存位置,甚至更糟糕的是:指向一个完全不相关的变量。