Как лучше всего инициализировать сложный статический член в Java?

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

public class MyClass {
    private static Properties DEFAULT_PROPERTIES = new Properties();

    static {
        try {
           DEFAULT_PROPERTIES.load(
               MyClass.class.getResourceAsStream("myclass.properties"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
 }

Глядя на это, это работает, но это не кажется правильным.

Как бы вы это сделали?


person Igor    schedule 08.01.2010    source источник


Ответы (3)


В основном есть два пути. Первый способ - использовать статический блок, как вы показали (но затем с ExceptionInInitializerError вместо RuntimeException). Второй способ - использовать статический метод, который вы вызываете сразу после объявления:

private static Properties DEFAULT_PROPERTIES = getDefaultProperties();

private static Properties getDefaultProperties() {
    Properties properties = new Properties();
    try {
        properties.load(MyClass.class.getResourceAsStream("myclass.properties"));
    } catch (IOException e) {
        throw new ConfigurationException("Cannot load properties file", e);
    }
    return properties;
}

ConfigurationException может быть просто вашим собственным классом, расширяющим RuntimeException.

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

private static Properties DEFAULT_PROPERTIES = SomeUtil.getProperties("myclass.properties");

// Put this in a SomeUtil class.
public static Properties getProperties(String filename) {
    Properties properties = new Properties();
    try {
        properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(filename));
    } catch (IOException e) {
        throw new ConfigurationException("Cannot load " + filename, e);
    }
    return properties;
}
person BalusC    schedule 08.01.2010
comment
Это на самом деле подходит для моей проблемы, потому что я создам больше классов с одним и тем же шаблоном, поэтому имеет смысл создать абстрактный класс и заполнить его методом getProperties(), чтобы его можно было повторно использовать во всех моих подклассах. Очень информативно, спасибо! - person Igor; 08.01.2010
comment
Обратите внимание, что я изменил способ загрузки файла свойств, используя Thread#getContextClassLoader(). Не забудьте также изменить это в своем коде. - person BalusC; 08.01.2010
comment
Будет ли это загружать ресурс в том же пакете, что и класс, вызывающий getProperties()? - person Igor; 08.01.2010
comment
Я не уверен, что понимаю тебя. В любом случае это просто независимый от пакета. Вы можете указать пакет в имени файла. Улучшенный способ просто не зависит от класса и использует загрузчик классов, который уже загрузил вызывающий класс. Таким образом вы гарантируете, что ресурс никогда не вернет значение null, что может произойти, если вызывающий класс не загружен загрузчиком классов потока. - person BalusC; 08.01.2010
comment
Я имел в виду, что org.example.MyClass.class.getResourceAsStream("myclass.properties") откроет ресурс ROOT/org/example/myclass.properties, а при использовании ClassLoader будет искать ресурсы в каталоге ROOT/. Я ошибся? - person Igor; 08.01.2010
comment
О, сюда. Да, просто укажите пакет в имени файла. - person BalusC; 08.01.2010

Вместо универсального RuntimeException я бы выдал ExceptionInInitializerError, который предназначен именно для этой цели. Из документации API: «Сигнализирует о том, что в статическом инициализаторе произошло непредвиденное исключение».

person jarnbjo    schedule 08.01.2010
comment
О, я этого не знал. Спасибо за подсказку! - person Igor; 08.01.2010
comment
@jarnbjo Я думаю, было бы полезно, если бы вы также предоставили ссылку на документ API (ExceptionInitializer): java.sun.com/j2se/1.5.0/docs/api/java/lang/ - person sateesh; 08.01.2010
comment
@sateesh: Если разработчик Java не может найти класс в документации API без ссылки в моем ответе, ему пора научиться это делать. - person jarnbjo; 08.01.2010

Мне кажется приемлемым; load в статическом инициализаторе, он вызывается только при ссылке на класс и вызывается только один раз. Мне это нравится. Единственное, что я бы сделал, это сделал бы это final.

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

person Noon Silk    schedule 08.01.2010