Что такое runtime data areas

Обновлено: 06.07.2024

Нигде не нашел чёткого опрделения этми двумя понятиям.

Я понимаю фреймворк, как платформу, которая необходима для работы каких-либо приложений. Например, набор динамически линкуемых библиотек для нескольких приложений - уже фреймворк. Также под это определение подохдит и Java Runtime Environment (в том числе и JVM). Однако что такое рантайм? С одной стороны это всего лишь фаза выполнения программы. С другой стороны есть куча терминов, как runtime libraries, runtime system. Что вкладывает майкрософт в это понятие тоже неясно. Объясните, пожалуйста!

183 1 1 золотой знак 1 1 серебряный знак 4 4 бронзовых знака

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

runtime - это часть кода, существует в выполнимом файле (либо в отдельных so/dll) и обеспечивает всякие "удобства". Например, узнать тип объекта или сделать те же виртуальные вызовы. Добавляется обычно компилятором и обычный пользователь может даже не знать о нем. Также словом runtime называют то время, когда программа выполняется. Что конкретно имеется ввиду - нужно сдедить за контекстом.

runtime libraries - это библиотеки, которые используются во время работы программы. Иногда библиотеки поставляются в двух видах - для разработки и для обычной работы (вторые часто оптимизированы и с них выброшено лишнее). Хороший пример - bpl файлы делфи. Для одного и того же компонента могут быть библиотеки, которые содержат всякие инструметы для IDE, а есть которые только для работоспособности кода.

JRE - это не фреймворк, это runtime библиотека. Хотя с другой стороны это фреймворк для байткода. Но так как на байткоде пищут только особые извращенцы, то обычному программисту это не фреймфорк. А вот вся java - это один сплошной фреймворк:)

Java Core

9. В чем разница между статическим и динамическим связыванием в Java?

На данный вопрос я уже ответил в этой статье в 18 вопросе про статический и динамический полиморфизм, советую ознакомиться.

10. Можно ли использовать private или protected переменные в interface?

  • public — интерфейс предоставляет возможность клиенту взаимодействовать с объектом. Если бы переменные не были общедоступными, у клиентов не было бы к ним доступа.
  • static — интерфейсы не могут быть созданы (а точнее, их объекты), поэтому переменная статична.
  • final — так как интерфейс используется для достижения 100% абстракции, переменная имеет свой конечный вид (и не будет изменена).

11. Что такое Classloader и для чего используется?

Bootstrap ClassLoader — базовый загрузчик, реализован на уровне JVM и не имеет обратной связи со средой выполнения, так как является частью ядра JVM и написан в машинном коде. Данный загрузчик служит родительским элементом для всех других экземпляров ClassLoader.

В основном отвечает за загрузку внутренних классов JDK, обычно rt.jar и других основных библиотек, расположенных в каталоге $ JAVA_HOME / jre / lib. У разных платформ могут быть разные реализации этого загрузчика классов.

Extension Classloader — загрузчик расширений, потомок класса базового загрузчика. Заботится о загрузке расширения стандартных базовых классов Java. Загружается из каталога расширений JDK, обычно — $ JAVA_HOME / lib / ext или любого другого каталога, упомянутого в системном свойстве java.ext.dirs (с помощью данной опции можно управлять загрузкой расширений).

System ClassLoader — системный загрузчик, реализованный на уровне JRE, который заботится о загрузке всех классов уровня приложения в JVM. Он загружает файлы, найденные в переменном окружении классов -classpath или -cp опции командной строки.

System Classloader пытается найти класс в своем кеше.

1.1. Если класс найден, загрузка успешно завершена.

1.2. Если класс не найден, загрузка делегируется к Extension Classloader-у.

Extension Classloader пытается найти класс в собственном кеше.

2.1. Если класс найден — успешно завершена.

2.2. Если класс не найден, загрузка делегируется Bootstrap Classloader-у.

Bootstrap Classloader пытается найти класс в собственном кеше.

3.1. Если класс найден, загрузка успешно завершена.

3.2. Если класс не найден, базовый Bootstrap Classloader попытается его загрузить.

4.1. Прошла успешно — загрузка класса завершена.

4.2. Не прошла успешно — управление передается к Extension Classloader.

5. Extension Classloader пытается загрузить класс, и если загрузка:

5.1. Прошла успешно — загрузка класса завершена.

5.2. Не прошла успешно — управление передается к System Classloader.

