Константы

Фрагмент лекции JavaRush - университета.


— Знаешь ли ты, Амиго, что не все переменные в Java можно изменять?

— Это как, Ким? Ведь даже само название “переменные” говорит об изменении.

— Ещё как уверена. Во многих языках программирования, в том числе и в Java, есть константы, то есть переменные, значения которых нельзя изменять. И само название “константа” говорит о постоянстве.

— И для чего они нужны?

— Обычно это какие-то фундаментальные вещи наподобие числа Pi или количества дней в месяцах. Хотя в принципе программист может сделать любую переменную константой, если решит, что это необходимо.

— Например, имя, или цвет машины, или название дня недели?

— Правильно мыслишь. Всё, что не нужно менять.

— И как эти константы выглядят в Java?

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

final тип имя = значение;

— А что, если после создания такой константы попытаться присвоить ей другое значение?

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

— А если объявить final-переменную, но не присвоить ей значение?

— В этом нет смысла, поэтому так поступать в Java тоже запретили. final-переменную обязательно нужно инициализировать при объявлении, то есть присваивать ей значение. У этого правила есть одно исключение: можно перенести инициализацию переменной класса в конструктор. Но об этом ты узнаешь позже.

— Но не всё то константа, что final. Чтобы уменьшить количество ключевых слов, разработчики Java используют слово final не только для объявления констант. final также может быть указан перед методом и даже классом. Методы, объявленные как final, нельзя переопределять (override), а от класса, объявленного как final, нельзя наследоваться.

— Эмм… Переопределять? Наследоваться? Это ты на каком языке сейчас говоришь?

— На языке объектно-ориентированного программирования. Совсем скоро ты вплотную к нему приступишь. А пока просто насладись красивыми терминами.

— Хорошо. Получается, final можно добавлять перед переменными, классами и методами, и это слово делает их в каком-то смысле неизменными?

— Да. Кроме того, модификатор final можно добавлять перед любыми переменными: локальными, параметрами, полями класса и статическими переменными.

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

— Не совсем понял.

— Сейчас поймешь. Вот тебе пример:

final int[] data = {1, 2, 3, 4, 5, 6};

data = new int[]{6, 7, 8, 9};

data[0] = 0;
data[1] = 0;
data[2] = 0;
Создаем массив.

Так нельзя: переменная data объявлена как final.

А так можно.
И так можно.

— Понял. Хитро.

Глобальные константы

— Как думаешь, что такое глобальные константы?

— Наверное, глобальные константы — это как глобальные переменные, только константы?

— Именно. Если тебе нужно объявить в своей программе глобальные константы, создай статические переменные класса, и сделай их public и final. Для имен таких переменных существует специальный стиль написания: они пишутся заглавными буквами, а в качестве разделителя слов выступает символ подчеркивания.

Примеры:

class Solution
{
   public static final String SOURCE_ROOT = "c:\\projects\\my\\";
   public static final int DISPLAY_WIDTH = 1024;
   public static final int DISPLAY_HEIGHT = 768;
}

Затенение переменных

— Как я уже говорила раньше, в одном методе нельзя создать несколько локальных переменных с одинаковыми именами. В разных методах — можно.

— Это я знаю!

— Но вот то, чего ты, скорее всего, не знаешь, так это того, что у переменных класса и локальных переменных метода вполне могут быть одинаковые имена.

Пример:

Код Доступность переменных
public class Solution
{
   public int count = 0;
   public int sum = 0;

   public void add(int data)
   {
     sum = sum + data;
     int sum = data * 2;
     count++;
   }
}

count, sum
count, sum
count, sum
count, sum
count, sum
count, sum, data
count, sum, data
count, sum, data
count, sum, data
count, sum

— В методе add мы объявили локальную переменную sum, и она до конца действия метода перекрывает (или ещё говорят затеняет) собой переменную класса sum.

— Хм… Я бы сказал, в каком-то смысле это ожидаемое поведение.

— Но это еще не все. Оказывается, если переменную класса затеняет локальная переменная, в методе все-таки существует способ обратиться к переменной класса. Для этого нужно перед ее именем написать ключевое слово this:

this.имя

— Вот пример, где конфликт имен успешно решается:

Код Доступность переменных
public class Solution
{
   public int count = 0;
   public int sum = 0;

   public void add(int data)
   {
     int sum = data * 2;
     this.sum = this.sum + data;
     count++;
   }
}

this.count, this.sum
this.count, this.sum
this.count, this.sum
this.count, this.sum
this.count, this.sum
this.count, this.sum, data
this.count, this.sum, data, sum
this.count, this.sum, data, sum
this.count, this.sum, data, sum
this.count, this.sum

Везде переменные count и sum доступны как с ключевым словом this, так и без него. В тех строках, где локальная переменная sum затеняет переменную класса sum, доступ к переменной класса sum возможен только при использовании this.

— С этим явно надо попрактиковаться.

— Ещё успеешь.

— А что, если затеняется не просто переменная класса, а статическая переменная класса? К ней же не обратишься через this?

— Всё верно, через this не получится. Обращаться к ней нужно через имя класса:

ClassName.имя

Пример:

Код Доступность переменных
public class Solution
{
   public static int count = 0;
   public static int sum = 0;

   public void add(int data)
   {
     int sum = data * 2;
     Solution.sum = Solution.sum + data;
     count++;
   }
}

Solution.count, Solution.sum
Solution.count, Solution.sum
Solution.count, Solution.sum
Solution.count, Solution.sum
Solution.count, Solution.sum
Solution.count, Solution.sum, data
Solution.count, Solution.sum, data, sum
Solution.count, Solution.sum, data, sum
Solution.count, Solution.sum, data, sum
Solution.count, Solution.sum

— Обрати внимание: везде статические переменные count и sum доступны как с префиксом в виде имени класса Solution, так и без него. В тех строках, где локальная переменная sum затеняет переменную класса sum, доступ к переменной класса sum возможен только при использовании префикса Solution.

Фрагмент лекции JavaRush - университета.


Переменные внутри цикла for

— И ещё один маленький, но интересный факт. Есть ещё место, где особенным образом объявляется переменная — это цикл for. Обычно у цикла for в круглых скобках объявляется переменная-счетчик. И какова же будет видимость этой переменной? Она ведь не находится в теле цикла — значит, весь метод? Или всё-таки нет?

— Мне уже что-то об этом рассказывали. Я так понял, что переменная, объявленная в заголовке цикла for, видна только в теле цикла и в заголовке цикла for.

— Молодец, Амиго. Но всё-таки посмотри на пример для закрепления:

Код Доступность переменных
public static void main(String[] args)
{
   int a = 0;

   for (int i = 0; i < 10; i++)
   {
     System.out.println(i);
   }

   System.out.println("end");
}


a
a
a, i
a, i
a, i
a
a
a

— Получается, я в своём коде могу последовательно написать несколько циклов с переменной-счетчиком с одинаковым именем, и проблем не будет?

Не будет. Вот, посмотри:

Код Доступность переменных
public static void main(String[] args)
{
   int a = 0;

   for (int i = 0; i < 10; i++)
   {
     System.out.println(i);
   }

   for (int i = 0; i < 10; i++)
   {
     System.out.println(i);
   }

   System.out.println("end");
}


a
a
a, i
a, i
a, i
a
a
a, i
a, i
a, i
a
a
a

undefined
7
Опрос
Функции,  7 уровень,  7 лекция
недоступен
Функции
Функции