Verherrlichte Klassen in der Java-Sprache

96

Einige Klassen in der Standard-Java-API werden geringfügig anders behandelt als andere Klassen. Ich spreche von Klassen, die ohne spezielle Unterstützung durch den Compiler und / oder die JVM nicht implementiert werden könnten.

Diejenigen, die mir sofort einfallen, sind:

  • Object (offensichtlich) wie es unter anderem keine Superklasse hat.
  • String da die Sprache spezielle Unterstützung für den Operator + hat.
  • Thread da es diese magische start () -Methode hat, obwohl es keinen Bytecode-Befehl gibt, der die Ausführung "gabelt".

Ich nehme an, dass alle Klassen wie diese auf die eine oder andere Weise in der JLS erwähnt werden. Korrigiere mich, wenn ich falsch liege.

Wie auch immer, welche anderen Klassen gibt es? Gibt es eine vollständige Liste der "verherrlichten Klassen" in der Java-Sprache?

aioobe
quelle
2
Generika passen fast, aber nicht ganz. Sie werden mit einem Compiler-Trick implementiert, aber nicht auf eine Klasse beschränkt.
Bill the Lizard
2
Sie sind alle Ehrfurcht-Typen. ;-)
Starblue
1
Ist "thread.start" magisch? Sicherlich ist es nur ein nativer Code, der dazu aufgerufen wird?
Jcoder
Dieser Gedanke hat mich auch beeindruckt. Möglicherweise reicht die JNI allein aus, um die Thread-Klasse zu implementieren. Ich nehme an, wenn ich dies versuchen würde, würde ich eine Thread-API auf Betriebssystemebene verwenden, die Ausführung bei der Implementierung der start () -Methode verzweigen, die run () -Methode im gegabelten Thread ausführen und zurückkehren. Aber dann würde mein Betriebssystem-Thread weiterlaufen. Würde das zusammen mit den von der JVM erstellten Threads reibungslos funktionieren? und das JLS-Speichermodell ehren und so weiter?
Aioobe
2
Gemäß der Spezifikation kann ein Thread nur über die Thread-Klasse ( java.sun.com/docs/books/jvms/second_edition/html/… ) erstellt werden. Natürlich hängt es von einer naiven Kommunikation mit der JVM ab, aber wenn Sie Ihre eigene JNI erstellt haben, könnten Sie einen Thread starten, aber Sie würden die JVM nicht dazu bringen, zu verstehen, was Sie tun (Sperren, Speichermodell usw.). ). Thread ist insofern privilegiert, als die Spezifikation ihm ein spezielles Privileg zuweist - die einzige Möglichkeit, einen Thread zu starten.
Yishai

Antworten:

35

Es gibt viele verschiedene Antworten, daher dachte ich, es wäre nützlich, alle zu sammeln (und einige hinzuzufügen):

