1. Переменные-ссылки

В языке Java переменные могут быть двух видов: переменные примитивного типа и все остальные. Вот про «все остальные» мы сейчас и поговорим.

На самом деле правильнее будет сказать, что есть переменные примитивного типа (primitive type variables) и ссылочные переменные (reference variables). Так что же это такое, эти ссылочные переменные?

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

Значения прямо внутри переменных хранят только примитивные типы, все остальные типы хранят только ссылку на объект. Ранее вы уже, кстати, сталкивались с двумя такими типами переменных — это переменные типа String и переменные типа массив.

И массив, и строка являются объектами, которые хранятся где-то в памяти. Переменные типа String и переменные типа массив хранят только ссылки на объекты.

Переменные int a, int b и double d — примитивные и хранят значение внутри себя.

Переменная String str — ссылочная и хранит адрес (ссылку) объекта типа String в памяти.

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


2. Суть ссылок

В чем принципиальное отличие переменных-ссылок от примитивных переменных?

Примитивная переменная — как коробка: в ней можно хранить какое-нибудь значение. Переменная-ссылка больше похожа на листок бумаги с телефонным номером на нем.

Машина vs ключи от машины

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

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

Человек vs его телефонный номер

Или еще один вариант: человек и его телефонный номер. Телефонный номер — не сам человек, но номер телефона можно использовать, чтобы звонить ему, спрашивать у него какую-то информацию, давать команды.

Ссылка тоже используется для взаимодействия с объектом. Все объекты взаимодействуют друг с другом при помощи ссылок. Вместо того, чтобы «обмениваться людьми», достаточно обменяться телефонными номерами.

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

Ссылка даёт ещё одно преимущество: можно передать ссылку на объект в какой-нибудь метод, и этот метод будет в состоянии модифицировать (изменять) наш объект, используя ссылку на него, вызывая его методы и обращаясь к данным внутри объекта.


3. Присваивание ссылок

При присваивании ссылочных переменных происходит просто присваивание адреса объекта в памяти. Сами объекты при этом не появляются и не исчезают.

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

Присваивание ссылок

Размер всех переменных-ссылок (независимо от типа) одинаков и составляет 4 байта (как тип int). Но! Если ваше приложение запущено на 64-х разрядной Java-машине, размер всех ссылок будет 8 байт (64 бита).

Ссылки при этом можно только присваивать друг другу. Вы не можете менять ссылки или присваивать им произвольные значения:

Код Описание
String hello = "Привет";
String s = hello;
Так можно
String hello = "Привет";
hello++;
А так — нельзя
String hello = 0x1234;
И так — нельзя

4. Пустая ссылка — null

А что хранит переменная-ссылка, если ей еще ничего не присвоили?

А хранит она пустую ссылку — null. null — это специальное ключевое слово в Java, обозначающее отсутствие ссылки (пустую ссылку). Значение null можно присвоить любой ссылочной переменной.

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

Примеры:

Код Описание
class Person
{
   public static String name;
   public static int age;
}


Переменная String name имеет значение по умолчанию: null.
Переменная int age имеет значение по умолчанию: 0.

Локальные переменные без значения считаются неинициализированными как для примитивных типов, так и для типов-ссылок.

Если переменная хранит ссылку на какой-то объект, а вы хотите стереть значение переменной, просто присвойте ей ссылку null.

Код Описание
String s = null;
s = "Привет";
s = null;
s хранит ссылку null.
s хранит ссылку на объект-строку
s хранит ссылку null

5. Передача ссылок в методы

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

Пример:

Код Описание
class Solution
{
   public static void fill(String[] array, String value)
   {
      for (int i = 0; i < array.length; i++)
        array[i] = value;
   }

   public static void main(String[] args)
   {
     String[] data = new String[10];
     fill(data, "Hello");
   }
}


Метод fill заполняет переданный массив array переданным значением value.

При вызове метода fill переменной array присваивается ссылка на массив data. Переменной value присваивается ссылка на объект-строку «Hello».

Вот как будет выглядеть ситуация в памяти перед вызовом метода fill:

Передача ссылок в методы

Вот как будет выглядеть ситуация во время работы метода fill:

Передача ссылок в методы 2

Переменные data и array ссылаются (хранят ссылки) на один и тот же массив-контейнер в памяти.

Переменная value хранит ссылку объекта строки Hello.

Ячейки массива тоже хранят просто ссылки на объект Hello.

Фактически никакого дублирования объектов не происходит — копируются только ссылки.


undefined
8
Задача
Java Syntax Pro, 8 уровень, 3 лекция
Недоступна
Массив значений
Часто перед началом использования массива его необходимо заполнить значениями по умолчанию. Реализуй такое заполнение в методе fillArray(Integer[] array, int value). В качестве аргументов метод принимает массив и значение, которым его необходимо заполнить. Массив заполняется полностью, независимо от ег
undefined
8
Задача
Java Syntax Pro, 8 уровень, 3 лекция
Недоступна
Гибкое заполнение массива
Иногда необходимо заполнить какую-то часть массива одинаковыми значениями или же удалить существующие (присвоить значение null). Реализуй для этого более гибкий метод fillArray(Integer[] array, int value, int begin, int end), где: - array — массив, который необходимо заполнить; - value — значение, которым

6. Сравнение с языком С/С++

Иногда Java-программистов спрашивают на собеседовании: как передаются данные в методы в Java? Иногда еще уточняют: по ссылке или по значению?

Этот вопрос идет из языка С++ — в языке Java он не имеет смысла. В Java переменным-параметрам всегда просто присваиваются значения переменных-аргументов. Так что правильным ответом будет — по значению.

Но будьте готовы к тому, чтобы объяснить свою позицию, т.к. вам могут тут же возразить, что «примитивные типы передаются по значению, а ссылочные – по ссылке».

Истоки этой проблемы связаны с тем, что многие Java-программисты были в прошлом С++ программистами. А там вопрос «как передаются параметры в методы» играл очень важную роль.

В Java все однозначно: примитивные типы хранят значения, ссылочные тоже хранят значение — ссылку. Все дело в том, что считать значением переменной.

В языке C++ в переменной можно было хранить как ссылку на объект, так и сам объект. То же касалось примитивных типов: в переменной можно было хранить значение или объявить переменную ссылкой на тип int. Поэтому чтобы не путаться, С++ программисты ссылку на объект всегда называют ссылкой, а сам объект — значением.

В С++ легко могла сложиться ситуация, что одна переменная содержит объект, а другая — ссылку на этот же объект. Поэтому вопрос, что хранит в себе переменная — сам объект или только ссылку на него — был очень важен. При передаче в метод объекта он копировался (если передавался по значению), и не копировался (если передавался по ссылке).

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