Практически любой, начинающий программировать на языке Java, рано или поздно сталкивается с таким вопросом:

Дан фрагмент программы:
Код:
. . .
String st1 = Hello; // 1
String st2 = Hello; // 2
String st3 = new String(Hello); // 3
. . .
Определить значения st1==st2, st1==st3, st1.equals(st2) и st1.equals(st3) в результате исполнения этого фрагмента.

Этот вопрос кочует из теста в тест, от одного собеседования при приеме на работу к другому, и даже тесты на получение сертификата от компании Sun Microsystems могут включать его в том или ином виде.

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

1.Простые переменные и ссылочные переменные в языке Java.

Вам, наверняка, приходилось сталкиваться в ходе изучения языка Java с простыми и ссылочными переменными. Иногда у новичков возникает определенная путаница в этом вопросе, так что давайте еще раз повторим, что это такое и когда какие виды переменных создаются.

Допустим, что вы написали в программе:
int a;
a = 10;

При этом компилятор отвел в памяти место, необходимое для хранения данных типа int (рис. 1) и заполнил это место значением 10, присвоенным переменной (рис. 2).


Рис 1


Рис 2
Так создается и используется простая переменная.

Теперь допустим, что вы написали:
String b;
b = Hello;

При этом компилятор не стал в результате первой команды отводить место, необходимое для текста Hello. Вместо этого он отвел место, необходимое для ссылки (указателя) на ЛЮБОЙ текст (рис. 3). Далее это место заполняется ссылкой на текст Hello, который был присвоен переменной (рис. 4). То есть, на самом деле, переменной b был присвоен не сам текст, а ссылка на него.


Рис 3


Рис 4
Так создается и используется ссылочная переменная.

По поводу использования простых и ссылочных переменных в языке Java существует только одно, зато очень важное правило: Java использует простые переменные ТОЛЬКО для при создании одиночных переменных базовых типов (byte, short, int, long, float, double, char, boolean). Во всех остальных случаях создаются ссылочные переменные. То есть, как при создании объектов любых классов, так и при создании массивов используются ссылки. Это правило работает автоматически и не может быть изменено программистом.

2.Оператор == (проверка на равенство).

Оператор == проверяет на равенство содержимого переменные любых типов (обратите внимание на ключевые слова "равенство содержимого" и "переменные любых типов"). При этом, если содержимое операндов одинаково, результат исполнения оператора == будет true, в противном случае - false. Конечно, в качестве операнда для этого оператора может выступать и константа, но для рассматриваемого вопроса это не важно.

3.Метод equals().

Метод equals( ) определен для объектов любых классов, но только для объектов. Он вызывается из одного объекта и принимает в качестве параметра другой объект. Далее он сравнивает содержимое двух вышеназванных объектов, и, если оно одинаковое, возвращает true, в противном случае - false. Понятие одинаковое в данном случае может быть определено программистом при создании класса, но обсуждение этого вопроса выходит за рамки данной статьи.

4.Память, ссылки и константы.

Теперь настало время задать себе вопрос: "А откуда берется слово Hello, изображенное на рисунке 4, или число 10, изображенное на рисунке 2, во время исполнения программы?" Естественно, не из воздуха. И текст "Hello", и число 10 в приведенных примерах, равно как и другие константы, находятся где-то в памяти компьютера. Детальное описание распределения памяти для нас сейчас не важно.

А что же тогда представляет из себя ссылка, помещаемая в ссылочную переменную b на рис. 4? Просто указатель на область в памяти, где хранится слово "Hello".

А теперь представим себе, что происходит, когда мы используем одну и ту же константу несколько раз в нашей программе. Можно, конечно, каждый раз выделять соответствующий кусочек памяти и хранить одну и ту же константу много раз, но это неэффективно. Все равно, никакой возможности изменить в ходе работы программы содержимое памяти, где лежит константа, нет, это ведь константа! Поэтому компилятор отводит место под константу один раз, а дальше она используется по мере необходимости.

5.Оператор new.

Оператор new в языке Java всегда создает новый объект и возвращает ссылку на этот объект.

6.Решение задачи.

Вот мы и подошли к решению приведенной в начале задачи. Давайте пройдем по строчкам кода и представим себе, что при этом творится в памяти.

String st1 = Hello; // 1

<Рис.5>
Рис. 5. В этот момент в памяти отведено место под строчку "Hello", создана переменная st1 и в эту переменную помещена ссылка на строчку.

String st2 = Hello; // 2

<Рис.6>
Рис. 6. Здесь мы вторично используем ту же самую строку "Hello". Как уже говорилось выше, вторично место под константу не отводится, используется та же самая константа. Таким образом, в двух переменных, st1 и st2 лежат ссылки на одну и ту же строку.

String st3 = new String(Hello); // 3

Рис.7
Рис. 7. Здесь, как мы видим, использован оператор new, который всегда создает новый объект. В данном случае создается объект класса строка, и при его создании конструктору передается константа Hello в качестве параметра. Конструктор создает новый объект, используя для его инициализации все ту же константу, однако, возвращаемая оператором new ссылка на новый объект.

Теперь, с помощью последнего рисунка, мы легко ответим на вопрос задания.

st1==st2 сравнивает значения ссылочных переменных st1 и st2. В них лежат ссылки на одну и ту же строку, то есть указатели на одну и ту же память, естественно, одинаковые. Таким образом, содержимое переменных одинаково, а значит, результат будет true.

st1==st3 сравнивает значения ссылочных переменных st1 и st3. В них лежат ссылки на разные области памяти, и при этом абсолютно не важно, что в этих областях размещается одинаковый текст. Текст оператор не проверяет, он проверяет содержимое переменных. Как мы видим, содержимое переменных разное, а значит, результат будет false.

st1.equals(st2) сравнивает содержимое объектов, адресуемых переменными st1 и st2. В данном случае это один и тот же объект, так что, естественно, содержимое совпадает, а следовательно, результат будет true.

st1.equals(st3) сравнивает содержимое объектов, адресуемых переменными st1 и st3. Хотя, в отличие от предыдущего случая, объекты эти разные, их содержимое совпадает, а следовательно, результат опять будет true.
Information
  • Posted on 31.01.2010 19:10
  • Просмотры: 387