2013-05-20 3 views
6

Ich dachte, dass, intuitiv gesprochen, ein Konstruktor in Java die Sache ist, die ein Objekt macht, und dass nichts dieses Objekt berühren kann, bis sein Konstruktor zurückkommt. Ich habe über diese bewährt falsch jedoch immer und immer wieder:Welche Eigenschaften werden von Konstruktoren in Java garantiert?

  1. nicht initialisierte Objekte durch den Austausch von this
  2. uninitialized objects can be leaked by a subclass accessing it from the finalizer
  3. uninitialized objects can be leaked to another thread before they're fully constructed

Alle verletzen dieser Fakten durchgesickert ist meine Intuition was ich dachte, ist ein Konstruktor.

Ich kann nicht mehr mit Zuversicht sagen, was ein Konstruktor tatsächlich in Java tut, oder wofür es verwendet werden soll. Wenn ich ein einfaches DTO mit alle letzten Felder mache, dann kann ich verstehen, was die Verwendung des Konstruktors ist, denn das ist genau das gleiche wie eine Struktur in C, außer es kann nicht geändert werden. Ansonsten habe ich keine Ahnung, für welche Konstruktoren Java zuverlässig verwendet werden kann. Sind sie nur eine Konvention/syntaktischer Zucker? (Wenn es nur Factories gäbe, die Objekte für Sie initialisieren würden, hätten Sie nur X x = new X(), dann ändern Sie jedes Feld in x, damit sie keine Standardwerte haben - bei den 3 obigen Fakten wäre dies fast gleichbedeutend mit Java.)

kann ich zwei Eigenschaften nennen, die von Konstrukteuren tatsächlich garantiert werden: Wenn ich X x = new X() tun, dann weiß ich, dass x eine Instanz von X ist aber nicht eine Unterklasse von X, und seine endgültigen Feldern vollständig initialisiert werden. Sie könnten versucht sein zu sagen, dass Sie wissen, dass der Konstruktor von X fertig ist und Sie ein gültiges Objekt haben, aber das ist nicht wahr, wenn Sie X an einen anderen Thread übergeben - the other thread may see the uninitialized version (dh was Sie gerade gesagt haben, ist nicht anders als die Garantien für den Aufruf einer Fabrik). Welche anderen Eigenschaften garantieren Konstrukteure eigentlich?

+0

also Ihre Frage ist, welche Eigenschaften garantieren Konstruktoren in Java ja? – Woot4Moo

+0

@ Woot4Moo: Ja, das und/oder was ist der Punkt in ihnen (z. B. wenn sie nur für die Konvention sind, wie Kommentare/Namen)? – Dog

+0

Ich habe den Titel geändert, weil ich denke, dass es mehr Zuschauer bekommen wird. – Woot4Moo

Antwort

6

All diese Fakten verletzen meine Intuition von dem, was ich dachte, ein Konstruktor ist.

Sie sollten nicht. Ein Konstruktor macht genau das, was er zu tun glaubt.

1: nicht initialisierte Objekte durch den Austausch dieses

3 geleckt werden können: nicht initialisierte Objekte können auf einen anderen Thread geleckt werden, bevor sie vollständig aufgebaut sind

Das Problem mit dem Auslaufen von this, Das Starten von Threads im Konstruktor und das Speichern eines neu erstellten Objekts, bei dem mehrere Threads ohne Synchronisierung auf es zugreifen, sind alle Probleme im Zusammenhang mit der Neuanordnung der Initialisierung von nicht endgültigen (und nichtflüchtigen) Feldern. Aber der Initialisierungscode wird immer noch vom Konstruktor ausgeführt. Der Thread, der das Objekt konstruiert hat, sieht das Objekt vollständig. Dies ist ungefähr, wenn diese Änderungen sichtbar in anderen Threads sind, die nicht durch die Sprachdefinition garantiert wird.

Sie könnten versucht sein, zu sagen, dass Sie das Konstruktor von X fertig wissen, und Sie haben ein gültiges Objekt, aber das ist falsch, wenn Sie X zu einem anderen Thread übergeben - der andere Thread kann sehen, die nicht initialisierten Version (also das, was Sie sagten nur, ist nicht anders als die Garantien, eine Fabrik zu nennen).

Das ist richtig. Es ist auch richtig, dass wenn Sie ein unsynchronisiertes Objekt haben und Sie es in einem Thread mutieren, andere Threads die Mutation sehen oder nicht. Das ist die Natur der Gewindeprogrammierung. Selbst Konstrukteure sind nicht sicher, Objekte ordnungsgemäß synchronisieren zu müssen.

2: nicht initialisierte Objekte können durch eine Unterklasse geleckt werden sie von den Finalizerthread Zugriff