6. System Classloader пытается загрузить класс, и если загрузка:

6.1. Прошла успешно — загрузка класса завершена.

6.2. Не прошла успешно — генерируется исключение — ClassNotFoundException.

12. Что такое Run-Time Data Areas?

PC Register — регистр ПК — локален для каждого потока и содержит адрес инструкции JVM, которую поток выполняет в данный момент.

JVM Stack — область памяти, которая используется как хранилище для локальных переменных и временных результатов. У каждого потока есть свой отдельный стек: как только поток завершается, этот стек также уничтожается. Стоит отметить, что преимуществом stack над heap является производительность, в то время как heap безусловно имеет преимущество в масштабе хранилища.

Native Method Stack — область данных для каждого потока, в которой хранятся элементы данных, аналогичные стеку JVM, для выполнения собственных (не Java) методов.

Heap — используется всеми потоками как хранилище которое содержит объекты, метаданные классов, массивы и т. д., которые создаются во время выполнения. Данная область создается при запуске JVM и уничтожается при завершении ее работы.

Method area — область метода — эта область времени выполнения общая для всех потоков и создается при запуске JVM. Он хранит структуры для каждого класса, такие как пул констант (Runtime Constant Pool — пул для хранения констант), код для конструкторов и методов, данные метода и т. д.

13. Что такое immutable object?

В данной части статьи в 14 и 15 вопросе уже есть ответ на этот вопрос, поэтому ознакамливаетесь не теряя времени зря.

14. В чем особенность класса String?

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

Объект данного класса можно создать без использования ключевого слова new — непосредственно через кавычки String str = “строка”; .

String — это immutable класс: при создании объекта данного класса его данные нельзя изменить (когда вы к некоторой строке добавляете + “другую строку”, как результат вы получите новую, третью строку). Неизменность класса String делает его потокобезопасным.

Класс String финализирован (имеет модификатор final ), поэтому его наследование невозможно.

У String есть свой пул строк, область памяти в heap, которая кеширует создаваемые строковые значения. В этой части серии, в 62 вопросе, я описывал строковой пул.

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

15. Что такое ковариантность типов?

- permanent generation/PermGen

Немного используемой терминологии:

- с точки зрения JVM: деление на требования спецификации (jvmspec) и особенности конкретной реализации (HotSpot, ? HotSpot TLAB, G1, CMS, . )
- с точки зрения JVM: деление на формулировки спецификации (jvmspec) и особенности конкретного поведения в конкретной реализации (object allocation in stack, lock coarcesing, . )


Terms: stack,

2.5 Runtime Data Areas
2.5.2 Java Virtual Machine Stacks

TODO: добавь возврат, а не только передачу


Поколенческая гипотеза - ?
разница между спекой и реализацией - ?
- хотспот
- ibm
- bea
- oracle - oc4j
- azul
- dalvic
- j2me
- .

слияние HotSpot и JRockit - ?
стек непрерывен - ?
стек фиксированного размера при создании - не растет - ?
стек берется из общего места с хипом - ?
хип - растет, но убывает ли - ?
компакт коллектор - ?
копи коллектор - ?
парал коллектор - ?
коекур коллектор - ?
поколенческий коллектор - ?
в памяти может быть несколько коллекторов - ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?
- ?

Примитивы состоят из одной части.
Ссылочные типы состоят из двух частей.
Object obj = null; = только ссылающееся
new Object(); = только ссылаемое
Примитивы передаются по значению. Ссылку на примитив и ссылающееся невозможно разделать.



Факты
Современные GC работают на основе поиска достижимых объектов, а не подсчета ссылок. Для них не составляют проблем циклические ссылки и время их работы пропорционально количеству живых объектов, а не выделенных.
В Sun HotSpot присутствуют несколько GC, можно выбрать один из них.
В Sun HotSpot Java 7 добавился G1 коллектор.
"Слабая поколенческая гипотеза" - большинство объектов умрут молодыми.
System.gc() - носит рекомендательный характер и каждая JVM сама решает как реагировать. Конкретно у Sun HotSpot - вызывается stop-the-world gc. Избегайте этого.
Параллельный коллектор - это коллектор, состоящий из нескольких потоков. . - параллельный.
Конкурентный коллектор - это коллектор, работающий одновременно с рабочими потоками. . - конкурентный.
Момент запуска finalize() - не определен и запуск может вообще не состояться.
Object.hashCode() - это не адрес объекта.
В Java начиная с 1.4.2 (добавилось NIO = java.nio.*) можно выделять объекты за пределами heap. Ссылки хранятся в heap, но указывают за его пределы. ByteBuffer.allocateDirect(. ). Соответственно возможна "настоящая" "утечка памяти".

