【问题标题】:Java pass by value clarified [duplicate]Java按值传递澄清[重复]
【发布时间】:2016-04-22 07:43:45
【问题描述】:

我有一个代表棋盘类中棋盘游戏的 Cell 对象列表。

Cell boardGame[][] = new Cell[8][8];

我需要一个临时单元格来尝试玩家在他身上移动并将其与其他单元格进行比较,所以我认为我可以使用 java pass-by-value 来做到这一点。

test(board.boardGame);
board.printBoard(); 

private static void test(Cell[][] boardGame) {
Cell c = new Cell((new Soldier(ChessPiece.QUEEN, Color.WHITE)), 7, 4);
    boardGame[7][7] = c;

}

我在这里阅读了一些关于 java 的帖子,但显然我仍然没有 100% 理解它。

我原以为棋盘上只会看到一个白皇后,但我看到了两个。 我知道如果你传递一个引用,你可以改变它的值,但我认为如果我传递数组本身,它的成员不会被修改,除非我执行返回。

请帮助我更好地理解这个主题。 谢谢

编辑:

我想我不明白它何时调用属性以及在何处不调用。 如果你被称为“新”,我会有所不同。

当它是另一个对象的一部分时它称为属性对吗?但是每个对象都可以创建为另一个对象的一部分。我可以在狗类中创建一个新字符串,然后在动物类中创建狗类,然后在另一个类中创建它。所以只有顶层类在堆栈中?

例如:

public class Board  { //fake class

    int num= 0;
    public void test(int i){
        i = 1;
    }
}

在另一个班级:

    public class Main {
        static void outsideTest(Board board){
            board.num = 1;
        }
    public static void main(String[] args) {
        Board board = new Board();
        System.out.println(board.num);
        board.test(board.num);
        System.out.println(board.num);
        outsideTest(board);
        System.out.println(board.num);
}
}

现在我不明白为什么在 test() 方法上 num 没有改变,而在 outsideTest() 上 num 改变了,num 是在堆中创建的,因为它是板对象的一部分,所以它需要两种情况都改变了没有?

【问题讨论】:

  • 数组的值是一个引用(和其他所有的Object一样)。
  • 为什么看不到 2 个皇后,因为这段代码中没有任何内容可以将皇后从第一个单元格中删除?
  • Cell的构造函数中的两个int参数是什么?您是否正在传递初始位置? printBoard() 的主体是什么?它是探测棋子以查看它们的位置还是依赖于 boardGame 数组的内容?
  • @Jim W,boardGame 数组内容的打印继电器主体。用于其他功能的单元格内的位置并不总是知道特定单元格的来源。

标签: java cell pass-by-value


【解决方案1】:

对对象的引用是按值传递的,也就是说

boardGame = new Cell[8][8];

不会造成任何伤害,但改变您从 boardGame 获得的任何东西都会。

【讨论】:

  • 传递了引用的副本。你不能修改被引用的对象,即使它没有被声明为 final,因为它只是引用的副本,而引用同一个对象的原始引用将不受影响,而被引用的对象可能是可变的。跨度>
【解决方案2】:

记住它的最好和最不容易混淆的方法如下:Java 按值传递所有内容,包括引用。 :)

当你有一个变量时:

Object a = new Object();

您实际上没有将对象存储在a 中。您所拥有的是对内存中某处对象的引用。

同样当你调用一个对象的方法时:

String b = a.toString();

你不要那样做。你调用的是一个使用被引用对象的数据作为其上下文的方法。

所以当你将一个对象作为参数传递时

System.out.println(b);

你没有传递整个对象。您传递引用,并且该引用是按值传递的。

编辑:

如果引用不是按值传递,而是按引用传递,你可以做这样的事情,幸运的是你不能。

public void swap(Object a, Object b){
   Object swap = a; a = b ; b = swap;
} 

String a = "a";
String b = "b";
swap(a,b);

// would print "b a" if references were 
// passed by reference but instead prints 
// "a b" as they're passed by value.
System.out.println(a + " " b);

【讨论】:

  • 我试图在单元格交换中做类似的事情,但它不会是永久性的。
【解决方案3】:

Java 本质上总是“pass-by-value”。

警告:它将存储在内存中的值传递给变量。

对于原始数据类型,内存是在堆栈空间中分配的,而对于对象,引用内存是在堆栈空间中分配的,但对象本身是在堆中创建的。数组也可以这样说,尽管它们不是严格意义上的对象。

下面的图表会更清楚。

您的 Cell 对象 2D 数组应该看起来像 anObjectArrayVar(不完全是图中指向对象的 ref 现在应该指向行,我们需要在堆中进行另一级别的分配在每一行的 ref 和 objects 之间(一组引用对象的单元格)。

因此,当您传递 boardGame 时,将传递存储在堆栈中的值,该值存储对 对象数组 的引用(就像存储在 anObjectArrayVar 中的值一样)。如果说 refs 列表存储在编号为 50 的位置,那么 anObjectArrayVar 将存储它,并在我们调用它时将该值传递给测试方法。在这种情况下,测试方法将无法转到内存位置 anObjectArrayVar 并更改其值(例如 100),因为它只有该值的副本,但它可以轻松更改它所指的内容(直接或间接),如值在 ref 或下一个级别(并添加新对象,就像在您的情况下添加带有女王的新单元格一样)或它们指向的对象,这些更改将反映在整个程序中!

我还想提请您注意代码

boardGame[7][7] = c;

将替换当前单元格(以及当前在其中的士兵),如果在游戏中那个地方最初有一名士兵,这将产生重大问题。游戏状态实际上会改变。

作为一个建议(鉴于您对设计的了解有限),我会说至少在替换之前将单元格保存在测试方法中的其他值中。

Cell old = boardGame[7][7];
//now do all your operations
boardGame[7][7] = old;//just before returning from the function

【讨论】:

  • 谢谢,我编辑问题。
  • @Smiled_One 恐怕我不明白编辑中问的 Q。如果您可以使用一些您所理解的示例可能会有所帮助
  • 好的,我得到了你的问题,但我的建议是在进一步阅读之前根据我上面的解释尝试一下。
  • 类似的现象也发生在上面。在 outsideTest() 中,您传递 board 的对象引用值...相当于上图中的 e1,因此它可以更改对象和属性。在图中,e1 的副本可用于更改名称、ssn、 e1 的出生年份。对于 test(),您传递 int 值,因此只能访问 int 值 num 的副本,因此无法更改 num。
  • 是的......我猜你明白了。当参数是引用(指针或地址,尽管 java 极客喜欢调用引用)类型(如 board 或 boardGame)时,您的函数有一个 该引用(地址)的副本,所以它可以转到指向的位置通过它,更改将是全局可见的。如果您传递原始数据类型(int),您将在该位置获得 数据的副本(它不是对任何地方的引用)。因此,所做的更改仅在副本上,并随着函数丢失记住上述内容,这只是能够知道传递了什么类型的问题——引用或原始
猜你喜欢
  • 2021-01-29
  • 2016-02-29
  • 2011-12-23
  • 2023-03-09
  • 2013-12-02
  • 2012-03-12
  • 2013-09-15
  • 2012-03-13
  • 2014-09-10
相关资源
最近更新 更多