Возврат в статическом инициализаторе - PullRequest
12 голосов
/ 09 апреля 2010

Неверный код:

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

Спасибо

Ответы [ 6 ]

15 голосов
/ 09 апреля 2010

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

10 голосов
/ 09 апреля 2010
  • поток программы всегда можно структурировать так, чтобы он не требовался return. (В вашем примере помещение System.exit(0) в предложение else даст желаемый результат)

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

.

static {
    staticInit();
}

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

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

2 голосов
/ 09 апреля 2010

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

1 голос
/ 09 апреля 2010

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

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

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

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

0 голосов
/ 10 апреля 2010

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

static { 
    if (!yesNo) 
       System.exit(0); // silently exiting a program is a bad idea!"
    System.out.println("Yes"); 
} 
0 голосов
/ 09 апреля 2010

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

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

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

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

...