Статьи

Главное руководство по Java 9

Узнайте больше о Java с помощью нашего учебника Соединение Android и Java в разработке Android на SitePoint.

Java 9 идет! До запланированного выпуска осталось всего шесть месяцев, и, помимо модульной системы, в нем есть пара новых языковых функций и множество новых и улучшенных API. Нам нужно взглянуть на все блестящие новые вещи, с которыми мы можем поиграть, так что вот оно, полное руководство по Java 9. (Кстати, вы можете найти некоторые фрагменты в этой статье на GitHub .)

Модульная система платформы Java

Во-первых, давайте выбросим самого большого слона из комнаты: модульная система платформы Java (JPMS), несомненно, является флагманской функцией Java 9, и об этом уже много написано: в грандиозном состоянии модульной системы , на этом сайте, когда мы суммировали JVMLS , в моем блоге , в других блогах , черт возьми, даже в Википедии есть статья — бесполезная, но все же.

Со всеми этими источниками нам не нужно ничего повторять здесь, поэтому мы не будем. Вместо этого давайте перейдем к менее известным функциям. И их так много!

Языковые изменения

Когда у Sun было мало денег, Java 7 принесла лишь небольшие изменения в языке. В акте саркастического юмора проект, который содержал их, был назван Project Coin . Для Java 9 JEP 213: Milling Project Coin уточняет эти и другие детали.

Методы частного интерфейса

Java 8 принесла в таблицу методы по умолчанию , чтобы можно было развивать интерфейсы . Это прошло довольно хорошо, но была одна неприятная деталь: повторное использование кода между методами по умолчанию было неприятно.

Извлеченные методы должны были быть методами по умолчанию, которые должны быть общедоступными, или входить в некоторый вспомогательный класс, чтобы сохранять их закрытыми:

public interface InJava8 { default boolean evenSum(int... numbers) { return sum(numbers) % 2 == 0; } default boolean oddSum(int... numbers) { return sum(numbers) % 2 == 1; } // we don't want this to be public; // but how else do we resuse? default int sum(int[] numbers) { return IntStream.of(numbers).sum(); } } 

В Java 9 у нас может быть просто метод закрытого интерфейса:

 public interface InJava9 { // as above private int sum(int[] numbers) { return IntStream.of(numbers).sum(); } } 

Аккуратно, а?

Попробуйте с эффективными окончательными переменными

Вы когда-нибудь делали что-то подобное?

 void doSomethingWith(Connection connection) throws Exception { try(Connection c = connection) { c.doSomething(); } } 

Переменная c существует только потому, что этого требовал синтаксис оператора try-with-resources — управляемые ресурсы должны были быть объявлены в заголовке оператора. Java 9 смягчает это: теперь можно управлять любым ресурсом, если он эффективно работает .

Так что в Java 9 мы можем использовать connection напрямую:

 void doSomethingWith(Connection connection) throws Exception { try(connection) { connection.doSomething(); } } 

Оператор Diamond для анонимных классов

Скажем, у нас есть простой класс Box<T> и в какой-то момент мы хотим сделать его анонимным подклассом. В Java 8 мы должны были сделать это так:

