Пожаловаться

5 особенностей языка Java, о которых вы должны знать

16717
Пожаловаться

О некоторых особенностях языка Java порой не знают сами джависты. Рассказываем о 5 особенностях этого языка, которые должен знать каждый.

Инструкции можно переставлять

Рассмотрим пример:

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.

В числовых значениях можно использовать нижние подчеркивания

Читать большие числа неудобно. Для удобства чтения в математике их принято разделять точкой: вместо 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:

 

16717

Комментарии

Рекомендуем

BUG!