Поля класса. Ключевое слово static. Константы

Поля класса. Ключевое слово static. Константы





ВАЖНО: ниже находится устаревшая версия статьи. Актуальную можно найти здесь: ссылка






Поля класса

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

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

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

·      byte, short, int, long – инициализируются значением 0;

·      char – инициализируется символом, код которого – 0. Данный символ нечитаем, но он есть. Вы можете прочесть больше, загуглив «\u0000»;

·      double, float – значением по умолчанию будет 0.0;

·      boolean – будет инициализирован значение false;

·      Абсолютно все ссылочные типы, включая хорошо известный нам String – инициализируются значением null.

Если с примитивами все просто, то с null мы ранее не сталкивались. Это ключевое слово, которое означает, что переменная ссылочного типа не содержит никакой ссылки на объект. Соответственно, обращение к полям и методам такой переменной завершится с ошибкой (исключением). Поэтому при работе с ссылочными типами достаточно часто используется проверка на null вида (object != null).

Также стоит рассмотреть способы инициализации полей класса. Мы уже знакомы с инициализацией с помощью конструктора, также в статье на metanit было упоминание о блоках инициализации (они нас не интересуют, поскольку данный функционал де-факто не используется).

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

И последним, хоть и самым очевидным, способом первичной инициализации является инициализация при объявлении. Ровно также, как мы часто делаем с переменными. Инициализировать таким образом мы можем поля и примитивных, и ссылочных типов:


public class Car {

           public int maxSpeed = 240;

           public String color = "Красный";

           public Counter mileage = new Counter("Пробег"); 

}

 

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


public class FullName {

           public String firstName = "Иван";

           public String lastName;

           public String fullName = firstName + " " + lastName;

}


При этом нет разницы, инициализированы использованные поля явно или нет. В нашем примере поле fullName будет инициализировано значением "Иван null".

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

 

Ключевое слово static

Мы уже встречались со static’ом ранее, при знакомстве с методами, но использовали его без понимания того, что он делает и для чего нужен.

Данное ключевое слово можно применить к полю и методу (еще к вложенному классу, но не трогайте, это на Новый Год).

В обоих случаях данное ключевое слово будет означать, что поле/метод относится не к объекту класса, а непосредственно к классу. Т.е. нам не нужно создавать объект, чтобы использовать данное поле/метод.

Ярким примером static-поля может быть поле out класса System: System.out – мы обращаемся к нему, не создавая объект класса System. Общий синтаксис обращения к статическому полю: имя_класса.имя_поля.

Наиболее распространенное применение статических полей – константы. В следующем подразделе мы разберем их подробнее.

Статические методы, в свою очередь, хорошо демонстрирует уже известный нам класс Math. Например: Math.pow(2, 3). Здесь мы вызываем метод, не создавая объект класса Math. Общий синстаксис: имя_класса.имя_метода([аргументы метода]).

Наиболее распространенное (и, практически, единственное) применение static-методов – утилитарные методы, в которых объект не нужен. Например, математические операции, простейшие конвертеры и пр.

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

Немного о нюансах использования. С методами это не так критично (разберем чуть ниже), но с полями неграмотное использование static может сыграть злую шутку. Рассмотрим на примере:


public class Counter {

           public static int counter;

}

 

public static void main(String… args) {

           Counter counter1 = new Counter();

           Counter counter2 = new Counter();

 

           counter1.counter++;

           counter2.counter++;  

 

           System.out.println(counter1.counter + " " + counter2.counter);

}

 

Вывод на консоль:

2 2

 

Поскольку static-поля относятся именно к классу, а не объекту, значение такого поля будет общим на весь класс. По сути, код выше равноценен следующему:


public class Counter {

           public static int counter;

}

 

public static void main(String… args) {

           Counter.counter++;

           Counter.counter++;   

 

           System.out.println(Counter.counter + " " Counter.counter);

}

 

Использование static-методов имеет свои ограничения: мы не можем внутри статического метода обратиться к не статическому полю/методу, не создав экземпляр класса (использовать this – тоже не можем). Т.е:

