JavaRush /Java блог /Random UA /Java 13: нові можливості

Java 13: нові можливості

Стаття з групи Random UA
Ми вже встигли звикнути до того, що новий реліз JDK з'являється раз на півроку. Поки що такий підхід себе виправдав, а переживання деяких розробників, мовляв, вони не встигатимуть за оновленнями, виявабося марними: піврічних змін трохи і вони не такі глобальні, як раніше. Ну а програмісти-початківці можуть і зовсім не помітити нововведення. Java 13: нові можливості - 1Тим не менш, майбутнім розробникам ПЗ краще бути в курсі нововведень. У цій статті ми традиційно опишемо прийняті пропозиції щодо розширення (JEP). Java 13 включає лише п'ять JEP'ів і 76 нових елементів базової бібліотеки (з яких майже половина — прості доповнення до пакету java.io).

JEP 355 : Text Blocks (Preview)

Почнемо із зміни синтаксису мови. Найзначніша з них – це текстові блоки. Вони дозволяють уникати екранування символів та вміють форматувати рядки. Можливо, ви пам'ятаєте , що в JDK 12 так і не з'явилася очікувана фіча Raw String Literals (JEP 326) для роботи з рядковими літералами. У Java 13 їй на зміну прийшов JEP 355 із його текстовими блоками. Ви, найімовірніше, пам'ятаєте, що Java рядок обгорнуто в подвійні лапки. Це добре, але проблема в тому, що рядок не може займати більше однієї лінії вихідного файлу (щоб не плутати з Java-рядком, тут називатимемо рядок файлу "лінією"). Що ж, підемо в обхід і використовуємо, наприклад, символ\nякщо потрібно розрив, або конкатенацію багаторядкових виразів. Не дуже гарно виходить! Особливо громіздко виглядають текстові літерали із вбудованими фрагментами HTML, XML, SQL чи JSON. Всі ці екранування, конкатенації, ручне редагування робить код незручним для написання і важко читати. Текстові блоки намагаються вирішити цю проблему. Вони починаються ЕЕЕ ... з потрійних подвійних лапок і закінчуються ними ж (знаю, звучить не дуже). Все, що знаходиться між лапками, інтерпретується як частина рядка, включаючи переклади на нову лінію. Текстові блоки можна використовувати точно так, як стандартні текстові літерали, Java скомпілює код однаково. Відкриваючі лапки повинні супроводжуватися обмежувачем рядка; текстові блоки не можна використовувати в одну лінію, тому код
String smallBlock = """Only one line""";
призведе до таких помилок:
TextBlock.java:3: error: illegal text block open delimiter sequence, missing line terminator
   String smallBlock = """Text Block""";
                          ^
TextBlock.java:3: error: illegal text block open delimiter sequence, missing line terminator
   String smallBlock = """Text Block""";
                                   	^
Простий HTML-фрагмент тепер можна написати так:
String htmlBlock = """
               	<html>
                 	<body>
                   	<p>JavaRush Web page</p>
                 	</body>
               	<html>
         	     """;
Згадаємо кілька тонкощів, про які краще знати під час використання текстових блоків. Розташування лапок виявляється важливим: воно визначає, як обробляється випадковий пробіл. У наведеному вище прикладі лапки, що закривають, вирівняні з відступом тексту HTML. У цьому випадку компілятор видалить прогалини відступу, і в результаті ми отримаємо такий рядок:
<html>
  <body>
    <p>My web page</p>
  </body>
</html>
Зверніть увагу:такий рядок міститиме нову лінію в кінці рядка. Якщо вона не потрібна, закриваючі лапки “”” можна поставити безпосередньо після тега</html>. Якщо ми перемістимо лапки, що закривають, ближче до лівого поля, це змінить кількість віддалених відступів. Якби ми перемістабо їх на два пробіли вліво, ми додали б два пробіли для відступу до кожної лінії рядка. Переміщення до лівого краю призведе до збереження всіх відступів. Переміщення лапок далі вправо не матиме жодного ефекту і не додасть більше відступів. У версію JDK 13 текстові блоки потрапабо як прев'ю-фічі. Це означає, що вони поки що не включені у відповідну специфікацію мови Java. Тобто незрозуміло, чи стане ця фіча постійною частиною мови, чи вона тут лише гість. В даний час розробники можуть протестувати фічу та висловити думку про неї. Від нього і залежатиме доля текстових блоків: фічу можуть покращити, а якщо вона не сподобається — то й зовсім вилучити. Якщо ви хочете випробувати текстові блоки на практиці, пам'ятайте, що прев'ю-фічі потрібно явно включати для компіляції та виконання. Компіляція:

javac --enable-preview --release 13 TextBlock.java
Щоб запустити програму, потрібно увімкнути функції попереднього перегляду:

java --enable-preview TextBlock
У класі Stringє три нові методи, які доповнюють цю зміну мови:
  • formatted(): форматує рядок, використовуючи сам рядок як рядок формату. Еквівалентний викликуformat(this, args)
  • stripIndent(): видаляє випадкові пробіли з рядка. Це корисно, якщо ви читаєте багаторядкові рядки і хочете застосувати такий самий виняток випадкових прогалин, як і у разі явного оголошення.
  • translateEscapes(): повертає рядок з escape-послідовностями (наприклад, \ r), переведеними у відповідне значення Unicode.
Цікаво, що ці методи тільки з'явабося, але вже помічені як deprecated ... такий стан речей наводить на думку, що їх можуть вилучити в майбутній версії JDK. Це здається дещо ексцентричним — додавати новий метод і одразу відмовлятися від нього. Однак потрібно врахувати, що ці методи пов'язані з прев'ю-фічею, яка може бути змінена або видалена. Можливо, введення анотації @PreviewFeatureдопомогло б з подібними ситуаціями, але поки що така вона не входить до JDK (хоча з високою ймовірністю вона з'явиться в JDK 14).

JEP 354 : Switch Expression (Preview)

У Java 12 з'явилася пропозиція про нову форму написання виразів з оператором switch - JEP 325 . Воно виявилося найпершою прев'ю-фічею і її доля доводить, що виносити пропозиції на суд користувачів - чудова ідея. До JDK 12 switchможна було використовувати лише як оператор, який виконує дію, але не повертає результату. А ось у Java 12 дозволила використовувати switchяк вираз, що повертає результат, який можна присвоїти змінній. Були й інші зміни в синтаксисі операторів case всередині switch. Давайте розглянемо приклад із JEP, щоб розібратися, як це працює.
int numberOfLetters;
switch(dayOfWeek) {
  case MONDAY:
  case FRIDAY:
  case SUNDAY:
    numberOfLetter = 6;
    break;
  case TUESDAY
    numberOfLetter = 7;
    break;
  case THURSDAY
  case SATURDAY
    numberOfLetter = 8;
    break;
  case WEDNESDAY
    numberOfLetter = 9;
    break;
  default:
   throw new IllegalStateException("Huh?: " + day);
}
У цьому прикладі ми використовуємо значення dayOfWeekдля визначення значення numberOfLetters. Через особливості роботи оператора switch, цей код — не найкрасивіший і легко наробити помилок. По-перше, якщо ми забудемо застосувати оператор breakдля кожної групи case-міток, ми за замовчуванням перейдемо до наступної групи міток. Це може призвести до помилок, які важко знайти. По-друге, ми повинні визначити кожну групу case-міток. Якщо забудемо, то, зрозуміло, отримаємо помилку компілятора, однак такий варіант не ідеальний. Також наш код дуже багатослівний, оскільки кожне значення dayOfWeekмає мати свою власну case-мітку. Використовуючи новий синтаксис, ми отримуємо набагато чистіший і менш схильний до помилок код:
int numberOfLetters = switch (dayOfWeek) {
   case MONDAY, FRIDAY, SUNDAY -> 6;
   case TUESDAY -> 7;
   case THURSDAY, SATURDAY -> 8;
   case WEDNESDAY -> 9;
   default -> throw new IllegalStateException("Huh?: " + day);
};
Тепер нам потрібно зробити привласнення тільки один раз (з повертається switchзначенням) і можна використовувати список, що розділяється комами, для case-міток. І оскільки ми не використовуємо оператор break, то усуваємо пов'язані з ним проблеми. Синтаксис виразу switchдозволяє використовувати синтаксис більш старого стилю, тому JDK 12 ми можемо написати і так:
int numberOfLetters = switch (dayOfWeek) {
  case MONDAY:
  case FRIDAY:
  case SUNDAY:
   break 6;
  case TUESDAY
   break 7;
  case THURSDAY
  case SATURDAY
   break 8;
  case WEDNESDAY
   break 9;
  default:
   throw new IllegalStateException("Huh?: " + day);
};
На думку Java-спільноти, перевантаження використання breakдля вказівки значення, що повертається, може збивати з пантелику. Мова Java також дозволяє використовувати breakcontinue) з позначкою на зразок оператора безумовного переходу goto. У JEP 354 було змінено подібне використання break, так що Java 13 наш код дещо змінюється:
int numberOfLetters = switch (dayOfWeek) {
  case MONDAY:
  case FRIDAY:
  case SUNDAY:
   yield 6;
  case TUESDAY
   yield 7;
  case THURSDAY
  case SATURDAY
   yield 8;
  case WEDNESDAY
   yield 9;
  default:
   throw new IllegalStateException("Huh?: " + day);
};
Наступні три JEP'а пов'язані з віртуальною машиною Java.

