28 Stimmen

Unbehandelte Ausnahmen bei Feldinitialisierungen

Verfügt Java über eine Syntax für die Verwaltung von Ausnahmen, die bei der Deklaration und Initialisierung von Klassenvariablen ausgelöst werden können?

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

Oder müssen solche Initialisierungen immer in eine Methode verlagert werden, in der wir deklarieren können throws IOException oder die Initialisierung in einen try-catch-Block verpacken?

20voto

MattC Punkte 12223

Verwendung eines statischen Initialisierungsblocks

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
  ...
}

3voto

akf Punkte 37387

Ist es am besten, diese Art von Initialisierungen in Methoden zu verlagern, die mit Ausnahmen umgehen können.

2voto

Michael Myers Punkte 183216

Diese Konstruktion ist, wie Sie festgestellt haben, illegal. Mitglieder, deren Konstruktoren geprüfte Ausnahmen auslösen, können nicht konstruiert werden, es sei denn, sie befinden sich in einem Kontext, der die Behandlung von Ausnahmen zulässt, wie ein Konstruktor, ein Instanzinitialisierer oder (für ein statisches Mitglied) ein statischer Initialisierer.

Dies wäre also ein legaler Weg, dies zu tun:

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

Legal, aber ziemlich hässlich. Ich würde es vorziehen, sie entweder im Konstruktor zu initialisieren und die Ausnahme dort zu deklarieren, oder den Benutzer eine Methode aufrufen zu lassen, um sie explizit zu initialisieren. Aber wenn der Benutzer es initialisieren muss, müssen Sie dann in allen abhängigen Methoden die Möglichkeit berücksichtigen, dass das Objekt ungültig ist.

Wenn Sie schreiben MyClass als Umhüllung für MyFileWriter würde ich sagen, dass die Initialisierung im Konstruktor erfolgen sollte. Andernfalls würde ich zunächst in Frage stellen, ob es notwendig ist, einen Writer während der gesamten Lebensdauer des Objekts geöffnet zu haben. Möglicherweise lässt sich dies durch Refactoring beheben.

Bearbeiten: Als ich dies schrieb, war die " static " nicht in das Feld eingefügt worden war. Das ändert die Dinge ziemlich: Ich würde jetzt gerne wissen, warum in aller Welt Sie einen Schreiber für die des Classloaders ein ganzes Leben lang. Wie könnte sie jemals geschlossen werden?

Handelt es sich dabei um eine Art selbst entwickeltes Protokollierungssystem? Wenn ja, würde ich Sie ermutigen, einen Blick zu werfen auf java.util.logging oder eines der vielen guten Logging-Frameworks von Drittanbietern.

2voto

Tom Hawtin - tackline Punkte 142461

Eine von einem statischen Intialisierer ausgelöste Ausnahme kann auf ein Designproblem hinweisen. Sie sollten wirklich nicht versuchen, Dateien in Statics zu laden. Auch statisch sollte im Allgemeinen nicht veränderbar sein.

Wenn man zum Beispiel mit JUnit 3.8.1 arbeitete, konnte man es fast von einem Applet/WebStart aus verwenden, aber es scheiterte an einem statischen Initialisierer, der Dateizugriffe durchführte. Der Rest der betroffenen Klasse passte gut in den Kontext, nur dieser Teil der Statik passte nicht in den Kontext und machte das ganze Framework kaputt.

Es gibt einige legitime Fälle, in denen eine Ausnahme ausgelöst wird. Wenn es sich um einen Fall handelt, in dem die Umgebung eine bestimmte Funktion nicht hat, z.B. weil es sich um ein altes JDK handelt, dann möchten Sie vielleicht Implementierungen ersetzen, und es ist nichts Ungewöhnliches. Wenn die Klasse wirklich defekt ist, werfen Sie eine ungeprüfte Ausnahme, anstatt eine defekte Klasse existieren zu lassen.

Je nach Vorliebe und Problemstellung gibt es zwei gängige Möglichkeiten, dies zu umgehen: ein expliziter statischer Initialisierer und eine statische Methode. (Ich, und ich denke die meisten Leute, bevorzugen Ersteres; ich glaube, Josh Bloch bevorzugt Letzteres).

private static final Thing thing;

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

Oder

private static final Thing thing = newThing();

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

Hinweis: Statics sollten endgültig (und im Allgemeinen unveränderlich) sein. Da sie endgültig sind, wird die korrekte Zuweisung von Ihrem freundlichen Compiler überprüft. Definitive Zuweisung bedeutet, dass es zu einer fehlerhaften Ausnahmebehandlung kommen kann - wrap and throw, do not print/log. Seltsamerweise können Sie den Klassennamen nicht verwenden, um die Initialisierung mit dem Klassennamen im statischen Initialisierer zu qualifizieren (ich bin sicher, es gibt einen guten Grund dafür).

Instanzinitialisierer sind ähnlich, allerdings kann man den Konstruktor zum Throw machen oder den Initialisierer im Konstruktor unterbringen.

-1voto

Itay Maman Punkte 29121

Wenn die Klasse nur einen Konstruktor hat, verschiebe ich solche Initialisierungen normalerweise in diesen Konstruktor.

Wenn die Klasse mehr als einen Konstruktor hat, verwende ich einen Initialisierungsblock:

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
   }
}

Das Schöne an einem Init-Block ist, dass er in den Anfang jedes Konstruktors "injiziert" wird. So müssen Sie die Initialisierungen nicht duplizieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X