 <T> Box<T> createBox(T content) { // we have to put the `T` here 🙁 return new Box<T>(content) { }; } 

Разве не очевидно, что это должна быть коробка T ? Я имею в виду, что компилятор может определить тип, если мы не создавали анонимный подкласс, так почему он не может сделать то же самое здесь?

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

Java 9 ослабляет это и разрешает алмаз, если выводится денотируемый тип:

 class inJava { <T> Box<T> createBox(T content) { // Java 9 can infer `T` because it is a denotable type return new Box<>(content) { }; } Box<?> createCrazyBox(Object content) { List<?> innerList = Arrays.asList(content); // we can't do the following because the inferred type is non-denotable: // return new Box<>(innerList) { }; // instead we have to denote the type we want: return new Box<List<?>>(innerList) { }; } } 

SafeVarargs на частные методы

Знаете ли вы о @SafeVarargs ? Вы можете использовать его, чтобы сообщить компилятору, что ваша специальная смесь varargs и generics безопасна. (Если вы спрашиваете себя, почему это небезопасно, ознакомьтесь с этими великолепными вопросами и ответами в StackOverflow .) Типичное использование выглядит следующим образом:

 @SafeVarargs public static <T> Optional<T> first(T... args) { if (args.length == 0) return Optional.empty(); else return Optional.of(args[0]); } 

@SafeVarargs можно применять только к методам, которые нельзя переопределить ( причина ). Это, очевидно, включает в себя статические, финальные и частные методы, а также конструкторы. Или это? По непонятной причине частные, не финальные методы не могли быть аннотированы раньше, и Java 9 исправляет это. На одно раздражение меньше.

Нет больше предупреждений об устаревании для импорта

Если вы поддерживаете старый проект, но хотите иметь сборку без предупреждений, вы можете столкнуться с досадным фактом, что импорт устаревших типов вызывает предупреждения.

Следующий класс делает все правильно: хотя он использует устаревший тип, он сам по себе не рекомендуется, так что, похоже, нет причин получать предупреждение.

 import java.io.LineNumberInputStream; @Deprecated public class DeprecatedImports { LineNumberInputStream stream; } 

Но в Java 8 мы делаем! WTF ?! Хорошо, @SuppressWarnings("deprecation") на помощь! Увы, импорт не может быть аннотирован, и аннотирование stream объявления также не помогает. Грустная панда.

Опять же, небольшая вдумчивая подстройка — это все, что нужно для появления Java 9: ​​импорт устаревшего типа больше не вызывает предупреждений, поэтому приведенный выше класс не содержит предупреждений.

API-интерфейсы

Я должен признать, что по сравнению с методами по умолчанию и лямбда-выражениями в Java 8 новые языковые возможности немного непримечательны. Но то же самое не относится к улучшениям API! В JDK было несколько вдумчивых и интересных дополнений, о которых вы должны знать.

Процессы ОС

API-интерфейс процесса был расширен в JEP 102 и теперь обладает возможностями для более насыщенного взаимодействия с процессами системного уровня. Новый ProcessBuilder значительно упрощает создание процессов и даже конвейеров.

Хотите вызвать ls , передать результаты в grep ? Нет проблем:

 ProcessBuilder ls = new ProcessBuilder() .command("ls") .directory(Paths.get("/home/nipa/tmp").toFile()); ProcessBuilder grepPdf = new ProcessBuilder() .command("grep", "pdf") .redirectOutput(Redirect.INHERIT); List<Process> lsThenGrep = ProcessBuilder .startPipeline(asList(ls, grepPdf)); 

Не менее новый ProcessHandle значительно упрощает взаимодействие с процессами и деревьями процессов.
Process реализует все это, но только по имени, потому что пара возвращаемых типов не совпадает. Чтобы получить правильный дескриптор, вызовите Process::toHandle .

Хотите дождаться завершения каждого из двух процессов, прежде чем записать свой PID на консоль? Ну вот:

 CompletableFuture[] lsThenGrepFutures = lsThenGrep.stream() // onExit returns a CompletableFuture<Process> .map(Process::onExit) .map(processFuture -> processFuture.thenAccept( process -> System.out.println("PID: " + process.getPid()))) .toArray(CompletableFuture[]::new); // wait until all processes are finished CompletableFuture .allOf(lsThenGrepFutures) .join(); 

Для получения дополнительной информации ознакомьтесь с постом Штеффена Джейкобса о расширенном API процесса .

Изображения с несколькими разрешениями

JEP 251 представляет MultiResolutionImage , который инкапсулирует набор изображений с различными разрешениями и позволяет запрашивать его с желаемой высотой и шириной. Аннотация и простая реализация предоставляются.

 public static void main(String[] args) throws IOException { MultiResolutionImage tokio = loadTokio(); int desiredImageWidth = new Random().nextInt(1500); Image variant = tokio.getResolutionVariant(desiredImageWidth, 1); System.out.printf("Width of image for %d: %d%n", desiredImageWidth, variant.getWidth(null)); } private static MultiResolutionImage loadTokio() throws IOException { List<Image> tokios = new ArrayList<>(); for (String url : IMAGE_URLS) { tokios.add(ImageIO.read(new URL(url))); } return new BaseMultiResolutionImage(tokios.toArray(new Image[0])); } 

Ходьба стека

Чтобы включить обход стека без создания полного (и, следовательно, дорогого) снимка, JEP 259 представляет StackWalker . Поддерживаемые улучшенными возможностями JVM, он создает поток кадров стека, который будет лениво оцениваться по мере необходимости. Это позволяет нам использовать возможности Stream API для фильтрации и отображения, в то же время делая короткие прогулки лучше.

Здесь мы делаем пару вызовов, а затем обходим стек, пока не найдем фрейм, принадлежащий определенному методу:

 public static void main(String[] args) { one(); } static void one() { two(); } static void two() { three(); } static void three() { String line = StackWalker.getInstance().walk(StackWalking::walk); System.out.println(line); } private static String walk(Stream<StackFrame> stackFrameStream) { return stackFrameStream .filter(frame -> frame.getMethodName().contains("one")) .findFirst() .map(frame -> "Line " + frame.getLineNumber()) .orElse("Unknown line"); } 

StackWalker::walk переводит функцию из Stream<StackFrame> в желаемый результат. Это немного необычно, почему он не просто возвращает поток? Ссылаясь на JEP:

Возврат потока, содержащего указатель стека, для дальнейших манипуляций неконтролируемым образом не будет работать, поскольку, как только фабрика потоков вернется, JVM сможет свободно реорганизовать стек управления (например, путем деоптимизации).

Вместо этого StackWalker::walk создает поток, передает его функции, которую мы передали, и закрывает ее, когда наша функция возвращается. Поэтому мы не можем хранить поток для последующей обработки.

Перенаправленное ведение журнала платформы

Вы когда-нибудь были разочарованы тем, что классы JDK используют свою собственную инфраструктуру журналирования? Разве не было бы здорово передать эти сообщения в бэкэнд регистрации, используемый приложением? Если это так, то вам повезло, потому что это именно то, что реализовал JEP 264 .

Классы JDK теперь передают сообщения через новый интерфейс Logger (напрямую или в обход через sun.util.logging.PlatformLogger ), экземпляры которого предоставляются одним LoggerFinder . Поиск в свою очередь — и это интересный момент — находится с помощью Service Loader API . Это позволяет регистрировать бэкэнды, такие как Log4J или Logback, чтобы обеспечить поиск, который создает регистраторы, которые пересылают сообщения в бэкэнд.

Настроить все просто — проверьте демо .

Реактивные потоки

Реактивные принципы находятся на подъеме, и многие различные библиотеки и структуры реализуют их. Для улучшения их взаимодействия JEP 266 содержит минимальный набор интерфейсов, которые захватывают сердце асинхронной публикации и подписки. Надежда состоит в том, что в будущем третьи стороны будут их реализовывать и, таким образом, соберутся на основе общего набора типов.

И вот они:

Publisher
Производит предметы для подписчиков для потребления. Единственный метод — subscribe(Subscriber) , цель которого должна быть очевидна.
Subscriber
Подписывается на издателей (обычно только один) для получения элементов (через метод onNext(T) ), сообщений об ошибках ( onError(Throwable) ) или сигнала о том, что больше элементов не следует ожидать ( onComplete() ). Однако, прежде чем что-либо из этого случится, издатель вызывает onSubscription(Subscription) .
Subscription
Связь между одним издателем и одним подписчиком. Подписчик будет использовать его для запроса большего количества элементов ( request(long) ) или для разрыва соединения ( cancel() ).

Поток выглядит следующим образом:

  1. Создать Publisher и Subscriber .
  2. Подписаться подписчика с помощью Publisher::subscribe .
  3. Издатель создает Subscription и вызывает с ее помощью Subscriber::onSubscription чтобы подписчик мог сохранить подписку.
  4. В какой-то момент абонент вызывает Subscription::request чтобы запросить несколько элементов.
  5. Издатель начинает передавать элементы подписчику, вызывая Subscriber::onNext . Он никогда не опубликует больше, чем запрошенное количество элементов.
  6. Издатель может в какой-то момент истощиться или столкнуться с проблемами и вызвать Subscriber::onComplete или Subscriber::onError соответственно.
  7. Подписчик может либо продолжать запрашивать больше элементов время от времени, либо прерывать соединение, вызывая Subscription::cancel .

Все это довольно просто, может быть, за исключением Subscription::request . Зачем подписчику это делать? Это реализация обратного давления , механизм, с помощью которого потребители могут сообщать производителям, сколько предметов они могут обработать. Без такого механизма производители могут легко перегружать потребителей, заставляя их отбрасывать товары или иметь неограниченные очереди, в которых хранятся необработанные товары. Ни одно из решений не является стабильным, поэтому при производстве изделий с дроссельной заслонкой было создано обратное давление, если потребители не могут идти в ногу.

Обратите внимание, что JDK предоставляет только интерфейсы и никаких реализаций (за исключением SubmissionPublisher )! Также нет движения к созданию издателей для асинхронных задач, таких как обход файловой системы. Все описанные интерфейсы являются внутренними типами класса Flow , который имеет хорошую вводную документацию . Существует также официальный проект GitHub , который содержит код и подробную спецификацию API . Я создал простой пример , который также может прояснить, что происходит.

Методы коллекционной фабрики

Когда-то была надежда на коллекционные литералы. Разве это не было бы здорово?

 List<String> list = [ "a", "b", "c" ]; Map<String, Integer> map = [ "one" = 1, "two" = 2, "three" = 3 ]; 

Ну, как бы здорово это ни казалось, это нетривиально, и есть веские причины не делать этого. Какая следующая лучшая вещь? Заводские методы! Благодаря JEP 269 мы можем сделать это сейчас:

 List<String> list = List.of("a", "b", "c"); Map<String, Integer> mapImmediate = Map.of( "one", 1, "two", 2, "three", 3); Map<String, Integer> mapEntries = Map.ofEntries( entry("one", 1), entry("two", 2), entry("three", 3)); 

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

Прежде всего, они все неизменны. Правильно, List.of("a").add("b") завершается ошибкой (с UnsupportedOperationException ). Мне нравится: эти фабричные методы часто используются для инициализации полей с набором фиксированных элементов, где изменчивость в большинстве случаев нежелательна. (Если вы слышали, что Java 9 будет содержать неизменяемые коллекции, имейте в виду, что это так. Неизменность не превратилась в систему типов.)

Методы фабрики полностью отклоняют null значения. Независимо от того, как элементы, как ключи, как значения, они запрещены. Хорошо, долой ноль!

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

Чтобы предотвратить это, порядок элемента рандомизируется при каждом запуске программы. При каждом запуске выбирается случайная соль, а затем используется во время этого прогона для размещения элементов в массиве во время построения. Это делает этот процесс детерминированным во время выполнения (так что два вызова Set.of("a", "b", "c") всегда будут иметь одинаковый порядок), но случайным образом при выполнении.

Встроенная интеграция с рабочим столом

Настольные приложения Java всегда торчат как больной палец. Это не изменится в ближайшее время, но JEP 272 делает это немного менее очевидным. Его цель состоит в том, чтобы «[d] определить новый общедоступный API-интерфейс для доступа к функциям рабочего стола для конкретной платформы, таким как взаимодействие с панелью задач или док-станцией, или прослушивание событий системы или приложений». В Linux поддерживается только Unity, поэтому я не смог попробуйте, но код выглядит многообещающе.

Давайте выберем панель задач в качестве примера. Вот некоторые из доступных функций:

  • Приложение может запросить внимание, что приведет к отскакиванию или миганию значков панели задач.
  • Специфичные для приложения меню могут быть добавлены в контекстное меню значка панели задач.
  • Значок на панели задач можно изменить программно.
  • Текстовые или числовые значки можно динамически добавлять к значку панели задач.
  • Записи панели задач могут отображать индикаторы выполнения и состояния, такие как выключено , приостановлено или содержит ошибку .
  • Окна предварительного просмотра могут иметь значки значков.

(Обратите внимание, что не все платформы поддерживают все функции. Удобный метод Taskbar::isSupported позволяет проверить, какие из них доступны.)

Для получения дополнительной информации взгляните на новый пакет java.awt.desktop .

Фильтр десериализации

Десериализация уже давно считается проблемой безопасности . Цель JEP 290 — улучшить ситуацию, позволив нам проверить поток байтов, прежде чем соглашаться на его десериализацию. Следующие детали принимаются во внимание:

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

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

 interface ObjectInputFilter { Status checkInput(Class<?> clazz, long size, long nRefs, long depth, long streamBytes); enum Status { UNDECIDED, ALLOWED, REJECTED; } } 

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

сетей

Есть несколько сетевых технологий, которые теперь поддерживаются JDK. Это не мой сильный костюм, поэтому я оставлю вам список причудливых имен и ссылок:

  • HTTP / 2 ( JEP 110 , введение Steffen Jacobs )
  • Безопасность транспортного уровня датаграмм (DTLS, JEP 219 )
  • Расширение согласования протокола прикладного уровня TLS (TLS ALPN, JEP 244 )
  • Сшивание OCSP для TLS ( JEP 244 )

XML

JEP 268 реализует стандарт OASIS XML Catalogs v1.1 . Он определяет «абстракции каталога и резолвера каталогов, которые можно использовать с процессорами JAXP, которые принимают резолверы». Обратите внимание, что «[e] существующие библиотеки или приложения, использующие внутренний API, должны будут перейти на новый API, чтобы воспользоваться преимуществами новых функций». Вы можете найти его в javax.xml.catalog .

JAXP также получил поддержку: JEP 255 объединил выбранные изменения между Xerces 2.10.0 и 2.11.0 в JDK.

Расширения к существующим API

Некоторые из существующих API также получили небольшую любовь. Просто назвать несколько:

  • Optional , Stream и Collectors получили несколько новых методов. Вот специальные посты, освещающие их: для Optional , для Stream и для Collectors .
  • Был добавлен ряд методов возврата потока, например, LocalDate::datesUntil или Scanner::tokens /
    Scanner::findAll .
  • Многие типы API DateTime, представленные в Java 8, теперь могут преобразовываться в секунды, например Chronology , LocalDate и OffsetTime .
  • Duration получила множество методов, которые интерпретируют ее как days:hours:minutes:seconds:ms:ns и возвращают отдельные части этой интерпретации.
  • Matcher получил новые методы для замены совпадений, и Matcher::results теперь возвращает Stream<MatchResult> .
  • В IO были добавлены мелкие детали: InputStream теперь может напрямую передаваться в OutputStream а Buffer может OutputStream и дублировать себя.
  • Классы Atomic... получили множество новых методов, которые обеспечивают доступ к памяти с семантикой, схожей с тем, что теперь возможно с VarHandles . Доступ ( get , set , compareAndSet и compareAndExchange ) может быть простым, непрозрачным, приобретенным или слабым, хотя и не во всех комбинациях.

Некоторые служебные классы также улучшены:

  • В Math и StrictMath были добавлены арифметические методы, например, умножение long на int с исключением, если происходит переполнение.
  • Arrays теперь могут сравнивать массивы и срезы массивов (т. Е. A a[3]-a[5] и b[2]-b[4] ) всех типов на равенство и, если заданы типы, реализующие Comparable или Comparator , для лексикографического заказ . Очень круто, если что-то не так, mismatch может вернуть индекс, куда отклоняются массивы (или кусочки).
  • Objects больше не имеют только requireNonNull — в requireNonNull Optional он получает requireNonNullElse и requireNonNullElseGet , которые не requireNonNullElseGet исключения, а вместо этого возвращают значение по умолчанию, если проверенное значение равно нулю. Он также использует несколько методов, которые проверяют, находятся ли индексы в желаемом диапазоне.

API низкого уровня

Java 9 содержит ряд очень интересных API, которые работают намного ближе к металлу (читай: JVM), чем то, что мы обсуждали до сих пор. Давайте также кратко рассмотрим их — мы могли бы узнать что-то совершенно новое (я, конечно, сделал).

Переменные ручки Aka VarHandles

Я здесь не в себе, поэтому позвольте мне процитировать цитату из JEP 193 :

«Поскольку параллельное и параллельное программирование в Java продолжает расширяться, программисты все больше разочаровываются тем, что не могут использовать конструкции Java для организации атомарных или упорядоченных операций над полями отдельных классов; например, атомное увеличение поля счетчика ». Существующие возможности сделать это все в том или ином смысле отсутствуют:

  • Atomic... классы добавляют «и пространство, и дополнительные проблемы параллелизма для управления косвенным обращением»
  • FieldUpdaters часто приводят к «большим накладным расходам, чем сама операция»
  • Unsafe , ну, в общем, небезопасно — оно не стандартизировано, не поддерживается и не станет недоступным в Java 10.

Java 9 представляет дескрипторы переменных, о которых пишет JEP: «Дескриптор переменной — это типизированная ссылка на переменную, которая поддерживает доступ на чтение и запись к переменной в различных режимах доступа. Поддерживаемые типы переменных включают в себя поля экземпляра, статические поля и элементы массива ».

Это снимет часть давления на Unsafe .

Улучшенные дескрипторы метода

JEP 274 улучшает API дескриптора метода, предоставляя новые комбинаторы для циклов, блоков try / finally и обработки аргументов, а также поиска для методов интерфейса.

Java 7 привнесла в язык invokedynamic : инструкция JVM, которая определяет, как именно выполнить вызов динамически, вызывая обратно язык. (В отличие от других команд invoke... , чье поведение статически определяется во время компиляции.) Он задумывался как инструмент для более динамичных языков JVM, но сама Java начала сильно на него опираться. Первым основным использованием indy были лямбда-выражения в Java 8.

Во время работы над Nashorn , который является invokedynamic пользователем invokedynamic , стало разумным разрабатывать высокоуровневый API поверх него. Он выражает такие операции, как «чтение свойства объекта», «запись свойства объекта», «вызов вызываемого объекта» и т. Д. И имеет имя Dynalink . Теперь JEP 276 делает его публичным API в Java 9 — вы можете найти его в пакете jdk.dynalink .

Nashorn Parser API

Говоря о Nashorn… Будучи ответственным за все JavaScript, у него есть парсер (очевидно), который находится во внутреннем пакете jdk.nashorn.internal.ir — он не предназначен для публичного использования. Но инструменты, тем не менее, начали зависеть от него (как правило, от IDE — как обычно), что препятствует свободной эволюции API и становится проблемой с сильной инкапсуляцией Jigsaw.

Чтобы исправить все это, JEP 236 создает поддерживаемый API синтаксического анализатора для Nashorn. Вы можете найти его в пакете jdk.nashorn.api.tree .

Советы Spin-Wait

Вы когда-нибудь писали такой код?

 class EventHandler { volatile boolean eventNotificationReceived; void waitForEventAndHandleIt() { while (!eventNotificationReceived) { /* spinning */ } readAndProcessEvent(); } void readAndProcessEvent() { /* ... */ } } 

И я нет. Но некоторые разработчики пишут вещи низкого уровня, и им иногда нужны такие циклы вращения. И, очевидно, некоторые аппаратные платформы могут оптимизировать задержку и энергопотребление для спин-циклов, если они знают, что код в настоящее время выполняет один.

И вот тут-то и вступает JEP 285. Он объявляет Thread::onSpinWait , метод, тело которого пусто, но в котором HotSpot имеет встроенную реализацию, которая вставляет инструкцию pause на компьютеры x86 во время компиляции C2. Вы бы использовали его следующим образом:

 void waitForEventAndHandleIt() { while (!eventNotificationReceived) { Thread.onSpinWait() } readAndProcessEvent(); } 

Теперь машины x86 увидят спин-петлю и смогут ее оптимизировать.

API, которые идут пока

Новые вещи приходят … и иногда старые вещи должны уходить. Устаревшие средства уже давно являются частью Java — как и тот факт, что никто не знает, как их получить. Является ли использование устаревшего API просто обескураженным или есть серьезные недостатки? Заменены ли они другими API? Будут ли они удалены?

Чтобы прояснить ситуацию, JEP 277 решает проблему, предоставляя аннотации устаревания два флага: forRemoval и since . Некоторые существующие амортизации были обновлены, чтобы включить их. И есть пара новых амортизаций — самые важные:

  • API апплета (Java в браузере … помните об этом?) Устарел, но еще не планируется удалить. ( JEP 289 )
  • Корба устарела. Люди разогревают костры и ожидают, что они скоро сгорят.
  • Явные конструкторы для примитивных оболочек (таких как new Integer(5) и new Integer ("5") ) устарели в пользу фабричных методов ( valueOf или parse... ), поскольку они используют интернирование для повышения производительности.
  • Observer и Observable устарели. Наконец — API имеет так много дыр, что удивительно, что он еще не утонул.
  • Сертификаты SHA-1 постепенно прекращаются. ( JEP 288 )

Если вы хотите узнать больше о том, что еще является устаревшим и как обрабатывается устаревание для скомпилированного кода, вам определенно стоит взглянуть на JEP 277 .

И пара вещей выгнали из Java навсегда:

  • Одно подчеркивание ( _ ) больше не является допустимым идентификатором. ( JEP 213 )
  • Определенные комбинации флагов сборки мусора устарели в Java 8 и сейчас удаляются. ( JEP 214 )
  • Инструмент hprof удаляется, потому что другие инструменты (например, JVisualVM ) предоставляют такие же или превосходные функции. ( JEP 240 )
  • Инструмент jhat удаляется, потому что доступны лучшие визуализаторы и анализаторы кучи. ( JEP 241 )

Но подождите, есть больше!

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

Внутри Java 9 — Часть I :
  • схема новой версии и параметры командной строки в стиле GNU
  • поддержка таких технологий, как GTK3, SHA-3, Unicode 8.0 и TIFF
  • дополнения к JVM (например, несколько выпусков JAR и унифицированная регистрация)
Внутри Java 9 — Часть II :
  • значительное улучшение производительности, особенно благодаря компактным струнам
  • дополнения компилятора (например, преждевременная компиляция) и рефакторинг (новый конвейер аннотаций)
  • Улучшения JavaDoc

Узнайте больше о Java с нашим учебным пособием Соединение Android и Java в разработке Android на SitePoint.