JEP 350 Dynamic CDS Archive

Це розширення дозволяє провести динамічне архівування класів наприкінці виконання Java-програми. CDS або Class Data Sharing дозволяє запакувати всі класи, що запускаються при старті, в спеціальний архів class data sharing, використовуючи список цих самих класів за замовчуванням. Це призводить до суттєвого прискорення запуску додатків та економії оперативної пам'яті. Раніше використання AppCDS було багатоетапним процесом, що включає створення списку відповідних класів та використання цього списку для створення архіву, який буде використовуватися для подальших запусків. Тепер все, що потрібно, - це один запуск програми з прапором -XX: ArchiveClassesAtExit, що вказує місце, куди буде записано архів. При такому підході класи автоматично упаковуються до архіву після нормальної зупинки програми.

JEP 351 ZGC : Uncommit unused memory

Рік тому в JDK 11 бид представлений ZGC - експериментальний збирач сміття, що масштабується, з низькою затримкою. Спочатку ZGC поводився досить дивно: він не дозволяв повернути пам'ять операційній системі, навіть якщо вона вже була не потрібна. Для деяких середовищ, наприклад, для контейнерів, де ресурси використовуються кількома службами одночасно, це може обмежувати масштабованість та ефективність системи. Купа ZGC складається з так званих ZPages. Коли ZPages очищаються під час циклу збору сміття, вони повертаються до кешу ZPageCache. ZPages у цьому кеші впорядковано за порядком давності використання. Java 13 ZGC повертатиме в операційну систему сторінки, які були визначені як ті, що не використовувалися досить довго. Таким чином, їх можна буде використовувати повторно для інших процесів.

JEP 353 Reimplement the legacy Socket API

Обидві реалізації API java.net.Socketвідносяться java.net.ServerSocketще JDK 1.0. У цій, та й у всіх наступних JDK, реалізація цих API-інтерфейсів використовує кілька методів (таких, як використання стека потоків як буфер введення-виведення), які роблять їх негнучкими і складними в обслуговуванні. Щоб вирішити цю проблему, JDK 13 надали нову реалізацію NioSocketImpl. Вона більше не вимагає нативного коду, тим самим спрощуючи портування на різні платформи. Цей клас також використовує існуючий механізм буферного кешу (за винятком використання стека потоків для цієї мети) і блокування java.util.concurrent, а не синхронізовані методи. Це спростить інтеграцію з файберами з Project Loom .

Нові API

Ми вже згадували раніше, що Java 13 включає 76 нових API в бібліотеках базових класів. Вони охоплюють такі області:
  • Оновлення підтримки Unicode.
  • Три нові методи Stringдля підтримки текстових блоків (див. опис JEP 255 вище).
  • Класи java.nioтепер мають абсолютні (на відміну від відносних) getта set-методи. Вони, як і базовий абстрактний клас Buffer, включають метод slice()вилучення частини буфера.
  • Метод force()класу MappedByteBufferпримусово записує розділ буфера у його резервне сховище.
  • nio.FileSystemдодає три нові перевантажені форми newFileSystem()для доступу до вмісту як файлової системи.
  • З'явився javax.annotation.processing.ProcessingEnvironmentновий цікавий метод. isPreviewEnabled(). Він повідомить про те, чи включені preview-фічі. Це цікаво тому, що згадана вище інструкція @PreviewFeatureне буде доступна до виходу JDK 14.
  • DocumentBuilderFactoryі SAXParserFactoryотримують javax.xml.parsersтри нових методу для створення екземплярів, що підтримують простір імен.
Матеріал заснований на статті Саймона Ріттера та офіційної документації .
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