Работа со статическим конструктором в Java

11

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

Kapeel45
источник
2
Обмен вашими исследованиями помогает всем. Расскажите нам, что вы пробовали и почему это не соответствует вашим потребностям. Это свидетельствует о том, что вы потратили время, чтобы попытаться помочь себе, избавляет нас от повторения очевидных ответов и, прежде всего, помогает получить более конкретный и актуальный ответ. Также см. Как спросить
Гнат
3
Нет такой вещи как «статический конструктор».
Дэвид Конрад

Ответы:

28

Строго говоря, в Java нет статических конструкторов, поскольку конструктор по определению не может быть статическим. То, на что вы ссылаетесь, называется «статическим блоком инициализации». Конструктор подразумевает, что вы создаете объект. Вы не можете иметь конструктор для класса, потому что класс не является его экземпляром. Это просто класс. То, что «конструирует» класс, называется компилятором (или виртуальной машиной, в зависимости от того, что подразумевается под «конструкциями»), и если вы попадаете в конструирование кода внутри другого кода, вы попадаете в процесс генерации кода, который совершенно другой зверь.

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

По сути, их можно использовать, чтобы сказать классу: «Эй, установите переменную A на это значение FIRST, а затем, как только это будет сделано, используйте значение A для инициализации B.» Поскольку Java требует, чтобы стандартная инициализация поля выполнялась либо внутри конструктора или метода, либо с помощью вызова конструктора или метода (если это не литерал), они могут быть удобным методом для инициализации сложных статических объектов.

Блоки статической инициализации не нужны слишком часто, и, как правило, их следует избегать, если они не имеют реального использования. Не поймите меня неправильно, они занимают свое место в Java, но, как и многие другие вещи (такие как операторы break, return, switch и goto), их можно легко использовать чрезмерно, что снижает их читаемость и удобство сопровождения кода. -на базе они используются в.

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

Код:

public class StaticExample{
    static {
        System.out.println("This is first static block");
    }

    public StaticExample(){
        System.out.println("This is constructor");
    }

    public static String staticString = "Static Variable";

    static {
        System.out.println("This is second static block and "
                                                    + staticString);
    }

    public static void main(String[] args){
        StaticExample statEx = new StaticExample();
        StaticExample.staticMethod2();
    }

    static {
        staticMethod();
        System.out.println("This is third static block");
    }

    public static void staticMethod() {
        System.out.println("This is static method");
    }

    public static void staticMethod2() {
        System.out.println("This is static method2");
    }
}    

Выход:

This is first static block
This is second static block and Static Variable
This is static method
This is third static block
This is constructor
This is static method2

Некоторые примеры, которые они перечисляют, когда статические блоки могут быть полезны:

  • Если вы загружаете драйверы и другие элементы в пространство имен. Например, класс Class имеет статический блок, в котором он регистрирует нативные.
  • Если вам нужно выполнить вычисления для инициализации статических переменных, вы можете объявить статический блок, который выполняется ровно один раз, когда класс загружается впервые.
  • Проблемы, связанные с безопасностью или ведение журнала задач

Некоторые причины НЕ использовать статические блоки (в других ситуациях):

  • В JVM есть ограничение: блок статического инициализатора не должен превышать 64 КБ.
  • Вы не можете бросить Проверенные исключения.
  • Вы не можете использовать thisключевое слово, так как нет экземпляра.
  • Вы не должны пытаться получить доступ к супер, так как для статических блоков такого нет.
  • Вы не должны ничего возвращать из этого блока.
  • Статические блоки делают тестирование кошмаром.

