JavaRush /Java-Blog /Random-DE /10 Dinge, die Sie über Java nicht wussten
minuteman
Level 32

10 Dinge, die Sie über Java nicht wussten

Veröffentlicht in der Gruppe Random-DE
Haben Sie kürzlich begonnen, mit Java zu arbeiten? Erinnern Sie sich an die Tage, als es „Oak“ hieß, als Objektorientierung noch ein heißes Thema war, als C++-Leute dachten, Java hätte keine Chance, und als noch niemand von Applets gehört hatte? Ich gehe davon aus, dass Sie nicht einmal die Hälfte der folgenden Dinge wissen. Beginnen wir die Woche mit einigen coolen Überraschungen über das Innenleben von Java. 10 Dinge, die Sie über Java nicht wussten – 11. Es gibt keine geprüfte Ausnahme. Alles ist richtig! Die JVM hat davon keine Ahnung, nur die Java-Sprache. Heute sind sich alle einig, dass geprüfte Ausnahmen ein Fehler waren. Wie Bruce Eckel in seinem letzten Vortrag auf der GeeCON in Prag sagte, gibt es keine andere Sprache, da Java geprüfte Ausnahmen verwendet, selbst Java 8 deckt sie nicht mehr in der neuen Streams-API ab (was ein wenig lästig sein kann, wenn Ihre Lambdas IO oder JDBC verwenden). ). Möchten Sie einen Beweis dafür, dass die JVM so etwas nicht weiß? Probieren Sie den folgenden Code aus: Dies führt nicht nur zu einer Kompilierung, sondern löst auch eine SQLException aus. Sie müssen dafür nicht einmal Lomboks @SneakyThrows verwenden. 2. Sie können überladene Methoden haben, die sich nur in ihren Rückgabetypen unterscheidenpublic class Test { // No throws clause here public static void main(String[] args) { doThrow(new SQLException()); } static void doThrow(Exception e) { Test. doThrow0(e); } @SuppressWarnings("unchecked") static void doThrow0(Exception e) throws E { throw (E) e; } } Das lässt sich nicht kompilieren, oder? class Test { Object x() { return "abc"; } String x() { return "123"; } } Rechts. Die Java-Sprache lässt nicht zu, dass zwei Methoden gleichzeitig innerhalb derselben Klasse gleichwertig überschrieben werden, unabhängig von ihren Unterschieden in den Würfen oder Rückgabetypen. Aber warte mal. Überprüfen Sie die Dokumentation erneut auf Class.getMethod(String, Class…). Es heißt: Beachten Sie, dass es möglich ist, dass es mehr als eine entsprechende Methode in einer Klasse gibt, denn während die Java-Sprache mehrere Methoden mit derselben Signatur, aber unterschiedlichen Rückgabetypen verbietet, ist dies bei der Java Virtual Machine nicht der Fall. Diese Flexibilität in einer virtuellen Maschine kann zur Implementierung verschiedener Sprachfunktionen genutzt werden. Beispielsweise können kovariante Rückgaben mit Brückenmethoden durchgeführt werden; Die Bridge-Methode und die überschriebene Methode hätten dieselbe Signatur, aber unterschiedliche Rückgabetypen. Wow, das macht Sinn. Es passiert tatsächlich ziemlich viel, wenn Sie Folgendes schreiben: Schauen Sie sich den generierten Bytecode an: T ist also tatsächlich ein Objekt im Bytecode. Das ist gut verstanden. Die synthetische Bridge-Methode wird tatsächlich vom Compiler generiert, da an bestimmten Stellen der Aufrufe der Rückgabetyp von Parent.x() erwartet werden kann. Das Hinzufügen von Generika ohne solche Brückenmethoden ist in der Binärdarstellung nicht mehr möglich. Das Ändern der JVM, um eine solche Funktion zu ermöglichen, verursachte also weniger Schmerzen (was als Nebeneffekt auch das Überschreiben kovarianter Methoden ermöglicht ...). Klug, oder? 3. Bei allen folgenden handelt es sich um zweidimensionale Arrays. Das ist tatsächlich wahr. Auch wenn Ihr mentaler Analysator den Rückgabetyp der oben beschriebenen Methoden nicht sofort verstehen kann, sind sie alle gleich! Wie der nächste Code. Findest du das verrückt? Auch die Vielzahl an Schreibmöglichkeiten sprengt einfach die Fantasie! Geben Sie eine Anmerkung ein. Ein Gerät, dessen Geheimnis nur von seiner Leistung übertroffen wird. Oder anders ausgedrückt: Wenn ich meinen letzten Commit kurz vor meinem 4-wöchigen Urlaub mache. Ich erteile Ihnen die Erlaubnis, es auf jede von Ihnen gewünschte Weise zu verwenden. 4. Sie erhalten keine Bedingung. Sie dachten also, Sie wüssten bereits alles über Bedingungen, als Sie anfingen, sie zu verwenden? Lassen Sie mich Sie enttäuschen – Sie haben sich geirrt. Die meisten von Ihnen werden denken, dass die folgenden beiden Beispiele gleichwertig sind: Äquivalent zu diesem? Nein. Machen wir einen kurzen Test. Das Programm gibt Folgendes aus: Ja! Der bedingte Operator führt bei Bedarf Typumwandlungen durch. Weil Sie sonst erwarten würden, dass das Programm eine NullPointerException auslöst? 5. Sie erhalten auch keinen zusammengesetzten Zuweisungsoperator. Reicht Einfallsreichtum? Schauen wir uns die folgenden zwei Codeausschnitte an: abstract class Parent { abstract T x(); } class Child extends Parent { @Override String x() { return "abc"; } } // Method descriptor #15 ()Ljava/lang/String; // Stack: 1, Locals: 1 java.lang.String x(); 0 ldc [16] 2 areturn Line numbers: [pc: 0, line: 7] Local variable table: [pc: 0, pc: 3] local: this index: 0 type: Child // Method descriptor #18 ()Ljava/lang/Object; // Stack: 1, Locals: 1 bridge synthetic java.lang.Object x(); 0 aload_0 [this] 1 invokevirtual Child.x() : java.lang.String [19] 4 areturn Line numbers: [pc: 0, line: 1] class Test { int[][] a() { return new int[0][]; } int[] b() [] { return new int[0][]; } int c() [][] { return new int[0][]; } } class Test { int[][] a = {{}}; int[] b[] = {{}}; int c[][] = {{}}; } @Target(ElementType.TYPE_USE) @interface Crazy {} class Test { @Crazy int[][] a1 = {{}}; int @Crazy [][] a2 = {{}}; int[] @Crazy [] a3 = {{}}; @Crazy int[] b1[] = {{}}; int @Crazy [] b2[] = {{}}; int[] b3 @Crazy [] = {{}}; @Crazy int c1[][] = {{}}; int c2 @Crazy [][] = {{}}; int c3[] @Crazy [] = {{}}; } 10 Dinge, die Sie über Java nicht wussten – 2 Object o1 = true ? new Integer(1) : new Double(2.0); Object o2; if (true) o2 = new Integer(1); else o2 = new Double(2.0); System.out.println(o1); System.out.println(o2); 1.0 1 Integer i = new Integer(1); if (i.equals(1)) i = null; Double d = new Double(2.0); Object o = true ? i : d; // NullPointerException! System.out.println(o); i += j; i = i + j; Intuitiv sollten sie gleich sein, oder? Aber wissen Sie was – sie sind unterschiedlich. In der JLS-Spezifikation heißt es: Ein zusammengesetzter Ausdruck vom Typ E1 op = E2 entspricht E1 = (T) ((E1) op (E2)), wobei T der Typ von E1 ist, mit der Ausnahme, dass E1 nur einmal ausgewertet wird. Ein gutes Beispiel ist die Verwendung von *= oder /= : byte b = 10; b *= 5.7; System.out.println(b); // prints 57 oder: byte b = 100; b /= 2.5; System.out.println(b); // prints 40 oder: char ch = '0'; ch *= 1.1; System.out.println(ch); // prints '4' oder: char ch = 'A'; ch *= 1.5; System.out.println(ch); // prints 'a' Ist das also immer noch ein nützliches Werkzeug? 6. Zufällige ganze Zahlen Nun zu einer schwierigeren Aufgabe. Lesen Sie nicht die Lösung. Versuchen Sie, die Antwort selbst zu finden. Wenn ich das folgende Programm ausführe: for (int i = 0; i < 10; i++) { System.out.println((Integer) i); } Manchmal erhalte ich die folgende Ausgabe: 92 221 45 48 236 183 39 193 33 84 Aber wie ist das überhaupt möglich? Ok, die Antwort liegt darin, den Integer-Cache des JDK per Reflektion zu überschreiben und dann Auto-Boxing und Auto-Unboxing zu verwenden. Tun Sie dies nicht ohne Erlaubnis eines Erwachsenen! Oder mit anderen Worten: 10 Dinge, die Sie über Java nicht wussten – 3 7. GOTO Einer meiner Favoriten. Java hat GOTO! Schreiben Sie Folgendes: int goto = 1; und Sie erhalten Folgendes: Das liegt daran, dass goto ein unbenutztes reserviertes Wort ist, nur für den Fall ... Aber das ist nicht der aufregende Teil. Das Coole ist, dass Sie goto gepaart mit break, continue und markierten Blöcken einbinden können: Vorwärtsspringen im Bytecode: Rückwärtsspringen im Bytecode: 8. Java hat Typ-Aliase. In anderen Sprachen (wie Ceylon) können wir Typen definieren Aliase ganz einfach: Die People-Klasse ist hier so aufgebaut, dass sie mit einer Set ausgetauscht werden kann Test.java:44: error: expected int goto = 1; ^ label: { // do stuff if (check) break label; // do more stuff } 2 iload_1 [check] 3 ifeq 6 // Jumping forward 6 .. label: do { // do stuff if (check) continue label; // do more stuff break label; } while(true); 2 iload_1 [check] 3 ifeq 9 6 goto 2 // Jumping backward 9 .. interface People => Set ; : In Java können wir nicht einfach einen Alias ​​auf der obersten Ebene definieren. Aber wir können dies für die Bedürfnisse einer Klasse oder Methode tun. Nehmen wir an, dass wir mit Namen wie Integer, Long usw. nicht zufrieden sind. und wir wollen kürzere Namen: I und L. Einfach: Im obigen Beispiel wird Integer für die Sichtbarkeit der Testklasse in I konvertiert, während Long für die Anforderungen der x()-Methode in L konvertiert wird. Jetzt können wir diese Methode so nennen: Natürlich sollte diese Technik nicht ernst genommen werden. In diesem Fall sind Integer und Long Endtypen, was bedeutet, dass I und L effiziente Konvertierungen sind (die Konvertierung erfolgt fast nur in eine Richtung). Wenn wir uns für die Verwendung nicht-finaler Typen (z. B. Object) entscheiden würden, könnten wir mit regulären Generika auskommen. Wir haben ein bisschen gespielt und das hat gereicht. Kommen wir zu etwas wirklich Interessantem. 9. Manche Typbeziehungen sind unentscheidbar! Okay, jetzt wird es richtig interessant, also schnappen Sie sich eine Tasse konzentrierten Kaffee und schauen wir uns die folgenden zwei Sorten an: People? p1 = null; Set ? p2 = p1; People? p3 = p2; class Test { void x(I i, L l) { System.out.println( i.intValue() + ", " + l.longValue() ); } } new Test().x(1, 2L);// A helper type. You could also just use List interface Type {} class C implements Type > {} class D

implements Type >>> {} Was bedeuten C und D überhaupt? In gewissem Sinne sind sie rekursiv, ähnlich der Rekursion in java.lang.Enum. Bedenken Sie: Angesichts der oben genannten Spezifikationen ist die eigentliche Implementierung von enum nur syntaktischer Zucker: In diesem Sinne kehren wir zu unseren beiden Typen zurück. Wird der folgende Code kompiliert? Eine schwierige Frage... und sie wird nicht wirklich gelöst? Ist C ein Subtyp von Type ? Versuchen Sie, dies in Ihrer Eclipse oder Idea zusammenzustellen, und sie werden Ihnen sagen, was sie denken. Spülen Sie es in den Abfluss... Einige Typbeziehungen in Java sind unentscheidbar! 10. Typüberschneidung Java verfügt über eine sehr interessante Funktion namens Typüberschneidung. Sie können einen (generischen) Typ deklarieren, der eigentlich die Schnittmenge zweier Typen ist. Beispiel: Der benutzerdefinierte Typparameter T, den Sie Instanzen der Testklasse zuordnen, muss sowohl die Serializable- als auch die Cloneable-Schnittstelle enthalten. Beispielsweise kann String nicht eingeschränkt werden, Date jedoch: Diese Funktion hat in Java8 mehrere Verwendungszwecke, wo Sie Typen umwandeln können. Wie hilft das? Fast nichts, aber wenn Sie Ihren Lambda-Ausdruck in den Typ umwandeln möchten, den Sie benötigen, gibt es keine andere Möglichkeit. Nehmen wir an, Sie haben eine so verrückte Einschränkung in Ihrer Methode: Sie möchten Runnable, das gleichzeitig serialisierbar ist, nur dann, wenn Sie es an einem anderen Ort ausführen und das Ergebnis über das Netzwerk senden möchten. Lambda und Serialisierung sorgen für etwas Ironie. Sie können Ihren Lambda-Ausdruck serialisieren, wenn sein Zieltyp und seine Argumente serialisierbar sind. Aber selbst wenn dies zutrifft, aktivieren sie die Serializable-Schnittstelle nicht automatisch. Sie müssen sie selbst zu diesem Typ bringen. Aber wenn Sie nur in „Serializable:“ umwandeln, ist das Lambda nicht mehr ausführbar, also wandeln Sie es in beide Typen um: Und zum Schluss: public abstract class Enum > { ... } // This enum MyEnum {} // Is really just sugar for this class MyEnum extends Enum { ... } class Test { Type c = new C(); Type> d = new D (); } Step 0) C Step 1) Type > >? Step 0) D > Step 1) Type >>> > Step 2) D >> Step 3) List >> > Step 4) D > >> Step . . . (expand forever) class Test { } // Doesn't compile Test s = null; // Compiles Test d = null; void execute(T t) {} execute((Serializable) (() -> {}));execute((Runnable & Serializable) (() -> {}));

Java ist ebenso mächtig wie mysteriös.

Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION