Робота зі статичним конструктором на Java


12

Я не отримав повного розуміння статичних конструкторів на Java. Якщо це дозволено, чому це дозволено? У яких сценаріях ви б його використовували? Якій меті це послужило б? Чи може хтось надати мені простий приклад?


2
Обмін дослідженнями допомагає всім. Розкажіть, що ви пробували і чому це не відповідало вашим потребам. Це свідчить про те, що ви знайшли час, щоб спробувати допомогти собі, це позбавляє нас від повторення очевидних відповідей, а найбільше це допомагає вам отримати більш конкретну та релевантну відповідь. Також дивіться Як просити
gnat

3
Немає такого поняття, як "статичний конструктор".
Девід Конрад

Відповіді:


28

Строго кажучи, Java не має статичних конструкторів, оскільки конструктор, за визначенням, не може бути статичним. Те, що ви маєте на увазі, називається "блоком статичної ініціалізації". Конструктор означає, що ви будуєте об’єкт. Ви не можете мати конструктор для класу, оскільки клас не є екземпляром сам по собі. Це просто клас. Те, що "конструює" клас, називається компілятором (або віртуальною машиною, залежно від того, що мається на увазі під "конструктами"), і якщо ви потрапляєте в конструювання коду в рамках іншого коду, ви потрапляєте в генерацію коду, тобто зовсім інший звір.

Усі ниткові вилучення в сторону, статичний блок ініціалізації використовується для ініціалізації складних статичних (або рівня рівня) полів для класу. Зазвичай вони використовуються для ініціалізації речей, які або не можуть бути ініціалізовані в одному рядку, або вимагають, щоб спочатку ініціалізувався якийсь інший об'єкт (який може бути або не може бути в класі, в якому реалізований статичний блок).

В основному, ви можете використовувати їх, щоб сказати класу "Ей, встановіть змінну A на це значення ПЕРШЕ, тоді, як тільки це буде зроблено, використовуйте значення A для ініціалізації B." Оскільки Java вимагає, щоб стандартна ініціалізація поля була здійснена або в конструкторі чи методі, або через виклик конструктора чи методу (якщо це не буквально), вони можуть бути зручним методом ініціалізації складних статичних об'єктів.

Блоки статичної ініціалізації не потрібні занадто часто, і їх взагалі слід уникати, якщо вони не мають реального використання. Не зрозумійте мене неправильно, вони займають своє місце в Java, але, як і багато інших речей (таких як заяви перерви, повернення, переключення та 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 у тому, що блок статичного ініціалізатора не повинен перевищувати 64K.
  • Ви не можете кидати перевірені винятки.
  • Ви не можете використовувати thisключове слово, оскільки немає жодного примірника.
  • Не слід намагатися отримати доступ до супер, оскільки у статичних блоків такого немає.
  • Вам не слід повертати нічого з цього блоку.
  • Статичні блоки роблять тестування кошмаром.

Я маю зазначити: Хоча деякі мови (наприклад, C #) можуть мати синтаксис для "конструкторів", які є статичними, ці "конструктори" функціонують так само, як і блоки статичної ініціалізації в Java, і багато хто (як я включаю) сприймається як помилки в мові, з огляду на базову концепцію конструктора ООП .


посилання про статичні блоки вже не працює
ASh

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

3

Відповідь Гургадургена - це, мабуть, те, що ви шукаєте, але я лише додам пару інших пунктів, які іноді нехтують, коли хтось хоче «статичного конструктора».

Якщо ви хочете статичний метод, який створює екземпляр вашого класу, ви можете створити статичний метод, який просто викликає конструктор класу.

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 до використання нового "навантажувача класів", який не буде знати про синглтон, який зберігався статично попереднім завантажувачем класів.


0

Хоча я розумію, що існує багато людей, які уявляють поняття "статичний конструктор" як неправильне значення, я не вважаю, що це так. Проблема полягає в процесі побудови як класів, так і їх об'єктів . В інших потоках було сказано, що побудова класу - це завдання компілятора. Навіть на Java це справді лише наполовину.

Компілятор будує ешафот для кожного визначення класу. Ешафот містить метадані про клас та про те, які екземпляри повинні бути в них під час побудови. Якщо клас визначає поле, якому присвоєно постійне примітивне значення, то це значення включається у риштування компілятором. Для будь-яких інших присвоєних типів значень компілятор генерує процедуру ініціалізації, яку слід запустити 1 раз до створення екземпляра першого класу, який оновлює ешафот належними значеннями. Компілятор не може виконати це оновлення .

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


0

Як сказано в інших відповідях, ви можете написати статичні методи, що будують об'єкт. Це усуває відсутність названих конструкторів на Java (та багатьох інших мовах. Delphi підтримує названі конструктори). Ви можете грати з типами та порядком параметрів, але це може призвести до незрозумілого та крихкого коду.

Наприклад, ми могли б винайти сценарій, коли ваш об'єкт може бути побудований з XML-рядка або рядка JSON. Ви можете написати такі методи, як:

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

Я дуже часто використовував це як альтернативу конструктору без параметрів з названими методами ініціалізації:

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

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


Коли я читаю питання, мова йде про статичні ініціалізатори, а не про звичайні статичні емтоди.
CodesInChaos

-2

Ви можете переглядати розділ "статичний", як конструктор рівня класу використовує для ініціалізації властивостей класу (статичний у Java). Те саме, що і "звичайний" конструктор, який використовується для ініціалізації властивостей рівня екземпляра.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.