从外部方法访问变量

时间:2020-08-06 15:32:32

标签: java class oop inner-classes

我正在研究Java内部类,并且遇到与外部方法中的变量引用有关的问题。例如,我有一个源代码来统计排序期间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");

我很困惑,这两个代码之间有什么区别,这个错误的原因和解决方案是什么?

1 个答案:

答案 0 :(得分:2)

您正在创建一段可以“旅行”的代码。 {}中属于您的new Date()声明的代码不在您编写的地方运行;它附加到您创建的日期对象上,并随其一起使用。此日期对象可以旅行:可以将其存储在字段中。也许它是从现在开始运行18天,线程完全不同。 VM不知道,因此需要为此做好准备。

因此,我们可以这样说:“计数器”变量会发生什么情况?

通常,局部变量存储在“堆栈上”,并在方法退出时销毁。但是在那种情况下,我们将销毁您的旅行代码仍可访问的变量,那意味着从现在起18天后调用日期compareTo代码是什么意思?

让我们说VM默默地“升级”了变量;与其像在普通栈上那样声明它,不如在堆上声明它,以便变量可以在退出方法时幸免。

好的。如果在另一个线程中调用compareTo怎么办?现在是否可以将局部变量标记为“易失性”?是否可以声明在Java中甚至局部变量也可能显示竞争条件?

这是一个判断电话;语言设计者可以决定的东西。

Java的语言设计人员决定将 against 静默升级到堆中,然后 against 允许本地人可能受到多线程访问。

因此,您在任何可以“移动” *的代码块中访问的任何局部变量都必须声明为[{]]或{B]声明为{A}或[B],就像这样,在这种情况下,java将静默运行为您做最后的决定。

更改是final(变量本身,不更改)在第二个片段中:它是对数组的引用,并且该引用永不更改。实际上,您已经添加了间接级别,并且可以自己访问堆:数组存在于堆中。

对于它的价值,我发现AtomicX的用法更具可读性。因此,如果您需要一个可以在旅行代码中修改的int,请不要执行counter;做new int[1]。如果您需要可修改的字符串,请使用new AtomicInteger,而不要使用new AtomicReference<String>()

NB:是的,在 this 特定代码中,即使在该方法中,即使在进行排序操作时也只能使用counter变量,并且一旦该方法结束,counter var就会消失,但是编译器并没有进行那种非常深入的分析来找出答案,它使用了更简单的规则:是否想在“旅行”代码中从外部脚本访问本地变量?不允许-除非最终(有效)。

*)行进代码是方法本地或匿名类定义中的任何内容,以及lambda中的任何内容。所以:

new String[1]