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

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

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 должен закончить построение там.

Спасибо

11
задан Sean Owen 9 April 2010 в 12:08
поделиться

6 ответов

Я думаю, причина в том, что инициализаторы переносятся вместе с инициализациями полей (и с конструкторами в случае инициализаторов экземпляров). Другими словами, 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

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

15
ответ дан 3 December 2019 в 03:17
поделиться

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

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

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

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

0
ответ дан 3 December 2019 в 03:17
поделиться

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

2
ответ дан 3 December 2019 в 03:17
поделиться

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

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

Внезапное завершение (среди прочего):« возврат без значения »,« возврат с заданным значением », и т. д.

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

1
ответ дан 3 December 2019 в 03:17
поделиться
  • поток программы всегда можно структурировать таким образом, чтобы не требовалось return . (В вашем примере размещение System.exit (0) в предложении else приведет к достижению желаемого результата)

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

.

static {
    staticInit();
}

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

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

10
ответ дан 3 December 2019 в 03:17
поделиться

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

static { 
    if (!yesNo) 
       System.exit(0); // silently exiting a program is a bad idea!"
    System.out.println("Yes"); 
} 
0
ответ дан 3 December 2019 в 03:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: