Философия Java

Присваение


Присваение выполняется с помощью оператора =. Это означает “взять значение правой части (часто называемое rvalue) и скопируй его в левую сторону (часто называемую lvalue). rvalue - это любая константа, переменная или выражение, которое может произвести значение, но lvalue должно быть определенной, поименованной переменной. (То есть, здесь должно быть физическое пространство для хранения значения.) Например, вы можете присвоить постоянное значение переменной (A = 4;), но вы не сможете присвоить ничего постоянному значению — оно не может быть lvalue. (Вы не можете сказать 4 = A;.)

Присвоение примитивов достаточно прямое и понятное. Так как примитивы хранят реальное значение, а не ссылку на объект, то когда вы присваиваете примитивы, вы копируете содержимое с одного места в другое. Например, если вы говорите A = B для примитивов, то содержимое B копируется в A. Если вы потом измените A, B не подвергнется изменениям. Как программист, это то, что вы хотите ожидать в большинству случаев.

Однако когда вы присваиваете объекты, все меняется. Когда бы вы ни манипулировали объектом, то, чем вы манипулируете - ссылка, так что когда вы присваиваете “один объект другому”, на самом деле вы копируете ссылку из одного места в другое. Это означает, если вы скажете C = D для объектов, в конце вы получаете, что C и D указывают на объект, на который первоначально указывает только D. Приведенный ниже пример будет демонстрировать это.

Вот этот пример:

//: c03:Assignment.java

// Присвоение объектов немного хитрая вешь.

class Number { int i; }

public class Assignment { public static void main(String[] args) { Number n1 = new Number(); Number n2 = new Number(); n1.i = 9; n2.i = 47; System.out.println("1: n1.i: " + n1.i + ", n2.i: " + n2.i); n1 = n2; System.out.println("2: n1.i: " + n1.i + ", n2.i: " + n2.i); n1.i = 27; System.out.println("3: n1.i: " + n1.i + ", n2.i: " + n2.i); } } ///:~

Класс Number - прост и внутри функции main( ) создаются два его экземпляра .(n1 и n2). Переменная Значения i в каждом из Number имеют разные значения, а затем n2 присваивается n1, а n1 изменяется. Во многих языках программирования вы можете ожидать, что n1 и n2 независимы все время, но потому что вы присвоили ссылку, здесь приводится вывод, который вы увидите:


1: n1.i: 9, n2.i: 47 2: n1.i: 47, n2.i: 47 3: n1.i: 27, n2.i: 27

Изменение объекта n1 проявляется в изменении объекта n2! Это потому, что и n1 и n2 содержат одну и ту же ссылку, которые указывают на один и тот же объект. (Начальная ссылка, которая была в n1 и указывала на объект, содержащий значение 9 была переписана во время присвоения и на самом деле потерялась; ее объект будет очищен сборщиком мусора.)

Этот феномен часто называется эффектом наложения (aliasing) и это фундаментальный путь, которым работают в Java с объектами. Но что, если вы не хотите, чтобы в этом случае возник эффект наложения? Вы можете воздержаться от присвоения и сказать:

n1.i = n2.i;

При этом сохраняются два различных объекта вместо отбрасывания одного и прикрепления n1 и n2 к одному и тому же объекту, но вы скоро поймете, что манипулирование полями внутри объекта - грязный метод и идет в разрез с принципами хорошего объектно-ориентированного дизайна. Это не тривиальная тема, так что оставим ее для приложения A, которое посвящено эффекту наложения. Тем временем, вы должны отложить в мозгу, что присвоение для объектов может стать источником сюрпризов.


Содержание раздела