Klassen

  • AutoBoxing- Klassen - Der Compiler lässt nur bestimmte Klassen zu
  • Klasse - hat eigene Literale (z. B. int.class). Ich würde auch seine generische Typisierung hinzufügen, ohne neue Instanzen zu erstellen.
  • String - mit überladenem + -Operator und der Unterstützung von Literalen
  • Enum - die einzige Klasse, die in einer switch-Anweisung verwendet werden kann (bald auch String. Es macht auch andere Dinge (automatische statische Methodenerstellung, Serialisierungsbehandlung usw.), aber diese könnten theoretisch mit Code erreicht werden - es ist nur eine Menge Boilerplate, und einige der Einschränkungen konnten in Unterklassen nicht durchgesetzt werden (z. B. die spezielle Unterklassenregeln), aber was Sie ohne den privilegierten Status einer Aufzählung niemals erreichen könnten, ist, sie in eine switch-Anweisung aufzunehmen.
  • Objekt - die Wurzel aller Objekte (und ich würde seine Klon- und Finalisierungsmethoden hinzufügen, die Sie nicht implementieren könnten)
  • Referenzen : WeakReference, SoftReference, PhantomReference
  • Thread - Die Sprache gibt Ihnen keine spezifische Anweisung zum Starten eines Threads, sondern wendet sie auf magische Weise auf die start () -Methode an.
  • Throwable - die Wurzel aller Klassen, die mit throw, throw und catch arbeiten können, sowie das Compiler-Verständnis von Exception vs. RuntimeException und Error.
  • NullPointerException und andere Ausnahmen wie ArrayIndexOutOfBounds, die von anderen Bytecode-Anweisungen als athrow ausgelöst werden können.

Schnittstellen

  • Iterable - die einzige Schnittstelle, die in einer erweiterten for-Schleife verwendet werden kann

Lobende Erwähnungen gehen an:

  • java.lang.reflect. Array - Das Erstellen eines neuen Arrays, wie es von einem Class-Objekt definiert wird, ist nicht möglich.
  • Anmerkungen Sie sind eine spezielle Sprachfunktion, die sich zur Laufzeit wie eine Schnittstelle verhält. Sie konnten sicherlich keine andere Annotation-Oberfläche definieren, genauso wie Sie keinen Ersatz für Object definieren können. Sie könnten jedoch alle ihre Funktionen implementieren und nur eine andere Möglichkeit haben, sie (und eine ganze Reihe von Boilerplates) abzurufen, anstatt sie zu reflektieren. Tatsächlich gab es viele XML-basierte und Javadoc-Tag-basierte Implementierungen, bevor Anmerkungen eingeführt wurden.
  • ClassLoader - Es hat sicherlich eine privilegierte Beziehung zur JVM, da es keine sprachliche Methode zum Laden einer Klasse gibt, obwohl es eine Bytecode-Methode gibt. Auf diese Weise ähnelt es Array. Es hat auch das besondere Privileg, von der JVM zurückgerufen zu werden, obwohl dies ein Implementierungsdetail ist.
  • Serialisierbar - Sie können die Funktionalität über Reflection implementieren, sie verfügt jedoch über ein eigenes privilegiertes Schlüsselwort, und Sie würden in einigen Szenarien viel Zeit damit verbringen, sich mit dem SecurityManager vertraut zu machen.

Hinweis: Ich habe Dinge aus der Liste gestrichen, die JNI bereitstellen (z. B. E / A), da Sie jederzeit Ihren eigenen JNI-Aufruf implementieren könnten, wenn Sie dazu geneigt wären. Native Aufrufe, die auf privilegierte Weise mit der JVM interagieren, sind jedoch unterschiedlich.

Arrays sind umstritten - sie erben Object, haben eine verstandene Hierarchie (Object [] ist ein Supertyp von String []), aber sie sind ein Sprachmerkmal, keine definierte Klasse für sich.

Yishai
quelle
@Donal, sehr wahr, ich dachte an Laufzeit-Annotation, aber Annotationen auf Quellenebene wurden tatsächlich auf diese Weise gemacht (Xdoclet am bemerkenswertesten oder sogar in Core Java mit @deprecated)
Yishai
Ich habe mich gerade durch ein Dickicht von Code gekämpft, das ursprünglich mit xdoclet-Verarbeitung geschrieben und dann in Anmerkungen konvertiert wurde. Oh, wie ich Annotationen dem vorziehen würde (obwohl bei geeigneten Maven-Beschwörungsformeln der Nettoeffekt der gleiche ist).
Donal Fellows
@ KK_07k11A0585, Sammlungen sind eine Standard-API, die von jedem auf andere Weise erstellt werden kann (tatsächlich gibt es alternative Implementierungen, die sich an Grundelementen orientieren, sowie ein sehr berühmtes Google-Projekt, das sie verbessert). Das einzige, was sie bekommen, was besonders ist, ist Iterable, was in der Antwort erwähnt wird. Sie sind zwar das A und O der Java-Programmierung, aber es handelt sich nur um reguläre Klassen ohne besondere Privilegien.
Yishai
@Yishai Bei der Entwicklung einer Anwendung ist das DATENMANAGEMENT von größter Bedeutung. Wir alle können eine Aufgabe auf unterschiedliche Weise erledigen, aber der optimierte Weg ist derjenige, der weniger Speicher belegt und weniger unnötige Referenzen verwendet. Mithilfe von Sammlungen können wir große Datenmengen sortieren, suchen und vergleichen. Dies bietet vordefinierte Algorithmen wie binäre Suche, Zusammenführungssortierung usw. Außerdem bietet es uns Vergleichs- und vergleichbare Schnittstellen, um unsere Techniken anzupassen. Die Sammlungsklasse ist vielleicht nicht die beste Klasse in Java, aber zweifellos eine Klasse von verherrlichten Eigenschaften.
KK_07k11A0585
1
Was ist mit SecurityManager? Oder irgendetwas im Zusammenhang mit Reflexion oder Serialisierung?
Antimon
19

Class, Na sicher. Es hat seine eigenen Literale (eine Unterscheidung, mit der es teilt String, übrigens) und ist der Ausgangspunkt all dieser Reflexionsmagie.

Michael Borgwardt
quelle
Ah, guter Punkt. Was ist ein Beispiel für ein Klassenliteral? Beziehen Sie sich auf MyClass.class?
Aioobe
4
@aioobe: genau. Beachten Sie, dass Sie auch int.class, char.class usw. haben
Michael Borgwardt
13

sun.misc.unsafe ist die Mutter aller schmutzigen Hacks, die den Geist der Sprache brechen.

Dean J.
quelle
12
  1. Aufzählung. Sie dürfen es nicht unterordnen, aber der Compiler kann es.
  2. Viele Dinge unter java.util.concurrent können ohne JVM-Unterstützung implementiert werden, wären aber viel weniger effizient.
Darron
quelle
1
Sie können Aufzählungen anonym unterordnen (dies ist die Basis des Besuchermusters, das für Aufzählungswerte impliziert wird). Aber ja, Sie können eine Aufzählung nicht vollständig unterordnen ;-)
Thierry
11