Dieses Dokument über Finalizers im Gespräch ist und nicht richtig ein Objekt nach zugreifen zu können es wurde Müll gesammelt. Indem Sie Subclasses und Finalizer hacken, können Sie ein Objekt erzeugen, das nicht richtig aufgebaut ist, aber es ist ein großer Hack, dies zu tun. Für mich stellt dies nicht in Frage, was ein Konstruktor tut. Stattdessen zeigt es die Komplexität der modernen, ausgereiften JVM. Das Dokument zeigt auch, wie Sie Ihren Code schreiben können, um diesen Hack zu umgehen.

Welche Eigenschaften werden von Konstruktoren in Java garantiert?

der Definition nach, einen Konstruktor:

  1. Ordnet Raum für das Objekt.
  2. Setzt alle Instanzvariablen im Objekt auf ihre Standardwerte. Dies umfasst die Instanzvariablen in den Superklassen des Objekts.
  3. Weist die Parametervariablen für das Objekt zu.
  4. Verarbeitet alle expliziten oder impliziten Konstruktoraufrufe (ein Aufruf von this() oder super() im Konstruktor).
  5. Initialisiert Variablen in der Klasse.
  6. Führt den Rest des Konstruktors aus.

In Bezug auf Ihre 3 Ausgaben, # 1 und # 3 sind wiederum etwa, wenn die Initialisierung nicht endgültig und nicht-flüchtige Felder werden durch Fäden anderes als das gesehen, die das Objekt aufgebaut. Diese Sichtbarkeit ohne Synchronisierung ist nicht garantiert.

Das # 2 Problem zeigt einen Mechanismus, bei dem, wenn eine Ausnahme während der Ausführung des Konstruktors ausgelöst wird, Sie die Finalize-Methode überschreiben können, um ein falsch konstruiertes Objekt zu erhalten. Konstruktor Punkte 1-5 sind aufgetreten. Mit dem Hack können Sie einen Teil von 6 umgehen. Ich denke, es liegt im Auge des Betrachters, wenn dies die Identität des Konstruktors in Frage stellt.

+0

Was? Diese Optimierungen führen dazu, dass sich der Code so verhält, wie ich es erwartet habe, auch die ersten 2 Aufzählungszeichen sind nicht betroffen, zum Beispiel bedeutet Zahl 2, dass ich mich nicht auf Konstruktoren verlassen kann, um das zu tun, was ich unter einem 'SecurityManager' erwarte. – Dog

+1

Es gibt Unmengen von Komplexitäten in einem Multithread-Programm, die es "so verhalten, wie wir es erwartet haben". Neuordnung der Anweisungen ist einer von ihnen. Sie können 'x = 1;' 'y = 2;' haben und ein anderer Thread würde die 'y'-Zuweisung sehen, aber nicht das 'x'. Ich sehe nicht, wie dies die Definition des Konstruktors @Dog bedroht. – Gray

+0

Das 'SecurityManager' Beispiel hat nichts mit Nebenläufigkeit zu tun. Außerdem wäre es vernünftig anzunehmen, wenn Sie eine Instanz von 'x' erhalten, die Fertigstellung des Konstruktors' happes-before', die den Verweis auf 'x' veröffentlicht, aber dies ist in Java nicht der Fall. Das Problem ist, ich weiß nicht, was die "Definition eines Konstruktors" (Zweck und Eigenschaften eines Konstruktors) ist, und deshalb stelle ich diese Frage. – Dog

-1

Konstruktoren ist Java werden nur verwendet, um den Zustand des Objekts zu erstellen erstellt .. nichts mehr.

+0

und so sind Fabriken. – Dog

+0

Factories erstellen eine Reihe von Objekten, initialisieren keinen einzelnen Objektstatus. –

+0

@Dog - Fabriken verwenden immer noch Konstruktoren. – jtahlborn

1

Vom JLS section 12.5:

12,5.Schaffung eines neuen Klasseninstanzen

Kurz bevor ein Verweis auf das neu erstellte Objekt als Ergebnis zurückgegeben wird, wird der angegebene Konstruktor verarbeitet, um die neue Objekt mit dem folgenden Verfahren zu initialisieren:

Ordnen Sie die Argumente für den Konstruktor für neu erstellte Parametervariablen für diesen Konstruktoraufruf.

Wenn dieser Konstruktor mit einem expliziten Konstruktor Aufruf beginnt (§8.8.7.1) eines anderen Konstruktor in der gleichen Klasse (mit diesem), dann bewerten die Argumente und Prozess, der Konstruktor Aufruf rekursiv dieselben fünf Schritten. Wenn dieser Aufruf des Aufrufers abrupt abgeschlossen wird, wird dieser Vorgang aus dem gleichen Grund abrupt beendet: ; Fahren Sie andernfalls mit Schritt 5 fort.

