Как инициализировать поле константного объекта для класса в Java - PullRequest
0 голосов
/ 25 июня 2019

Интересно, как лучше всего инициализировать постоянное поле типа сложного объекта для класса, который имеет наибольшую производительность?

1) Встроенная инициализация

public class TopClass {
   private static final ComplexObject sdf = new ComplexObject();
            
   public TopClass (
   }
}

2) Методы инициализации

public class TopClass {
     private static final ComplexObject sdf = initializeComplexObject();
     private static ComplexObject initializeComplexObject(){
                return sdf == null ? new ComplexObject() : sdf;
     }
     public TopClass (
     }
}

3) Инициализация в конструкторах, 4) Статические блоки инициализатора или какой другой подход вы предлагаете ...

Инициализирует ли sdf каждый раз при создании нового класса TopClass?Я хочу, чтобы поле sdf инициализировалось только один раз за время существования приложения.

Ответы [ 3 ]

3 голосов
/ 25 июня 2019

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

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

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

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

3) Не инициализировать статические члены в конструкторе.Конструкторы предназначены для инициализации элементов экземпляра.

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

4) Инициализация поля при его объявлении - это просто сокращение дляобъявив статический инициализатор.Первый кодовый блок в вопросе семантически идентичен следующему:

private static final ComplexObject sdf;

static {
  sdf = new ComplexObject(); 
}

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

Статические инициализаторы являются sort-как анонимные методы.Внутренние практики Google по Java рекомендуют использовать методы вместо явных статических блоков инициализатора, где это возможно, отчасти потому, что вы можете явно вызывать их для тестов, а также потому, что они обязательно заставляют вас инициализировать только одно поле.(Я в значительной степени согласен, что это хороший совет, но учтите, что вы теряете определенную проверку присваивания в методах - как показано выше - которые могут помочь в обнаружении определенных типов ошибок).

В заключение: используйте private static final ComplexObject sdf = new ComplexObject();, потому чтовам не нужно ничего более сложного.


Гораздо большая проблема здесь заключается в правильности, а именно в том, что sdf не разделен между потоками: до того, как вы отредактировали вопрос, sdf был SimpleDateFormat, который не является потокобезопасным.Я понятия не имею, что такое ComplexObject, но вы должны убедиться, что он потокобезопасен или что он доступен потокобезопасным способом.Беспокойство о таких вещах до микрооптимизаций.

1 голос
/ 25 июня 2019

Как говорит другой ответ: производительность абсолютно здесь не проблема. При запуске JVM он должен загружать, вероятно, тысячи, если не сотни тысяч классов. То, как во время этого процесса инициализируется одна константа, совершенно не имеет значения. Лучше всего говорить о наносекундах для разных подходов.

Таким образом, единственное, что остается для принятия решения: идеи чистого кодирования, как в: каков «самый» читаемый / понятный для человека способ взглянуть на это.

Я думаю: если возможно, вы выбираете вариант 1. Если выражение не слишком сложное, и можно просто посмотреть на него и понять SOME_CONSTANT = some expression, зачем добавлять сложность блока вызова метода / инициализатора к нулю, усложнять вещи?

Но, конечно: когда выражение уже «сложное», и вы испытываете желание написать комментарий, объясняющий , почему делает что-то определенным образом, тогда вспомогательный метод - хорошая идея. , Одно только полезное имя метода может объяснить, что нужно объяснить (избавив вас от необходимости комментировать!)

Другими словами: всегда сосредотачивайтесь на наименьшем количестве кода, который также легко читается и понимается. Вы не используете метод инициализатора, потому что можете, а потому, что это делает вещи проще для понимания (что в вашем случае: это не так). Блоки инициализатора (imho) хуже, просто потому, что они такие редкие. На мой взгляд, это аномалия, ведь вы можете даже создавать Карты, Списки, ... как "литералы" в наши дни.

0 голосов
/ 25 июня 2019

@ Ответ Энди Тернера в значительной степени отвечает на этот вопрос, но я хочу добавить дополнительное соображение при проектировании, поскольку ваш пример объекта ComplexObject и вы, кажется, сосредоточены на аспекте производительности.

Если вы хотите создать экземпляр объекта только один раз, но эта инициализация довольно дорогая (ComplexObject может предложить такую ​​вещь), вы можете захотеть инициализировать объект только после того, как он действительно используется, т.е. доступ ( отложенная инициализация и отложенная загрузка ).

В простом виде вы, в основном, скрываете ComplexObject за статическим геттером private static ComplexObject getComplexObject(). Тогда вы можете использовать определенные идиомы:

Если вам не нужен дополнительный метод, вы можете определить класс Lazy, например ::

class Lazy<T> implements Supplier<T> {

    private T value;
    private final Supplier<T> initializer;

    public Lazy(final Supplier<T> initializer) {
        this.initializer = initializer;
    }

    @Override
    public T get() {
        if (value == null) {
            synchronized (this) {
                if (value == null) {
                    value = initializer.get();
                }
            }
        }
        return value;
    }
}

public class TopClass {
    private static final Lazy<ComplexObject> sdf = new Lazy<>(ComplexObject::new);

    public void exampleMethod() {
        ComplexObject o = sdf.get();
    }
}

Если вам не нужен дополнительный вызов метода get(), вы можете использовать прокси-шаблон , если вы можете инкапсулировать поведение ComplexObject в интерфейсе. Затем прокси должен делегировать все вызовы методов ComplexObject реальному экземпляру (В этом случае я использовал предыдущий код для создания экземпляра, расширив Lazy, который использует доступ через get(), но Вы можете реализовать ленивый init любым способом в своем прокси).

interface ComplexObject {}

class ComplexObjectImpl implements ComplexObject {}

class ComplexObjectProxy extends Lazy<ComplexObjectImpl> implements ComplexObject {

    public ComplexObjectProxy() {
        super(ComplexObjectImpl::new);
    }
}

public class TopClass {
    private static final ComplexObject sdf = new ComplexObjectProxy();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...