Alle Number-Klassen haben ein bisschen Magie in Form von Autoboxing .

Bill die Eidechse
quelle
Sie meinen die Klassen, die Grundelemente umschließen - einschließlich Boolean und Character; schließt jedoch BigInteger aus.
Emory
1
@emory: Richtig, nur die primitiven Wrapper-Klassen. Leider schließt dies BigInteger aus.
Bill the Lizard
10

Da die wichtigen Klassen erwähnt wurden, werde ich einige Schnittstellen erwähnen:

Die IterableSchnittstelle (seit 1.5) - ermöglicht es einem Objekt, an einer foreach-Schleife teilzunehmen:

Iterable<Foo> iterable = ...;
for (Foo foo : iterable) {

}

Die SerializableSchnittstelle hat eine ganz besondere Bedeutung, die sich von einer Standardschnittstelle unterscheidet. Sie können Methoden definieren, die berücksichtigt werden, obwohl sie nicht in der Schnittstelle definiert sind (wie readResolve()). Das transientSchlüsselwort ist das Sprachelement, das das Verhalten von SerializableImplementierern beeinflusst.

Bozho
quelle
Es stimmt jedoch, dass es sich um Schnittstellen handelt, sodass keine "spezielle" Implementierung erforderlich ist. (Ähnlich wie Throwable.)
Aioobe
Serializable erfordert tatsächlich eine spezielle Implementierung. Es ist eine Schnittstelle, von der erwartet wird, dass sie zwei Methoden hat (ich vergesse die Namen ... ich könnte sie googeln ...), aber sie sind nicht erforderlich oder im Standardschnittstellenschema definiert.
CorsiKa
@glowcoder: Man könnte jedoch einmal argumentieren, dass die ganze Besonderheit von Serializable nicht für die Java-Sprache relevant ist, sondern nur für die Implementierung von ObjectOutputStream und ObjectInputStream.
Michael Borgwardt
5
@ Michael Borgwardt hat es - das transientSchlüsselwort
Bozho
1
Ich glaube nicht - Comparablenimmt an nichts Internem teil. Sie könnten leicht schreiben NewComparableund neu NewArrays.sort(..)mit der gleichen Funktionalität
Bozho
6
  1. Throwable , RuntimeException, Error AssertionError
  2. Referenzen WeakReference, SoftReference, PhantomReference
  3. Aufzählung
  4. Anmerkung
Emory
quelle
Schöne Liste. +1, Annotation ist jedoch eine Schnittstelle und hat keine Implementierung.
Aioobe
1
Anmerkungen werden vom Compiler anders behandelt als normale Schnittstellen. Ähnlich wie bei enum erweitern sie alle Annotation, und der Compiler erledigt dies automatisch. Aus dem JavaDoc of Annotation: Die gemeinsame Schnittstelle, die um alle Annotationstypen erweitert wurde. Beachten Sie, dass eine Schnittstelle, die diese manuell erweitert, keinen Anmerkungstyp definiert. Beachten Sie auch, dass diese Schnittstelle selbst keinen Anmerkungstyp definiert. Sie können also keine Annotation mit normaler Syntax erstellen. Sie mussten den Compiler ändern, um sie hinzuzufügen.
Andrei Fierbinteanu
6

Java-Array wie in int[].class

Alexander Pogrebnyak
quelle
Ah, gut! die [IKlasse;) es ist jedoch keine API-Klasse.
Aioobe
2

Ich bin mir nicht sicher. Ich kann mir jedoch keine Möglichkeit vorstellen, E / A-Objekte manuell zu implementieren.

demotics2002
quelle
Sie haben Recht, sie können nicht in reinem Java implementiert werden. Sie müssen über JNI implementiert werden (genau wie jede andere Klasse, die Systemaufrufe ausführen muss). Abgesehen davon benötigen sie jedoch keine spezielle Unterstützung durch Compiler oder JVM.
Aioobe
2

In der SystemKlasse steckt etwas Magie .

System.arraycopy ist ein Hook in nativen Code

public static native void arraycopy(Object array1, int start1, 
  Object array2, int start2, int length);

aber...

/**
 * Private version of the arraycopy method used by the jit
 * for reference arraycopies
 */
private static void arraycopy(Object[] A1, int offset1,
  Object[] A2, int offset2, int length) {
   ...
}
Eljenso
quelle
1
Hey, was meinst du mit deinem zweiten Teil der Antwort?
Pacerier
1

Nun, da der spezielle Umgang mit Assert erwähnt wurde. Hier sind einige weitere Ausnahmetypen, die vom JVM speziell behandelt werden:

  • NullPointerException
  • ArithmeticException.
  • StackOverflowException
  • Alle Arten von OutOfMemoryErrors
  • ...

Die Ausnahmen sind nicht speziell, aber die JVM verwendet sie in besonderen Fällen, sodass Sie sie nicht selbst implementieren können, ohne Ihre eigene JVM zu schreiben. Ich bin mir sicher, dass es weitere besondere Ausnahmen gibt.

josefx
quelle
0

Die meisten dieser Klassen werden nicht wirklich mit 'spezieller' Hilfe des Compilers oder der JVM implementiert. Object registriert einige Eingeborene, die sich in den internen JVM-Strukturen umsehen, aber Sie können dies auch für Ihre eigenen Klassen tun. (Ich gebe zu, dass dies der Semantik unterliegt. "Aufrufe eines in der JVM definierten Native" kann als spezielle JVM-Unterstützung angesehen werden.)

Was / ist / speziell ist das Verhalten der Anweisungen 'new' und 'throw', wie sie diese internen Strukturen initialisieren.

Anmerkungen und Zahlen sind allerdings ziemlich ausgeflippt.

Millimoose
quelle
4
aioobe bezieht sich auf Klassen, die Sie nicht selbst implementieren können, da sie eine spezielle integrierte Unterstützung haben. Sie können keine eigene Objektklasse als Stamm des Typsystems erstellen, ohne die Form java.lang.Object zu erben. Object wird sowohl vom Compiler als auch vom JVM speziell unterstützt, um sicherzustellen, dass es sich um eine Root-Klasse handelt. Native Anrufe reichen nicht aus, um dies zu umgehen.
Josefx