Проектное решение. В чем польза/преимущество отдельного класса RandomNumberGeneratorHolder в Math.java?

Итак, я просматривал исходный код Math.java и обнаружил, что существует класс-держатель, созданный для хранения randomNumberGenerator статическая переменная. Вот соответствующий фрагмент кода.

public final class Math {
 // other methods.
 public static double random() {
   return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
 }

 private static final class RandomNumberGeneratorHolder {
   static final Random randomNumberGenerator = new Random();
 }
}

ИМО, мы могли бы просто объявить randomNumberGenerator как private static final внутри самого класса Math.

Мой вопрос: есть ли какое-либо преимущество в создании для этого отдельного класса держателя? Или это просто личные предпочтения.


person Hardik Modha    schedule 17.08.2018    source источник


Ответы (4)


Это пример шаблона инициализации-по-запросу. Когда класс Math загружается JVM, экземпляр Random не будет создан немедленно. Вместо этого это произойдет только при вызове метода random(), после чего будет загружен класс RandomNumberGenreatorHolder и построен объект Random singleton.

По сути, код обеспечивает ленивое создание синглтона Random.

person Richard Fearn    schedule 17.08.2018
comment
Спасибо! Это имеет смысл. :) - person Hardik Modha; 17.08.2018

Они оба lazy, но один lazier (одноэлементный шаблон). Наличие поля private static будет означать, что когда класс, содержащий это поле, инициализируется, это поле также инициализируется. Поэтому, если вам нужно вызвать метод этого класса, но этот синглтон вам не нужен, он все равно будет инициализирован.

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

Точнее, это очень редкий случай, когда это имеет значение, и сам jdk, скорее всего, является одним из очень-очень немногих примеров.

person Eugene    schedule 17.08.2018
comment
Спасибо за ответ! Это очень полезно. - person Hardik Modha; 17.08.2018

Это пример синглтона с ленивой инициализацией, реализованного с помощью вложенных классов.

«Держатель» инициализируется при первом вызове random(). Первый вызов запускает выполнение статической инициализации класса RandomNumberGeneratorHolder.

(Стоит делать это лениво. Инициализация случайного генератора без предоставления начального числа влечет за собой обращение к ОС, чтобы получить некоторую «энтропию» для заполнения генератора. Это относительно дорогая операция. Вы не хотите, чтобы JVM несла такие затраты, которые если random() не будет вызываться.)

person Stephen C    schedule 17.08.2018

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

person batero    schedule 17.08.2018