Материалы
Heap, stack
- [Шилдт. Руководство] - стр 149-151
Передача по ссылке/по значению
- [Шилдт. Руководство] - стр 172-174
GC, finalize
- [Шилдт. Руководство] - стр 161-162

- задания по "уборке мусора" (garbage collector, gc)

Словарь
"Уборка мусора", garbage collection, "уборщик мусора", garbage collector, недостижимые объекты, утечка памяти (memory leak)

  • JRockit
  • IBM JVM
  • SUN JVM
  • Open JDK


--------------------------------------------------------------
примитивные типы данных всегда хранятся в стеке?
- да
+ нет
(локальная переменная метода и статического метода, конструктора, инициализатора и статического инициализатора, аргумент метода и стат метода, конструктора лежат в стеке. Но примитивное поле экземпляра класса (не статическое) лежит в хипе, примитивное поле класса (статическое поле) лежит в PermGen )

экземпляры ссылочных типов данных всегда хранятся в хипе?
+ да
- нет
(все, что выделяется по new явно или неявно (enum), хранится в хипе )


--------------------------------------------------------------

ссылки на экземпляры ссылочных типов данных могут быть из стека?
+ да
- нет
(да, из локальной переменной )

ссылки на экземпляры ссылочных типов данных могут быть из хипа?
+ да
- нет
(да, из поля объекта (не статического поля) )

ссылки на экземпляры ссылочных типов данных могут быть из PermGen?
+ да
- нет
(да, из поля класса ( статического поля ) )


--------------------------------------------------------------
void f(long N) <
for (long k = 0; k < N; k++) <
long tmp = k + 1;
>
>
Каждый раз входя в тело цикла мы имеем новую переменную tmp никак не связанную с предыдущей, значит на каждую переменную tmp выделяется новая память, значит при достаточно большом N:
- может не хватить памяти и будет выброшено OOM.
- память будет потребляться, но так как мы теряем ссылки на предыдущее значение, то нас выручит GC.
+ у этого метода фиксированное потребление памяти не зависящее от N
--------------------------------------------------------------

Эта статья написана в продолжение к первой части и посвящена новому Date Time API, который был введен в Java 8. Я изначально хотел оформить эту тему отдельно, поскольку она достаточно большая и серьезная. Я еще сам не в полной мере начал использовать этот API в проектах, поэтому разбираться будем вместе по ходу. В принципе в переходе на новый API нет никакой срочной необходимости, более того многие еще и не начинали проекты на Java 8, а это означает, что время на освоение еще есть.

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

  • классов стандартной библиотеки все равно не избежать, в 99% случаях их функциональность с задачей справляется, а умножать сущности сверх необходимости не хочется;
  • библиотека joda-time не использует стандартную базу временных зон из JVM, поэтому при очередном маневре законодателей приходится помнить, что обновлять tzdata нужно не только в JDK, но и в библиотеке joda-time.

Начать наверное стоит с того, что именно не устраивало многих в старом API. И тут же, чтобы не терять время сразу укажу, что в новом API изменилось к лучшему.

  • В старом API классы для работы со временем усажены в пакеты java.util и java.sql — среди большого множества других классов. Кроме того существуют еще классы java.util.concurrent.TimeUnit и java.text.DateFormat c наследниками.
  • В новом API для работы с временем выделен отдельный пакет java.time
  • Названия классов в старом API не отражают суть происходящего. В старом API есть два класса которые способны обозначить точку на временной оси: java.util.Date и java.util.Calendar. Класс java.util.Date обозначает время в миллисекундах по Unix-time, а вовсе не дату (он назван так скорее всего из тех же соображений, по которым время в командной строке выдает утилита /bin/date). Класс java.util.Calendar также вовсе не календарь, у него есть состояние в виде временной зоны, календарных и временных полей.
  • В новом API названия классов даны более осмысленно. Есть классы аналогичные уже упомянутым: java.time.Instant и java.time.ZonedDateTime. Существует также множество других классов для более специализированного использования.

    Класс java.util.Date не является immutable и отягощен большим количеством лишних методов, которые хоть уже и помечены как устаревшие, но вряд ли будут удалены в обозримом будущем. Изменяемость java.util.Date заставляет некоторых клонировать инстанты java.util.Date — для того чтобы враг не пробрался:

Класс java.util.Calendar также изменяем. Хотя это особых проблем это не доставляет поскольку большинство понимает что у него есть внутреннее состояние которое меняется, да и передавать его аргументами как-то не очень принято.

Поскольку классы в старом API изменяемые, использовать их в многопоточной среде нужно с осторожностью. В частности java.util.Date можно признать «эффективно» потоко-безопасным, если вы не вызываете у него устаревшие методы.

  • Точность представления в времени составляет одну миллисекунду. Для большинства практических задач этого более чем достаточно, но иногда хочется иметь точность повыше.
  • В новом API представления времени составляет одну наносекунду, что в миллион раз точнее.
  • Классы для меток времени и даты (java.sql.Date и java.sql.Time) не являются чистым представлением меток времени и даты, поскольку унаследованы от java.util.Date и так или иначе хранят полное значение Unix-time с игнорированием части этого значения.
  • В новом API соответствующие классы java.time.LocalDate и java.time.LocalTime хранят чистые кортежи (yyyy,MM,dd) и (HH,mm,dd) соответственно, и никакой лишней информации или логики в этих классах нет. Также введен класс java.time.LocalDateTime который хранит оба кортежа.
  • В старом API многие действия, где необходимо указание временной зоны, могут быть выполнены без ее указания. В этом случае берется временная зона по-умолчанию, а программист может даже и не догадаться о том, что он что-то упустил.
  • В новом API все действия, где необходимо указание временной зоны, требуют ее явно: либо в виде аргумента метода, либо временная зона отображена прямо в названии метода. Другими словами временная зона «по-умолчанию» нигде по умолчанию не используется.
  • Старый API очень сложно использовать в тестах, в которых нужно протестировать поведение логики с течением времени (об этом подробно расписано в предыдущей статье).
  • В новом API введен специальный абстрактный класс java.time.Clock, единый экземпляр которого можно инжектить в контекст или просто передавать в свою логику. Переопределив этот класс для тестов, можно контролировать течение времени для своего кода в ходе его выполнения.
  • В старом API номера месяцев идут с 0, что очень неинтуитивно.
  • В новом API номера месяцев идут с 1. Появилось новое перечисление java.time.Month.
  • В java.util.Calendar устанавливала год-месяц-день-час-минуту-секунду, но для сброса миллисекунд нужно было сделать отдельный вызов.
  • В java.time.ZonedDateTime устанавливаются все поля сразу, включая наносекунды.
  • В старом API нет классов для определения длительности и промежутков времени. Обычно используется простой long и хранение длительности в виде миллисекунд.
  • В новом API определены специальные классы для длительности и периодов.
  • если раньше было два активно используемых класса: java.util.Date и java.util.Calendar, то сейчас классов стало сильно больше, плюс к ним добавилась иерархия интерфейсов и абстрактных классов.
  • отчасти из-за большого количества новых классов появились некоторые нюансы работы, которые я успел найти уже за несколько часов исследований и о которых мы поговорим позже.
  • новый API не контролирует правильность операций в compile-time. Многие проблемы с отсутствием временной зоны будут проявляться только в runtime — как это далее будет видно в примерах. Я бы предпочел возможно менее гибкий, но более строгий контракт.
  • в ходе работы генерируется большее количество объектов. Например получение текущей временной точки через Instance.now() кроме самого экземпляра java.time.Instance создает еще и java.time.Clock на каждый запрос, хотя это вообще ему ни к чему — в текущей реализации достаточно было бы вызова System.currentTimeMillis(). Также промежуточные объекты создаются и при многих других действиях. Но я не думаю, что для типичного бэкенда это будет представлять какую-нибудь проблему — ни по потреблению памяти, ни по времени исполнения. Хардкорщики все равно хранят время в long или даже пакуют в int.
  • проблему (проблема ли это?) с учетом leap second никак не решили. Фактически новая библиотека все также не отходит ни на шаг от Unix-time и полагается на внешний перевод секундной стрелки назад. Прошлое при этом все также каждый раз теряет секунду и сдвигается вперед. Библиотеку для точных научных расчетов мы так и не получили.

Об этих и других, настороживших меня кейсах расскажу подробнее уже в примерах.

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

Не очень понятно почему case-4, который фактически запрашивает тоже что и case-3, в результате создает java.time.ZoneRegion, а не java.time.ZoneOffset.

"Время — это часы" — как утверждают некоторые физики. И это фраза является ключевой для нового API, где класс java.time.Clock является краеугольным. И также как некоторые из наших часов, время для нас может быть: константным (неидущим), опаздывающим, идущим с различной степенью точности, двигающем стрелки по разному в разных часовых поясах. В общем в новом API можно использовать (либо определить самому) практически любой ход времени, в том числе и для проверки тестов.

Стандартный экземпляр java.time.Clock можно создать только фабричными статическими методами (сам класс абстрактный).

Стандартный экземпляр java.time.Clock всегда знает о временной зоне в которой его создали (хотя это бывает и ненужным).

Можно переопределить java.time.Clock и написать любую свою логику выдачи времени, например часы которые выдают случайное время на каждый запрос, почему бы и нет?

java.time.Instant — это новый java.util.Date, только неизменяемый, с наносекундной точностью и корректным названием. Внутри хранит Unix-time в виде двух полей: long с количеством секунд, и int с количеством наносекунд внутри текущей секунды.

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


Также как и java.util.Date (при правильном его использовании), объект класса java.time.Instant ничего не знает про временную зону.

Отдельно стоит сказать про метод java.time.Instant.toString(). Если раньше java.util.Date.toString() работал с учетом текущей локали и временной зоны по умолчанию, то новый java.time.Instant.toString() всегда формирует текстовое представление во временной зоне UTC и одинаковым форматом ISO-8601 — это касается и вывода переменных в IDE при отладке:

Посмотрим на базовый интерфейс java.time.temporal.TemporalAccessor. Интерфейс TemporalAccessor — это справочник для запроса отдельной частичной информации по текущей точке или метке и его реализуют все временные классы нового API.

Попросим значение Unix-time у java.time.Instant:


Немного подебажив, становится ясна причина исключения: результат теоретически может не помещаться в диапазон int (хотя в данный момент помещается). Поле INSTANT_SECONDS надо запрашивать как long. Исправим запрос, попутно запросим дополнительную мета-информацию:

Поле CLOCK_HOUR_OF_DAY не поддерживается типом Instant. Это совершенно ожидаемо, поскольку для выяснения часа дня по временной точке нам нужно указать временную зону, которой в java.time.Instant нет. Попробуем все таки запросить это значение:


Все правильно — при запросе часа дня мы получаем исключение. Прекрасно, что метод запроса не стал использовать временную зону по умолчанию (которой в новом API и нет).

Кроме запроса отдельных полей можно запрашивать значения с помощью более сложных алгоритмов-стратегий наследующих интерфейс java.time.TemporalQuery:


java.time.temporal.Temporal — интерфейс является наследником интерфейса TemporalAccessor. Вводит операции сдвига временной точки/метки вперед и назад, операцию замены части временной информации, а также операцию вычисления расстояния до другой временной точки/метки. Реализуется почти всеми «полноценными» временными классами нового API.

Пробуем сдвинуть метку на день вперед и посчитаем разницу:


Поскольку все классы наконец-то стали неизменяемыми, то результаты операций надо не забыть присвоить другой переменной, поскольку оригинальная при операции не изменяется — все аналогично java.lang.String или java.math.BigDecimal.

Попробуем изменить час дня в java.time.Instant:


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

java.time.temporal.TemporalAdjuster — интерфейс стратегии коррекции временной точки/метки, например перемещение в первый день текущего кода. Раньше приходилось для этого писать свои вспомогательные классы для работы с полями java.util.Calendar — сейчас весь код можно оформить в виде стратегии, если нужной еще нет в стандартной поставке:


Теперь можно перейти к временным классам.

java.time.LocalTime — это кортеж (час, минуты, секунды, наносекунды)
java.time.LocalDate — это кортеж (год, месяц, день месяца)
java.time.LocalDateTime — оба кортежа вместе

К этим же классам я бы отнес еще и специфические классы для хранения части информации: java.time.MonthDay, java.time.Year, java.time.YearMonth

Все эти классы объединяет то, что они содержат временные метки или их части, но временные точки на временной оси сами по себе определить не в состоянии (даже LocalDateTime) — поскольку ни в одном из них нет ни временной зоны, ни даже смещения.


