Подтвердить что ты не робот

Необработанные исключения при инициализации полей

Есть ли у Java какой-либо синтаксис для управления исключениями, которые могут возникать при объявлении и инициализации переменной-члена класса?

public class MyClass
{
  // Doesn't compile because constructor can throw IOException
  private static MyFileWriter x = new MyFileWriter("foo.txt"); 
  ...
}

Или такие инициализации всегда нужно переместить в метод, где мы можем объявить throws IOException или обернуть инициализацию в блоке try-catch?

4b9b3361

Ответ 1

Использовать статический блок инициализации

public class MyClass
{
  private static MyFileWriter x;

  static {
    try {
      x = new MyFileWriter("foo.txt"); 
    } catch (Exception e) {
      logging_and _stuff_you_might_want_to_terminate_the_app_here_blah();
    } // end try-catch
  } // end static init block
  ...
}

Ответ 2

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

Ответ 3

Исключение, созданное статическим intialiser, может указывать на проблему проектирования. Действительно, вы не должны пытаться загружать файлы в статику. Также статический не должен, вообще говоря, быть изменчивым.

Например, работая с JUnit 3.8.1, вы почти могли использовать его из апплета /WebStart, но это не удалось из-за того, что один статический инициализатор делал доступ к файлам. Остальная часть рассматриваемого класса была подобрана в контексте, это всего лишь эта бит статичности, которая не соответствовала контексту и ударила всю структуру.

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

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

private static final Thing thing;

static {
    try {
        thing = new Thing();
    } catch (CheckedThingException exc) {
        throw new Error(exc);
    }
}

или

private static final Thing thing = newThing();

private static Thing newThing() {
    try {
        return new Thing();
    } catch (CheckedThingException exc) {
        throw new Error(exc);
    }
}

Примечание: статика должна быть окончательной (и, как правило, неизменной). Будучи окончательным, правильное одиночное задание проверяется вашим дружественным компилятором. Определенное присваивание означает, что он может ловить обработанную обработку исключений - обернуть и выбросить, не печатать/записывать. Как ни странно, вы не можете использовать имя класса для квалификации инициализации с именем класса в статическом инициализаторе (я уверен, что для этого есть веская причина).

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

Ответ 4

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

Итак, это был бы законный способ сделать это:

public class MyClass {
    MyFileWriter x;
    {
        try {
            x = new MyFileWriter("foo.txt");
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
    ...
}

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

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

Изменить: Когда я писал это, "static" не было добавлено в поле. Это немного меняет ситуацию: теперь я хотел бы знать, почему на земле вы хотите, чтобы писатель был открыт для всей жизни загрузчика. Как он может быть закрыт?

Это какая-то доморощенная система каротажа? Если это так, я бы посоветовал вам взглянуть на java.util.logging или на любую из многих сторонних сторонних систем ведения журнала.

Ответ 5

Если класс имеет только один конструктор, я обычно перемещаю такие инициализаторы в этот конструктор.

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

public class MyClass {
   private static MyFileWriter x;

   // initialization block start here
   {
       try {
          x = new MyFileWriter("..");
       } catch(Exception e) {
         // exception handling goes here
       }
   }

   public MyClass() { 
    // ctor #1
   }

   public MyClass(int n) { 
     // ctor #2
   }
}

Хорошая вещь об инициализации. блок состоит в том, что он "вводится" в начало каждого конструктора. Таким образом, вам не нужно дублировать инициализаторы.

Ответ 6

Я предлагаю метод factory:

  public class MyClass{
      private static MyFileWriter fileWriter = MyFileWriter.getFileWriter("foo.txt");

  }

  public class MyFileWriter {
     /*
      * Factory method. Opens files, etc etc 
      * @throws IOException.
      */
     public static MyFileWriter getFileWriter(String path) throws IOException{

        MyFileWriter writer  = new FileWriter();

       //stuff that can throw IOException here.

       return writer;
     }

   /*protected constructor*/
    protected MyFileWriter(){

     //build object here.

    } 
  }

Ответ 7

Существует другой способ обработки исключений в инициализации полей. Рассмотрим ваш случай, когда конструктор MyFileWriter генерирует исключение. Рассмотрим этот пример кода для ссылок.

import java.io.IOException;
public class MyFileWriter {
    public MyFileWriter(String file) throws IOException{
         throw new IOException("welcome to [java-latte.blogpspot.in][1]");
    }
}

Если мы попытаемся инициализировать файл, как это.....

public class ExceptionFields{
    MyFileWriter f = new MyFileWriter("Hello");

}

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

Объявить конструктор по умолчанию, который вызывает IOException

import java.io.IOException;
public class ExceptionFields{
    MyFileWriter f = new MyFileWriter("Hello");
    public ExceptionFields() throws IOException{    

    }    
}

Это приведет к инициализации объекта MyFileWriter.