Возврат в статическом инициализаторе

Недействительный код:

public class MyClass
{
    private static boolean yesNo = false;

    static
    {
        if (yesNo)
        {
            System.out.println("Yes");
            return; // The return statement is the problem
        }
        System.exit(0);
    }
}

Это глупый пример, но в статическом конструкторе классов мы не можем return;. Зачем? Есть ли веские причины для этого? Кто-нибудь знает что-то об этом?

Таким образом, причина, по которой мне следует делать return, - это закончить ее там.

Спасибо

Ответ 1

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

Так, например, когда вы пишете класс:

class A {
    static int x = 3;
    static {
        y = x * x;
    }
    static int z = x * x;
}

Затем это на самом деле, как будто вы написали:

class A {
    static int x, y, z;
    static {
        x = 3;
        y = x * x;
        z = x * x;
    }
}

Это подтверждается, если вы посмотрите на разборку:

static {};
  Code:
   0:   iconst_3
   1:   putstatic       #5; //Field x:I
   4:   getstatic       #5; //Field x:I
   7:   getstatic       #5; //Field x:I
   10:  imul
   11:  putstatic       #3; //Field y:I
   14:  getstatic       #5; //Field x:I
   17:  getstatic       #5; //Field x:I
   20:  imul
   21:  putstatic       #6; //Field z:I
   24:  return

Итак, если бы вы добавили "возврат" где-то посередине вашего статического инициализатора, это также предотвратило бы вычисление z.

Ответ 2

  • поток программ всегда может быть структурирован без необходимости return. (В вашем примере размещение System.exit(0) в предложении else приведет к желаемому результату)

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

.

static {
    staticInit();
}

private static void staticInit() {
    if (yesNo) {
        System.out.println("Yes");
        return;
    }
    System.exit(0);
}

Обратите внимание, что это не статический конструктор, это статический инициализатор. Ничего не построено.

Ответ 3

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

Ответ 4

От JSL относительно статических инициализаторов:

"Это ошибка времени компиляции для статического инициализатора, чтобы он мог внезапно завершить (§14.1, §15.6) с проверенным исключением (§11.2). Это ошибка времени компиляции, если статический инициализатор не может завершить обычно (§14.21)."

Абсолютное завершение (среди прочего): "возврат без значения", "возврат с заданным значением" и т.д.

Таким образом, оператор return в статическом инициализаторе является "внезапным завершением" и создает ошибку времени компиляции.

Ответ 5

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

Обратите внимание, что можно загрузить байт-код без его инициализации; см. метод Class.forName(String, boolean, ClassLoader). Если параметр boolean false, тогда он загрузит класс, но не инициализирует его. Программист все еще может сделать некоторое отражение, чтобы обнаружить информацию об этом классе до сих пор, не будучи инициализированным. Однако, как только вы попытаетесь использовать класс напрямую, вызвав статический метод или создав экземпляр экземпляра, JVM начнет его сначала инициализировать.

Если какой-либо из статических инициализаторов резко прервется - что может произойти с RuntimeException, класс останется в недопустимом состоянии. В первый раз JVM будет бросать ExceptionInInitializeError (уведомление о том, что это Error, что означает, что он считается внутренним сбоем). С этого момента не удастся использовать класс - попытка вызвать статический метод или создать экземпляр объекта, вместо этого вы получите NoClassDefFoundError.

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

Ответ 6

Я бы переупорядочил выражение, сделав его более простым/короче. Никогда не будет хорошего случая, когда обе ветки if/else нуждаются в возврате.

static { 
    if (!yesNo) 
       System.exit(0); // silently exiting a program is a bad idea!"
    System.out.println("Yes"); 
}