Нужно сказать, что несмотря на неизбежные параллели в использовании между java.time.LocalTime и java.sql.Time, а также между java.time.LocalDate и java.sql.Date — это совершенно различные классы. В старом API классы java.sql.Time и java.sql.Date являются наследниками java.util.Date, а это значит, что их интерпретация (получение значения часа например) зависит от временной зоны в которой объект этого класса был создан и от временной зоны в которой этот объект будет прочитан. В новом API классы java.time.LocalTime и java.time.LocalDate — это честные кортежи значений и при записи и чтении значения часа временная зона никак не участвует.

Однако временная зона необходима при создании их из временной точки, поскольку интерпретация дней-часов от нее зависит:


Исключение выбрасывается, по причине того, что временную зону взять просто неоткуда (в Instant ее нет, а зону по-умолчанию не берем). Но ее можно получить либо из часов java.time.Clock, либо передать дополнительно:


Теперь все работает, но легкость, с которой можно сделать ошибку, несколько настораживает.

В комментариях к предыдущей статье упомянули, что настоящие параноики должны еще указывать календарь при операциях с календарными значениями (что включает создание объектов всех временных классов кроме Instant). В новом API есть несколько календарей, которые названы хронологиями:


Вообще сложно представить кейс, где может потребоваться отличная от ISO-8601 хронология IsoChronology (которая практически эквивалентна грегорианскому календарю), но, если что, новый API это поддерживает.

java.time.ZonedDateTime — аналог java.util.Calendar. Это самый мощный класс с полной информацией о временном контексте, включает временную зону, поэтому все операции со сдвигами этот класс проводит правильно.

Попробуем создать ZonedDateTime из LocalDateTime:


Сразу же получаем по рукам за то, что в операции (в LocalDateTime) нет временной зоны, а использовать временную зону по-умолчанию новое API опять отказывается (это очень хорошо).


Посмотрим, насколько ZonedDateTime строг по отношению к некорректно указанным датам. В java.util.Calendar есть переключатель lenient, который можно настроить как на «строгий», так и на «мягкий» режим. В новом API такого переключателя нет.

29-е февраля не в високосном году не пройдет:


60-ю секунду указать нельзя:


Но указание метки в момент перевода стрелок на летнее время успешно проходит, а результат отличается от ожидаемого. В строгом режиме java.util.Calendar такое не пропускал (см. предыдущую статью).


Про операции в ZonedDateTime я ничего писать не буду — можно посмотреть документацию.

java.time.OffsetTime — это LocalTime + ZoneOffset
java.time.OffsetDateTime — это LocalDateTime + ZoneOffset

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

Эти классы можно использовать, если по ситуации известно только текущее смещение пользователя (например через JavaScript). Полностью корректные операции сдвигов они не позволяют сделать, поэтому лучше использовать ZonedDateTime — если есть способ выяснить полноценную временную зону пользователя. С другой стороны, между двумя экземплярами OffsetDateTime всегда можно успешно и правильно посчитать разницу в секундах.

Из всех классов нового API временную точку на временной оси однозначно определяют только три: java.time.Instant, java.time.ZonedDateTime и java.time.OffsetTime.

Операции сдвига и модификации времени в общем случае выполняются корректно только в java.time.ZonedDateTime, поскольку только он один знает про временные зоны.

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

Я ни в коем случае не хочу сказать, что эти примеры показывают ошибки в новом API (если кто-то так подумал). Все эффекты ожидаемы и объяснимы. Напрягает другое — насколько такое поведение будет осознано армией Java-разработчиков. В старом API такие потенциальные проблемы были невозможны, поскольку всеми расчетами занимался только один класс java.util.Calendar, а единственное, что в нем можно было сделать неправильно — забыть явно указать временную зону.

Возможно стоило запретить большинство операций со временем во всех классах кроме ZonedDateTime, поскольку только он один в курсе переводов стрелок. Возможно стоило запретить расчет Duration с использованием LocalDateTime, поскольку без временной зоны он не определяет временную точку. Я не готов сейчас как-то серьезно дискутировать на тему возможности или невозможности таких решений, но ощущение опасности от нового API у меня есть.

В новом API есть два класса для определения длительности.

java.time.Period — описание календарной длительности (периода) в виде кортежа (год, месяц, день).

java.time.Duration — описание точной длительности в виде целого количества секунд и долей текущей секунды в виде наносекунд.

Разницу между двумя можно показать в примере с днем перевода стрелок на зимнее время. Из-за перевода стрелок назад этот календарный день состоит из 25 часов.

Читайте также: