Ссылочные типы

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

Рассмотрим ссылочные типы в Java. К таким типам относятся массивы, классы и String. Рассмотрим поподробнее.

Эти типы хранятся в памяти как и все, но переменные хранят ссылки на ячейки памяти в которых хранятся эти типы, и если мы копируем эту ссылку в другую переменную к примеру, то сможем потом в дальнейшем в коде изменить объект хранящийся в памяти по ссылке и все переменные что указывали на этот объект будут иметь обновленный объект, но НЕ ссылку.

Это достаточно сложно для понимания, я советую перечитать этот абзац несколько раз.

Возможно на примере все станет гораздо проще и понятнее. Давайте рассмотрим следующий пример.

class A {

  private String innerValue;
 
  A() {
      this.innerValue = "Empty";
  }

  A(String innerValue) {
      this.innerValue = innerValue;
  }

  String getInnerValue() {
      return innerValue;
  }

  void setInnerValue(String innerValue) {
      this.innerValue = innerValue;
  }

}

public class Main {

  private static void changeObject(A object) {
      object.setInnerValue("changed value");
  }

  public static void main(String[] args) {

      A object = new A("simple string");
      System.out.println(object.getInnerValue()); // simple string

      changeObject(object);
      System.out.println(object.getInnerValue()); // changed value

  }

}

В этом примере создается класс А, затем создается его объект, после чего мы выводим на консоль текущее хранимое значение в объекте А, затем передаем объект А в метод changeObject, который в свою очередь устанавливает новое значение для innerValue. И дальше мы выводим измененное значение на консоль. Значение в объекте поменяется, хоть мы его и передаем в отдельный метод. В принципе тут все логично. Просто нужно запомнить что все-все объекты передаются по копии ссылки. Как раз то, о чем я говорил. переменная object при создании объекта A сохраняет в себя ссылку на этот объект, дальше, когда мы передаем этот объект в метод changeObject в параметр метода копируется ссылка на объект A. Теперь у нас есть две переменных которые имеют ссылку на один и тот же объект в памяти. Это локальная переменная object метода main и аргумент метода changeObject. Если мы изменяем объект по ссылке в методе changeObject, то он меняется и для ссылки в переменной object метода main. НО заметьте, что саму ссылку мы для локальной переменной object метода main из метода changeObject изменить не сможем. Это значит что если мы присвоим аргументу object метода changeObject NULL, то это никак не повлияет на переменную object в методе main.

Разберемся с массивами.

В Java все примитивные типы передаются по значению, а не по ссылке, то есть.

int a = 10;
int b = a;

b = 44;

System.out.println(a); // 10

Получается что мы изменив переменную b никак не влияем на переменную a. А теперь возьмем следующий пример.

public class Main {

  private static void changeArray(int[] input) {
      input[0] = 2;
  }

  public static void main(String[] args) {

      int[] input = { 1 };
      System.out.println(input[0]); // 1

      changeArray(input);
      System.out.println(input[0]); // 2

  }

}

Да, как вы уже знаете, массив передается по ссылке и значение 0-го элемента меняется. Для меня это казалось нелогичным когда я изучал Java, потому что примитивы передаются по значениям, а массивы по ссылкам. Сейчас это кажется очевидным решением, потому что массивы могут хранить не только примитивы, но и объекты, да и в принципе чтобы можно было его менять на лету, но с этим нужно быть осторожным, чтобы потом не засесть на несколько часов с поиском проблемы от которой данные в массиве меняются :).

А теперь разберемся со строками. Начнем с примера который я потом объясню.

public class Main {

  public static void main(String[] args) {

      String s1 = "str1";
      String s2 = s1;

      System.out.println(s1 == s2); // true
      System.out.println(s1.equals(s2)); // true

      String s3 = new String(s2);

      System.out.println(s2 == s3); // false
      System.out.println(s2.equals(s3)); // true

      s1 += "Ex";
      System.out.println(s1 == s2); // false

      s1 = s2;
      System.out.println(s1 == s2); // true

   }

}

Вначале мы создаем две строки, первую со значением str1, а вторую из строки s1.

Далее мы их сравниваем s1 == s2 (для тех кто еще не знает, тут сравниваются ссылки на ячейку памяти, а не значения строк), в результате true. Да, эти две строки указывают на одну ячейку памяти. Значит это ссылка, то есть мы предполагаем, если мы изменим строку s2 то и должна поменяться строка s1. Но нет. Строки в Java иммутабельные, и если мы как-то изменить одну строку, то для нее создается новая ячейка в памяти и туда сохраняется измененная строка, соответственно и ссылка на нее тоже меняется и s1 == s2 уже false. Но если вернуть прежнее значение строке s2, то обе строки снова будут указывать на одно значение в памяти. Это вроде правильно называется как-то “Пул значений”.

На этом все, если остались вопросы, пишите в комментариях. Спасибо.

Java
27.10.2016
1 ответ
авторизуйтесь чтобы ответить