Я должен отметить: в то время как некоторые языки (такие как C #) могут иметь синтаксис для «конструкторов», которые являются статическими, эти «конструкторы» функционируют почти так же, как статические блоки инициализации в Java, и рассматриваются многими (включая меня) как неправильные в языке, учитывая основную концепцию конструктора ООП .

Gurgadurgen
источник
ссылка о статических блоках больше не работает
АШ
5

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

public class Example{

    public final static Map<String, String> preFilledField;

    static{
        Map<String, String> tmp = new HashMap<>();
        //fill map
        preFilledField = Collections.unmodifiableMap(tmp);
    }
}

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

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

чокнутый урод
источник
Это было одно из основных применений, которые я видел, но с включением статических методов фабрики для структуры коллекций в Java 9 этот вариант использования действительно устарел.
Hangman4358
2

Ответ Гургадургена, вероятно, то, что вы ищете, но я просто добавлю пару других моментов, которые иногда игнорируются, когда кто-то хочет «статический конструктор».

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

public class Example
{
    /** Static method to create an instance. */
    public static Example build()
    { return new Example() ; }

    /** A field of each instance. */
    private String stuff ;

    /** The class's actual constructor. */
    public Example()
    { stuff = new String() ; }

    public String getStuff()
    { return this.stuff ; }

    /**
     * Mutator for "stuff" property. By convention this returns "void"
     * but you might want to return the object itself, to support the
     * sort of chained invocations that are becoming trendy now. You'll
     * see the stylistic benefits of chaining later in this example.
     */
    public Example setStuff( String newStuff )
    {
        this.stuff = newStuff ;
        return this ;
    }
}

public class ExampleTest
{
    public static void main( String[] args )
    {
        // The usual instance model.
        Example first = new Example() ;
        System.out.println( first.setStuff("stuff").getStuff() ) ;

        // Using your static method to construct an instance:
        Example second = Example.build() ;
        System.out.println( second.setStuff("more stuff").getStuff() ) ;

        // Chaining all the invocations at once:
        System.out.println( Example.build().setStuff("even more stuff").getStuff() ) ;
    }
}

Это производит вывод:

stuff
more stuff
even more stuff

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

public class SingletonExample extends Example
{
    // Note: This extends my previous example, which has a "stuff"
    // property, and a trivial constructor.

    /** The singleton instance, statically initialized as null. */
    private static SingletonExample singleton = null ;

    /**
     * The static accessor for the singleton. If no instance exists,
     * then it will be created; otherwise, the one that already exists
     * will be returned.
     */
    public static SingletonExample getInstance()
    {
        if( singleton == null )
            singleton = new SingletonExample() ;
        return singleton ;
    }
}

public class SingletonExampleTest
{
    public static void main( String[] args )
    {
        System.out.println( SingletonExample.getInstance().setStuff("stuff").getStuff() ) ;

        // You could still create instances of this class normally if you want to.
        SingletonExample otherstuff = new SingletonExample() ;
        otherstuff.setStuff("other stuff") ;

        // But watch what happens to this.
        System.out.println( SingletonExample.getInstance().getStuff() ) ;
        System.out.println( otherstuff.getStuff() ) ;

        // Now we show what happens when you start modifying the singleton.
        SingletonExample theoneandonly = SingletonExample.getInstance() ;
        theoneandonly.setStuff("changed stuff") ;
        System.out.println( SingletonExample.getInstance().getStuff() ) ;
    }
}

Это производит следующее.

stuff
stuff
other stuff
changed stuff

Сохраняя ссылку на синглтон, а затем изменяя его, следующий вызов getInstance()получает этот модифицированный синглтон.

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

zerobandwidth
источник
0

Хотя я понимаю, что многие считают понятие «статический конструктор» неправильным, я не верю, что это так. Проблема заключается в создании классов и их экземпляров объектов . В других потоках говорилось, что создание класса - это работа компилятора. Даже в Java это только наполовину правда.

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

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

Arkain
источник
0

Как уже говорили другие ответы, вы можете написать статические методы, которые создают объект. Это обходит отсутствие именованных конструкторов в Java (и во многих других языках. Delphi поддерживает именованные конструкторы). Вы можете играть с типами и порядком параметров, но это может привести к нечеткому и хрупкому коду.

Например, мы могли бы придумать сценарий, в котором ваш объект может быть построен из строки XML или строки JSON. Вы можете написать такие методы, как:

static MyObject createFromXml(String xml);
static MyObject createFromJson(String json);

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

MyObject myObject = new MyObject();
myObject.loadXml(xml). 

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

kiwiron
источник
Когда я читаю вопрос, речь идет о статических инициализаторах, а не о нормальных статических emthods.
CodesInChaos
-2

Вы можете просмотреть «статический» раздел как конструктор уровня класса, используемый для инициализации свойств класса (статический в Java). Как и «обычный» конструктор, он используется для инициализации свойств уровня экземпляра.

AlfredoCasado
источник