Прояснение прохождения 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.