java - 从外部方法访问变量

标签 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");

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

最佳答案

您正在创建一段可以“旅行”的代码片段。代码在 {}属于你的new Date()声明没有在您编写的地方运行;它附加到您创建的日期对象上,并与之一起使用。该日期对象可以移动:它可以存储在字段中。也许它会在 18 天后以完全不同的线程运行。虚拟机不知道,因此需要为此做好准备。

那么假设确实如此:您的“计数器”变量会发生什么?

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

假设虚拟机默默地“升级”了变量;它不像平常那​​样在堆栈上声明它,而是在堆上声明它,以便变量可以在方法退出后继续存在。

好吧。如果在另一个线程中调用compareTo 会怎样?现在是否可以将局部变量标记为“ volatile ”?可以说,在 java 中,即使是局部变量也可能显示竞争条件吗?

这是一个判断性的决定;由语言设计者决定的事情。

Java 语言设计者决定反对静默升级到堆,并且反对允许局部变量可能受到多线程访问。

因此,您在任何可以“移动”*的代码块中访问的任何局部变量都必须声明为 [A] final或 [B] 表现得好像它本来可以那样,在这种情况下,java 会默默地为你确定它的最终结果。

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

就其值(value)而言,我发现 AtomicX 的用法更具可读性。因此,如果您需要一个可在旅行代码中修改的 int,请不要这样做 new int[1] ;做new AtomicInteger 。如果您需要可修改的字符串,请使用 new AtomicReference<String>() ,不是new String[1] .

注意:是的,在这个特定代码中,即使排序操作也仅在该方法中使用计数器变量,并且一旦该方法结束,计数器变量就会消失,但是编译器没有进行那种极其深入的分析来弄清楚这一点,它使用更简单的规则:想要从“旅行”代码中的外部 scipe 访问本地变量?不允许 - 除非它是(实际上)最终的。

*) 移动代码是本地方法或匿名类定义中的任何内容,以及 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 - 从外部方法访问变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63286968/

相关文章:

java - Ant 类互相寻找

java - 如何编写一个程序将java中的列表中的度数转换为弧度

python - 组成:为什么 "container instance"改变时 "contained instance"属性没有更新?

java - 从 Java 读取 Arduino 太慢

command-line - Java-运行cmd

Java EE - 如何使用自定义注释在方法上注入(inject)方法参数

design-patterns - 什么时候工厂方法比简单工厂更好,反之亦然?

java - 如何将音频文件转换为字节数组

java - 将对象从一个类移动到另一个类 (Java)

C#类转Sql表