【问题标题】:Reference to a final variable being changed in a loop对循环中更改的最终变量的引用
【发布时间】:2018-05-17 20:34:49
【问题描述】:
private void loadFromFolder(String folderPath, PApplet loader) {
    File folder = new File(folderPath);
    final String[] files = folder.list();
    for(int i = 0; i < files.length; i++) {
        String filePath = files[i].substring(0, files[i].indexOf("."));
        images.put(filePath, loader.loadImage(folderPath+fileSeparator+files[i]));
        System.out.print(files);
        System.out.print(": " + filePath + ", " + i + "\n");
    }
}

当我运行此代码时,我想将文件夹 (folderPath) 中的所有图像文件添加到 hashMap,其中 .png 之前的所有内容的字符串作为键。无论出于何种原因,最终的字符串数组以某种方式更改了其引用,并且 int i 到处乱跳。我真的试过了,我无法弄清楚这个。谁能给我解释一下?我的控制台打印如下。 (我的代码都在一个线程中,所以这不是问题)

[Ljava.lang.String;@3d207c96: 1stBulletBar, 0
[Ljava.lang.String;@3d207c96: 1stEmptyBar, 1
[Ljava.lang.String;@3d207c96: 1stHealthBar, 2
[Ljava.lang.String;@3d207c96: 1stStaminaBar, 3
[Ljava.lang.String;@246ef213: 1stBulletBar, 0
[Ljava.lang.String;@3d207c96: BulletBar, 4
[Ljava.lang.String;@3d207c96: BulletBarCap, 5
[Ljava.lang.String;@246ef213: 1stEmptyBar, 1
[Ljava.lang.String;@3d207c96: Button, 6
[Ljava.lang.String;@246ef213: 1stHealthBar, 2
[Ljava.lang.String;@3d207c96: ButtonLeft, 7
[Ljava.lang.String;@246ef213: 1stStaminaBar, 3
[Ljava.lang.String;@3d207c96: ButtonLeftPressed, 8
[Ljava.lang.String;@246ef213: BulletBar, 4
[Ljava.lang.String;@246ef213: BulletBarCap, 5
[Ljava.lang.String;@3d207c96: ButtonPressed, 9
[Ljava.lang.String;@246ef213: Button, 6
[Ljava.lang.String;@246ef213: ButtonLeft, 7
[Ljava.lang.String;@3d207c96: ButtonRight, 10
[Ljava.lang.String;@246ef213: ButtonLeftPressed, 8
[Ljava.lang.String;@246ef213: ButtonPressed, 9
[Ljava.lang.String;@3d207c96: ButtonRightPressed, 11
[Ljava.lang.String;@246ef213: ButtonRight, 10
[Ljava.lang.String;@3d207c96: EmptyBar, 12
[Ljava.lang.String;@246ef213: ButtonRightPressed, 11
[Ljava.lang.String;@1e172402: 1stBulletBar, 0
[Ljava.lang.String;@3d207c96: FirstInventoryPiece, 13
[Ljava.lang.String;@246ef213: EmptyBar, 12
[Ljava.lang.String;@1e172402: 1stEmptyBar, 1
[Ljava.lang.String;@3d207c96: HealthBar, 14
[Ljava.lang.String;@246ef213: FirstInventoryPiece, 13
[Ljava.lang.String;@3d207c96: HealthBarCap, 15
[Ljava.lang.String;@1e172402: 1stHealthBar, 2
[Ljava.lang.String;@246ef213: HealthBar, 14
[Ljava.lang.String;@1e172402: 1stStaminaBar, 3
[Ljava.lang.String;@3d207c96: HUD, 16
[Ljava.lang.String;@246ef213: HealthBarCap, 15
[Ljava.lang.String;@1e172402: BulletBar, 4
[Ljava.lang.String;@3d207c96: InventoryCap, 17
[Ljava.lang.String;@1e172402: BulletBarCap, 5
[Ljava.lang.String;@246ef213: HUD, 16
[Ljava.lang.String;@3d207c96[Ljava.lang.String;@1e172402: Button, 6
: InventoryPiece, 18
[Ljava.lang.String;@1e172402: ButtonLeft, 7
[Ljava.lang.String;@246ef213: InventoryCap, 17
[Ljava.lang.String;@3d207c96: StaminaBar, 19
[Ljava.lang.String;@1e172402: ButtonLeftPressed, 8
[Ljava.lang.String;@3d207c96: StaminaBarCap, 20
[Ljava.lang.String;@246ef213: InventoryPiece, 18
[Ljava.lang.String;@1e172402: ButtonPressed, 9
[Ljava.lang.String;@3d207c96: Title, 21
[Ljava.lang.String;@246ef213: StaminaBar, 19
[Ljava.lang.String;@1e172402: ButtonRight, 10
[Ljava.lang.String;@3d207c96: TitleLeft, 22
[Ljava.lang.String;@246ef213: StaminaBarCap, 20
[Ljava.lang.String;@1e172402: ButtonRightPressed, 11
[Ljava.lang.String;@246ef213: Title, 21
[Ljava.lang.String;@3d207c96: TitleRight, 23
[Ljava.lang.String;@246ef213: TitleLeft, 22
[Ljava.lang.String;@1e172402: EmptyBar, 12
[Ljava.lang.String;@246ef213: TitleRight, 23
[Ljava.lang.String;@1e172402: FirstInventoryPiece, 13
[Ljava.lang.String;@1e172402: HealthBar, 14
[Ljava.lang.String;@1e172402: HealthBarCap, 15
[Ljava.lang.String;@246ef213: ui_big_pieces, 24
[Ljava.lang.String;@3d207c96: ui_big_pieces, 24
[Ljava.lang.String;@1e172402: HUD, 16
[Ljava.lang.String;@1e172402: InventoryCap, 17
[Ljava.lang.String;@1e172402: InventoryPiece, 18
[Ljava.lang.String;@1e172402: StaminaBar, 19
[Ljava.lang.String;@1e172402: StaminaBarCap, 20
[Ljava.lang.String;@1e172402: Title, 21
[Ljava.lang.String;@1e172402: TitleLeft, 22
[Ljava.lang.String;@1e172402: TitleRight, 23
[Ljava.lang.String;@1e172402: ui_big_pieces, 24

编辑:我忘了提到我的代码在 Eclipse 的调试模式下运行时运行正常。以下是在方法的第一行带有断点的结果。

[Ljava.lang.String;@6e9cfc2e: 1stBulletBar, 0
[Ljava.lang.String;@6e9cfc2e: 1stEmptyBar, 1
[Ljava.lang.String;@6e9cfc2e: 1stHealthBar, 2
[Ljava.lang.String;@6e9cfc2e: 1stStaminaBar, 3
[Ljava.lang.String;@6e9cfc2e: BulletBar, 4
[Ljava.lang.String;@6e9cfc2e: BulletBarCap, 5
[Ljava.lang.String;@6e9cfc2e: Button, 6
[Ljava.lang.String;@6e9cfc2e: ButtonLeft, 7
[Ljava.lang.String;@6e9cfc2e: ButtonLeftPressed, 8
[Ljava.lang.String;@6e9cfc2e: ButtonPressed, 9
[Ljava.lang.String;@6e9cfc2e: ButtonRight, 10
[Ljava.lang.String;@6e9cfc2e: ButtonRightPressed, 11
[Ljava.lang.String;@6e9cfc2e: EmptyBar, 12
[Ljava.lang.String;@6e9cfc2e: FirstInventoryPiece, 13
[Ljava.lang.String;@6e9cfc2e: HealthBar, 14
[Ljava.lang.String;@6e9cfc2e: HealthBarCap, 15
[Ljava.lang.String;@6e9cfc2e: HUD, 16
[Ljava.lang.String;@6e9cfc2e: InventoryCap, 17
[Ljava.lang.String;@6e9cfc2e: InventoryPiece, 18
[Ljava.lang.String;@6e9cfc2e: StaminaBar, 19
[Ljava.lang.String;@6e9cfc2e: StaminaBarCap, 20
[Ljava.lang.String;@6e9cfc2e: Title, 21
[Ljava.lang.String;@6e9cfc2e: TitleLeft, 22
[Ljava.lang.String;@6e9cfc2e: TitleRight, 23
[Ljava.lang.String;@6e9cfc2e: ui_big_pieces, 24

当每行打印线程 ID 时,每打印一行我都会得到 Thread[Animation Thread,5,main]

【问题讨论】:

  • 你正在打印files,这是一个字符串数组,为什么?
  • 所以你在多个线程中运行它?因为很明显,您已经通过多个不同的数组在这里混杂在一起进行迭代。
  • 打印你的线程ID...因为你至少有三个具有相同值的数组。查找文本“1stBulletBar, 0”
  • 你能发个minimal reproducible example吗?
  • @khelwood 是对的,看起来你在多个线程中运行。输出中有一条扭曲的线证明了多个线程:)

标签: java arrays string file


【解决方案1】:

问题中的数据对于 files 数组具有三个不同的值,并且每个值的计数器从 0 到 24 运行。显然,对该方法的三个调用同时在三个不同的线程中进行。根据您的评论:

我有一堆扩展处理 PApplet 的场景对象。调用它的类中有一个布尔值。在处理 setup() 中制作场景时,将调用该方法。如果该布尔值为 false,则调用此方法,然后将布尔值设置为 true,因此如果另一个场景尝试调用它,则不会发生任何事情。

您有一个race condition,其中三个线程正在检查布尔值并在它们中的任何一个看到布尔值已设置之前调用此方法。如何解决这个问题取决于代码在测试和设置变量以及进行调用时的样子。

【讨论】:

    【解决方案2】:

    您应该阅读this post

    关键字final 仅表示您只能初始化该变量一次。这并不意味着值不能改变。实际上,您可以随意调用方法并更改final 变量的值。

    我怀疑您实际上是在更改变量的值,而不是它的引用。

    【讨论】:

    • 是的,我发现在研究东西时,但它仍然没有回答为什么多次调用该方法?上面的人在说多个线程,我认为这是有道理的。
    • @BenjaminLeistiko 我想我问你的问题 - 你写这个线程吗?从您在问题中写的内容来看,它似乎是一个单线程程序
    • 我是单线程写的。这是一个合作项目,所以今天下午我将与其他从事该项目的人交谈,但最初的愿景是把它全部写在一个线程中。当我打印线程 ID 时,我得到了所有线程 ID。线程[动画线程,5,main]
    • 在方法执行期间数组引用的值永远不会改变。对方法的三个调用并行运行,每个调用都有自己独特的、不变的引用。
    猜你喜欢
    • 2011-02-19
    • 2013-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多