从外部方法访问变量
Access variables from outer methods
我正在研究 Java 中的内部 Class,我有一个与外部方法中的变量引用相关的问题。例如,我有一个源代码来计算在排序过程中调用了多少次 compareTo()
方法:
int counter = 0;
Date[] dates = new Date[100];
for(int i = 0; i < dates.length; i++)
{
dates[i] = new Date()
{
public int compareTo(Date other)
{
counter++;
return super.compareTo(other);
}
};
}
Arrays.sort(dates);
System.out.println(counter + " comparisons");
执行源码可以看到counter++
的使用存在错误。为了解决这个问题,有人告诉我应该这样改:
int[] counter = new int[1];
Date[] dates = new Date[100];
for(int i = 0; i < dates.length; i++)
{
dates[i] = new Date()
{
public int compareTo(Date other)
{
counter[0]++;
return super.compareTo(other);
}
};
}
Arrays.sort(dates);
System.out.println(counter[0] + " comparisons");
我很困惑,这两个代码之间有什么区别,这个错误的原因和解决方法是什么?
您正在创建可以 'travel' 的代码片段。 {}
中属于您的 new Date()
声明的代码不在您编写它的地方 运行;它附加到您创建的这个日期对象,并与之一起使用。这个日期对象可以旅行:它可以存储在一个字段中。也许是 运行 18 天后,在一个完全不同的线程中。 VM 不知道,因此需要为此做好准备。
假设它确实存在:您的 'counter' 变量会发生什么?
通常情况下,局部变量会存储 'on the stack' 并在方法退出时销毁。但在那种情况下,我们会破坏您的旅行代码仍然可以访问的变量,那么这意味着什么,从现在起 18 天,当您的日期 compareTo 代码被调用时?
假设 VM 静默 'upgrades' 变量;它不是像往常一样在堆栈上声明它,而是在堆上声明它,以便变量可以在方法退出后继续存在。
好的。如果在另一个线程中调用 compareTo 怎么办?现在是否可以将局部变量标记为 'volatile'?是否可以声明,在 java 中,即使局部变量也可能显示竞争条件?
这是一个判断电话;由语言设计者决定的东西。
Java 的语言设计者决定反对 静默升级到堆,反对 允许本地人可能受到 multi-thread访问。
因此,您在可以 'travel'* 的任何代码块中访问的任何局部变量必须声明 [A] final
或 [B] 就像它本可以那样,其中case java 会默默地为你完成它。
变化是 counter
,变量本身,在第二个片段中没有改变:它是对数组的引用,并且该引用永远不会改变.实际上,您自己添加了间接级别和堆访问:堆上存在数组。
就其价值而言,我发现 AtomicX 的用法更具可读性。因此,如果您需要一个可在移动代码中修改的 int,请不要执行 new int[1]
;做 new AtomicInteger
。如果您需要可修改的字符串,请使用 new AtomicReference<String>()
,而不是 new String[1]
。
注意:是的,在 this 特定代码中,仅使用计数器变量,即使是排序操作,在该方法中,计数器变量也可以在该方法中消失结束,但编译器并没有进行那种极其深入的分析来弄清楚这一点,它使用了更简单的规则:想从 'travelling' 代码中的外部 scipe 访问本地变量?不允许 - 除非它(实际上)是最终的。
*) 移动代码是方法本地或匿名 class 定义中的任何内容,以及 lambda 中的任何内容。所以:
void method() {
class MethodLocalClassDef {
// anything here is considered 'travelling'
}
Object o = new Object() {
// this is an anonymous class def,
// and anything in here is 'travelling'
};
Runnable r = () -> {
// this is a lambda, and considered 'travelling'
};
}
我正在研究 Java 中的内部 Class,我有一个与外部方法中的变量引用相关的问题。例如,我有一个源代码来计算在排序过程中调用了多少次 compareTo()
方法:
int counter = 0;
Date[] dates = new Date[100];
for(int i = 0; i < dates.length; i++)
{
dates[i] = new Date()
{
public int compareTo(Date other)
{
counter++;
return super.compareTo(other);
}
};
}
Arrays.sort(dates);
System.out.println(counter + " comparisons");
执行源码可以看到counter++
的使用存在错误。为了解决这个问题,有人告诉我应该这样改:
int[] counter = new int[1];
Date[] dates = new Date[100];
for(int i = 0; i < dates.length; i++)
{
dates[i] = new Date()
{
public int compareTo(Date other)
{
counter[0]++;
return super.compareTo(other);
}
};
}
Arrays.sort(dates);
System.out.println(counter[0] + " comparisons");
我很困惑,这两个代码之间有什么区别,这个错误的原因和解决方法是什么?
您正在创建可以 'travel' 的代码片段。 {}
中属于您的 new Date()
声明的代码不在您编写它的地方 运行;它附加到您创建的这个日期对象,并与之一起使用。这个日期对象可以旅行:它可以存储在一个字段中。也许是 运行 18 天后,在一个完全不同的线程中。 VM 不知道,因此需要为此做好准备。
假设它确实存在:您的 'counter' 变量会发生什么?
通常情况下,局部变量会存储 'on the stack' 并在方法退出时销毁。但在那种情况下,我们会破坏您的旅行代码仍然可以访问的变量,那么这意味着什么,从现在起 18 天,当您的日期 compareTo 代码被调用时?
假设 VM 静默 'upgrades' 变量;它不是像往常一样在堆栈上声明它,而是在堆上声明它,以便变量可以在方法退出后继续存在。
好的。如果在另一个线程中调用 compareTo 怎么办?现在是否可以将局部变量标记为 'volatile'?是否可以声明,在 java 中,即使局部变量也可能显示竞争条件?
这是一个判断电话;由语言设计者决定的东西。
Java 的语言设计者决定反对 静默升级到堆,反对 允许本地人可能受到 multi-thread访问。
因此,您在可以 'travel'* 的任何代码块中访问的任何局部变量必须声明 [A] final
或 [B] 就像它本可以那样,其中case java 会默默地为你完成它。
变化是 counter
,变量本身,在第二个片段中没有改变:它是对数组的引用,并且该引用永远不会改变.实际上,您自己添加了间接级别和堆访问:堆上存在数组。
就其价值而言,我发现 AtomicX 的用法更具可读性。因此,如果您需要一个可在移动代码中修改的 int,请不要执行 new int[1]
;做 new AtomicInteger
。如果您需要可修改的字符串,请使用 new AtomicReference<String>()
,而不是 new String[1]
。
注意:是的,在 this 特定代码中,仅使用计数器变量,即使是排序操作,在该方法中,计数器变量也可以在该方法中消失结束,但编译器并没有进行那种极其深入的分析来弄清楚这一点,它使用了更简单的规则:想从 'travelling' 代码中的外部 scipe 访问本地变量?不允许 - 除非它(实际上)是最终的。
*) 移动代码是方法本地或匿名 class 定义中的任何内容,以及 lambda 中的任何内容。所以:
void method() {
class MethodLocalClassDef {
// anything here is considered 'travelling'
}
Object o = new Object() {
// this is an anonymous class def,
// and anything in here is 'travelling'
};
Runnable r = () -> {
// this is a lambda, and considered 'travelling'
};
}