5 особенностей языка Java, о которых вы должны знать
О некоторых особенностях языка Java порой не знают сами джависты. Рассказываем о 5 особенностях этого языка, которые должен знать каждый.
☕ Подтянуть свои знания по Java вы можете на нашем телеграм-канале «Библиотека Java для собеса»
Инструкции можно переставлять
Рассмотрим пример:
int x = 1; int y = 3; System.out.println(x + y);
Взглянув на код, мы можем предположить, что сначала значение 1 будет присвоено x, а затем значение 3 – переменной y. Но если порядок присвоения переменных изменится, конечный результат окажется тем же: мы увидим в выводе 4. Используя это наблюдение, компилятор может безопасно изменить порядок этих двух операций присваивания в случае необходимости. Когда мы компилируем код, компилятор делает именно это: он может свободно переупорядочивать инструкции, если это не изменяет ожидаемое поведение системы.
И хотя перестановка в примере выше не приведет ни к каким положительным изменениям, есть ситуации, когда изменение порядка инструкций повысит производительность. Предположим, в нашем коде переменные x и y дважды увеличиваются в процессе чередования.
int x = 1; int y = 3; x++; y++; x++; y++; System.out.println(x + y);
Этот код должен вывести 8, и он сделает это даже при изменении порядка операций. В процессе оптимизации компилятор может полностью избавить код от операций приращения:
// Смена порядка инструкций int x = 1; int y = 3; x++; x++; y++; y++; System.out.println(x + y); // Сжатие инструкций int x = 1; int y = 3; x += 2; y += 2; System.out.println(x + y); // Полное сжатие инструкций int x = 3; int y = 5; System.out.println(x + y);
В действительности, компилятор продвинется еще на шаг дальше и переместит значения x и y сразу в print.
🧩☕ Интересные задачи по Java для практики можно найти на нашем телеграм-канале «Библиотека задач по Java»
В числовых значениях можно использовать нижние подчеркивания
Читать большие числа неудобно. Для удобства чтения в математике их принято разделять точкой: вместо 1183548876845 получается вполне читабельное 1.183.548.876.845.
К сожалению, в коде языка Java часто встречаются огромные числа в виде констант, как в примере ниже.
public class Foo { public static final long LARGE_FOO = 1183548876845l; } System.out.println(LARGE_FOO);
Конечно, содержимое констант редко претендует на эстетичность, но разбирать такое число глазами не очень удобно. К счастью, в Java Development Kit (JDK) седьмой версии появилась возможность разделять числа с помощью нижнего подчеркивания ( _ ) так же, как мы привыкли делать это с помощью точки.
public class Foo { public static final long LARGE_FOO = 1_183_548_876_845l; } System.out.println(LARGE_FOO);
Таким же образом, кстати, можно разделять и длинные хвосты десятичных дробей.
public static final double LARGE_BAR = 1.546_674_876;
Автоупаковка целочисленных кэшируется
В Java мы можем записать целочисленное значение следующим образом:
Integer myInt = 500;
Примитив int 500 преобразуется в объект типа Integer и сохраняется в myInt. Такая обработка называется автобоксинг или автоупаковка, поскольку преобразование примитива в объект типа Integer происходит автоматически.
Так как myInt является объектом типа Integer, мы ожидаем, что сравнение его с другим объектом того же типа и содержащего то же значение с помощью оператора == приведет к false. Но вызов сравнения для этих двух объектов приведет к true, так как оба объекта представлены одним значением – 500:
Integer myInt = 500; Integer otherInt = 500; System.out.println(myInt == otherInt); // false System.out.println(myInt.equals(otherInt)); // true
На этом этапе autoboxing работает точно так, как ожидается, но что произойдет, если мы попробуем это с меньшим числом? Например, 25:
Integer myInt = 25; Integer otherInt = 25; System.out.println(myInt == otherInt); // true System.out.println(myInt.equals(otherInt)); // true
Тут мы увидим, что оба объекта равны как по ==, так и по equals. Это означает, что два объекта Integer, фактически являются одним объектом. Такое странное поведение, на самом деле, не является ни ошибкой, ни недосмотром: оно было допущено умышленно. Поскольку многие операции автобоксинга выполняются с небольшими числами (до 127), JLS говорит о том, что значения Integer в диапазоне от -128 до 127 включительно кэшируются.
Файлы языка Java могут содержать множество не вложенных классов
В одном файле исходного кода может разместиться множество не вложенных классов, которые не являются public. Для них уровень доступа будет установлен как package-private (то есть, без модификатора доступа). При этом, в каждом файле может содержаться только один public класс.
public class Foo { //... } class Bar { //... } class Bar1 { //... }
После компиляции количество файлов будет равно количеству не вложенных классов.
StringBuilder используется для конкатенации строк
Конкатенация строк присутствует почти во всех языках программирования и позволяет соединять между собой строки или объекты и примитивы разных типов в одну строку. Одна из сложностей конкатенации в Java – неизменяемость строк. То есть мы не можем все время добавлять данные к одной и той же строке. Каждый append будет создавать новый объект String и продолжать работу с ним.
Это не критично, когда нужно произвести конкатенацию нескольких строк, но когда объемы вырастают до, скажем, 1000 строк, эта техника становится нестабильной, да и создавать тысячу сущностей String довольно расточительно.
Однако документация JLS сообщает, что подобного можно избежать. Для этого нужно использовать StringBuilder, который поработает как буфер. Строка, к которой идет присоединение других строк, будет существовать до тех пор, пока она нужна. Таким образом, при конкатенации будет существовать всего одна сущность String.
Для примера, соединение строк с помощью StringBuilder в цикле выглядит так:
StringBuilder builder = new StringBuilder(); for (int i = 0; i < 1000; i++) { builder.append("a"); } String myString = builder.toString();
Больше о возможностях языка Java:
- Встречаем Java 10 и разбираемся в главных нововведениях
- Пишем элегантный и чистый Java код в 5 этапов
- Скрытые возможности в Java