public class SthClass {

public static void doSth() {

                       doSth1();

}

 

           public void doSth1() {

           //Какая-то логика

}

}

Приведет к ошибке компиляции. Чтобы подобный код заработал, нужно сделать следующее:

public class SthClass {

public static void doSth() {

           SthClass sthObject = new SthClass();

                       sthObject.doSth1();

}

 

           public void doSth1() {

           //Какая-то логика

}

}

К слову, именно поэтому при первом знакомстве с методами я рекомендовал помечать их как static – чтобы мы могли их использовать в статическом методе main() до того, как изучим конструкторы.

Однако если вы в рамках статического метода создаете объект того же класса – почти гарантированно вы где-то свернули не туда. Исключения есть, но к моменту, когда они вас коснутся, использование static вряд ли будет вызывать у вас какие-либо вопросы.

 

Константы

Мы уже знакомы с тем, как объявить переменную-константу внутри метода. Константы уровня класса достаточно похожи, для их объявления также используется ключевое слово final. Но есть и отличия.

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

Однако под константными полями обычно подразумеваются не просто final-поля (в каком-то смысле, их можно назвать константами уровня объекта и они тоже имеют право на жизнь), а поля, помеченные как static final – константы уровня класса.

В такие поля часто выносят литералы, которые не будут изменяться в ходе программы. Это могут быть какие-то строки, особенно, если одно строковое значение используется в рамках класса (или приложения) несколько раз, числа, особенно, если это какие-либо коэффициенты для расчетов и т.д.

Примером такой константы может быть

public static final double PI = 3.14159265358979323846;

в классе Math. Обратите внимание на нейминг: константы называют прописными буквами, слова разделяются символом ' _'. Например: SOMETHING_CONSTANT. Это сделано для того, чтобы отличать константные поля от обычных.

Если мы вспомним Задачу 3 (рисование прямоугольника) из урока про циклы, в константы стоило бы вынести ' -', '|', ' '. Ведь если мы захотим нарисовать прямоугольник другими символами, заменить их будет проще в одном месте, чем искать по коду, особенно, если он разделен на методы.

Обращение к константе уровня класса ничем не отличается от обращения к обычному статическому полю. Кроме того, что изменить значение такого поля мы не сможем.

К слову, final-методы тоже существуют, но в них смысл слова final уже иной. Мы можем даже создавать final-static-методы. Другой вопрос, что такое объявление избыточно, почему – разберем в уроке, посвященном наследованию в Java.

 

С теорией на сегодня все!


Переходим к практике:

Задача 1:

Используя кодовую базу из задачи https://github.com/KFalcon2022/practical-tasks/blob/master/src/com/walking/lesson6_methods/Task3.java вынести строковые и символьные литералы в константы. Попробуйте нарисовать прямоугольник, используя "==" для каждой единицы длины и "||" – для каждой единицы ширины.

Также попробуйте записать в константу переменную scanner. Упростится ли использование сканера внутри методов чтения с клавиатуры?


Задача 2:

Для задачи https://github.com/KFalcon2022/practical-tasks/tree/master/src/com/walking/lesson8_classes_objects реализуйте неизменность поля названия у класса Counter. Ведь очень странно, если мы можем менять название счетчика по ходу выполнения программы, не так ли?

 

Задача 3:

Используя задачу https://github.com/KFalcon2022/practical-tasks/blob/master/src/com/walking/lesson7_varargs_overloading/Task5.java (можете сделать на основе своего решения, но для наглядности удобства новых возможностей рекомендую взять за основу решение по ссылке):

1.    Вынесите поиск простых чисел в отдельный класс.

2.    Реализуйте возможность вывода на экран суммы N первых простых чисел, где N – число, введенное пользователем с клавиатуры;

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

Примечание: это одна задача, а не различные варианты:)


Если что-то непонятно или не получается – welcome в комменты к посту или в лс:)

Канал: https://t.me/+relA0-qlUYAxZjI6

Мой тг: https://t.me/ironicMotherfucker

 

Дорогу осилит идущий! 

Report Page