Прояснение прохождения Java по значению

У меня есть список объектов Cell, представляющих настольную игру внутри класса Board.

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

Мне нужна была временная ячейка, чтобы попытаться переместить игрока на него и сравнить ее с другими ячейками, поэтому я решил, что для этого можно использовать java-передачу по значению.

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, как было создано в куче, потому что это часть объекта board, поэтому его нужно изменить на обоих случаев нет?

3 ответа

Решение

По сути, Java всегда "передается по значению".

Предостережение: передает значение, хранящееся в памяти, для переменной.

Для примитивных типов данных память выделяется в пространстве стека, тогда как для объектов ссылочная память выделяется в пространстве стека, но сам объект создается в куче. Аналогичное может быть указано и для массивов, хотя в строгом смысле они не совсем объекты.

Диаграммы ниже сделают это более понятным.

Пространство стека и кучи памяти

Распределение массива

2D-массив вашего объекта Cell должен выглядеть примерно как anObjectArrayVar (не совсем так, как ссылка на диаграмме, указывающая на объекты, теперь должна указывать на строки, и нам потребуется другой уровень размещения в куче между ref и объектами для каждой строки (набор ячеек, относящихся к объектам).

Поэтому, когда вы передаете boardGame, передается значение, хранящееся в стеке, в котором хранится ссылка на массив объектов (точно так же, как значение, хранящееся в anObjectArrayVar). Если, скажем, список ссылок хранится в ячейке с номером 50, тогда anObjectArrayVar сохранит это и передаст это значение методу тестирования, когда мы его вызовем. В таком сценарии тестовый метод не сможет перейти в область памяти anObjectArrayVar и изменить его значение (скажем, 100), поскольку у него есть только копия значения, но он может легко изменить то, к чему он относится (прямо или косвенно), как значения в ref или следующем уровне (и добавьте новые объекты, как в вашем случае добавление новой ячейки с queen) или объекты, на которые они указывают, и эти изменения будут отражены во всей программе!

Я также хотел бы обратить ваше внимание на то, что код

boardGame[7][7] = c;

заменит текущую ячейку (а также солдата, находящегося в ней в данный момент), что создаст серьезные проблемы, если в тот момент в этом месте в этом месте был солдат. Состояние игры фактически изменится.

В качестве предложения (учитывая ограниченные знания о вашем дизайне) я бы сказал, по крайней мере, сохранить ячейку в каком-либо другом значении в методе теста перед его заменой.

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

Лучший и наименее запутанный способ запомнить это так: 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);

Ссылка на объект передается по значению, что означает, что

boardGame = new Cell[8][8];

не причиняет вреда, но изменяет все, что вы получаете от boardGame.

Другие вопросы по тегам