Dieser Konstruktor beginnt nicht mit einem expliziten Konstruktoraufruf eines anderen Konstruktors in derselben Klasse (mit diesem). Wenn dieser Konstruktor für eine andere Klasse als Object ist, dann beginnt dieser -Konstruktor mit einem expliziten oder impliziten Aufruf eines Superklassenkonstruktors (mit super). Werten Sie die Argumente aus und verarbeiten Sie den Superklassenkonstruktoraufruf rekursiv unter Verwendung dieser gleichen fünf Schritte. Wenn dieser Aufruf des Konstruktors abrupt abgeschlossen wird, wird dieser Vorgang aus dem gleichen Grund abrupt beendet. Andernfalls fahren mit Schritt 4.

Ausführen der Instanz Initialisierungen und Instanzvariable Initialisierungen für diese Klasse sind die Werte von Instanzvariablen Initialisierungen zu den entsprechenden Instanzvariablen der Zuordnung in der links-rechts-Reihenfolge, in der sie erscheinen im Quelltext für die Klasse. Wenn die Ausführung eines dieser Initialisierer zu einer Ausnahme von führt, werden keine weiteren Initialisierer verarbeitet, und diese Prozedur wird abrupt mit der gleichen Ausnahme beendet. Andernfalls mit Schritt 5 fortfahren.

Führen Sie den Rest des Rumpfes dieses Konstruktors. Wenn diese Ausführung abrupt abgeschlossen wird, wird diese Prozedur aus dem gleichen Grund abrupt beendet. Andernfalls wird diese Prozedur normal abgeschlossen.

**

Im Gegensatz zu C++, die Java-Programmiersprache ist nicht festgelegt veränderte Regeln für die Methode> Versand bei der Erstellung einer neuen Instanz der Klasse. Wenn Methoden aufgerufen werden, die in Unterklassen des initialisierten Objekts überschrieben werden, dann werden diese überschreibenden Methoden verwendet, noch bevor das neue Objekt vollständig initialisiert wird.

Und von JLS 16.9:

Beachten Sie, dass es keine Regeln gibt, die uns, dass V zu dem Schluss zulassen würde, ist definitiv nicht zugewiesen, bevor eine Instanz Variableninitialisierer. Wir können informell zu dem Schluss kommen, dass V vor keinem Instanzvariable-Initialisierer von C definitiv nicht zugewiesen ist, aber eine solche Regel muss nicht explizit angegeben werden.

Happens before 17.4.5:

Threading 17.5.2:

ein Lesen von einem letzten Feld von einem Objekt innerhalb des Thread, der Konstrukte, die in Bezug auf die Initialisierung des Feld innerhalb der geordneten Aufgabe wird Konstruktor nach den üblichen "happes-before" -Regeln. Wenn der Wert gelesen wird, nachdem das Feld im Konstruktor festgelegt wurde, wird der Wert als das letzte Feld angezeigt, andernfalls wird der Standardwert angezeigt.

0

A-Klasse enthält Konstrukteuren, die aufgerufen werden Objekte aus der Klasse Plan zu erstellen.

This ist das, was Oracle über Konstrukteure sagt.

Jetzt zu Ihrem Punkt. Ein Konstruktor in Java ist die Sache, die ein Objekt erzeugt, und nichts kann dieses Objekt berühren, bis der Konstruktor zurückkehrt.

Also laut der offiziellen Dokumentation ist Ihre Annahme nicht richtig. Und der Punkt 1 und 2 ist der Missbrauch der Regeln und Verhaltensweisen von Java, es sei denn, Sie möchten bewusst Ihre Objekte lecken! Da es auch für Constructor irrelevant ist, werde ich diese Punkte nicht diskutieren.

Nun, wenn wir über Ihre 3rd Punkt, in multi-threaded Umgebung reden gibt es nichts, was Sie über die Konsistenz des Codes garantieren kann, es sei denn, „richtig synchronisierten Blöcke“ oder „die Atom Anweisungen“ . Da die Objekterstellung weder eine synchronized noch eine atomic Anweisung ist, gibt es keine Garantie für die Konsistenz! Es gibt nichts, was die Constructor damit machen kann. Mit anderen Worten ist es nicht die Verantwortung der Constructor, Ihre Objekterstellung atomic zu machen.

Jetzt die Antwort auf Ihre Frage, "Welche anderen Eigenschaften garantieren Konstruktoren tatsächlich?" ist etwas einfach. Constructors sind nichts anderes als eine spezielle Art von Methoden, die bei der Objekterstellung aus dem Blue Print der Klasse aufgerufen werden. Es kann also nichts garantieren, es sei denn, Sie geben ihm die Chance, konsequent ausgeführt zu werden wie jede andere Methode. Nach einer konsistenten Ausführung kann es Ihnen garantieren, dass Ihr Objekt nach Belieben erstellt und initialisiert wird.

Verwandte Themen