JavaRush /Blog Java /Random-FR /10 choses que vous ne saviez pas sur Java
minuteman
Niveau 32

10 choses que vous ne saviez pas sur Java

Publié dans le groupe Random-FR
Alors, avez-vous récemment commencé à travailler avec Java ? Vous souvenez-vous de l'époque où on l'appelait "Oak", où l'orientation objet était encore un sujet brûlant, où les gens de C++ pensaient que Java n'avait aucune chance et où personne n'avait même entendu parler des applets ? Je peux supposer que vous ne savez même pas la moitié des choses suivantes. Commençons la semaine avec quelques surprises intéressantes sur le fonctionnement interne de Java. 10 choses que vous ne saviez pas sur Java - 11. Il n’existe pas d’exception vérifiée. C'est exact! La JVM n'a aucune idée d'une telle chose, seul le langage Java l'a. Aujourd’hui, tout le monde s’accorde à dire que les exceptions vérifiées étaient une erreur. Comme Bruce Eckel l'a dit lors de sa dernière conférence à la GeeCON à Prague, aucun autre langage depuis Java n'utilise les exceptions vérifiées, même Java 8 ne les couvre plus dans la nouvelle API Streams (ce qui peut être un peu gênant lorsque vos lambdas utilisent IO ou JDBC). ). Voulez-vous la preuve que la JVM ne sait pas une telle chose ? Essayez le code suivant : non seulement cette compilation sera effectuée, mais elle lancera également une exception SQLException, vous n'avez même pas besoin d'utiliser @SneakyThrows de Lombok pour cela. 2. Vous pouvez avoir des méthodes surchargées qui ne diffèrent que par leurs types de retourpublic 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; } } Cela ne compilera pas, n'est-ce pas ? class Test { Object x() { return "abc"; } String x() { return "123"; } } Droite. Le langage Java ne permet pas de substituer deux méthodes de manière équivalente au sein de la même classe en même temps, quelles que soient leurs différences en termes de lancements ou de types de retour. Mais attendez une minute. Consultez à nouveau la documentation pour Class.getMethod(String, Class…). Il dit : Notez qu'il est possible qu'il y ait plus d'une méthode correspondante dans une classe, car si le langage Java interdit plusieurs méthodes avec la même signature mais des types de retour différents, ce n'est pas le cas de la machine virtuelle Java. Cette flexibilité dans une machine virtuelle peut être utilisée pour implémenter diverses fonctionnalités de langage. Par exemple, les retours covariants peuvent être effectués avec des méthodes de pont ; La méthode pont et la méthode remplacée auraient la même signature mais des types de retour différents. Wow, c'est logique. Il se passe en fait beaucoup de choses lorsque vous écrivez ce qui suit : Regardez le bytecode généré : Il s'agit donc en fait d'un objet dans le bytecode. Cela est bien compris. La méthode du pont synthétique est en fait générée par le compilateur car le type de retour de Parent.x() peut être attendu à certaines parties des appels. L'ajout de génériques sans de telles méthodes de pont ne sera plus possible en représentation binaire. Ainsi, changer la JVM pour autoriser une telle fonctionnalité a produit moins de douleur (ce qui permet également le remplacement de la méthode covariante comme effet secondaire...) Intelligent, n'est-ce pas ? 3. Tous les éléments suivants sont des tableaux bidimensionnels. C'est en fait vrai. Même si votre analyseur mental ne peut pas comprendre immédiatement le type de retour des méthodes décrites ci-dessus, elles sont toutes identiques ! Comme le prochain morceau de code. Pensez-vous que c'est fou ? Le nombre d’occasions d’écrire fait également exploser l’imagination ! Tapez une annotation. Un appareil dont le mystère n’a d’égal que sa puissance. Ou en d’autres termes : Quand je fais mon dernier engagement juste avant mes 4 semaines de vacances. Je vous donne la permission de l'utiliser comme vous le souhaitez. 4. Vous n'obtiendrez pas de conditionnel Alors, vous pensiez déjà tout savoir sur les conditions lorsque vous avez commencé à les utiliser ? Laissez-moi vous décevoir, vous aviez tort. La plupart d’entre vous penseront que les deux exemples suivants sont équivalents : équivalent à ceci ? Non. Utilisons un test rapide. Le programme affichera ce qui suit : Oui ! L'opérateur conditionnel effectuera des conversions de type si nécessaire. Parce que sinon, vous vous attendriez à ce que le programme lève une NullPointerException ? 5. Vous n'obtiendrez pas non plus d'opérateur d'affectation composé. La débrouillardise est-elle suffisante ? Examinons les deux extraits de code suivants : 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 choses que vous ne saviez pas sur Java - 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; Intuitivement, ils devraient être égaux, n’est-ce pas ? Mais vous savez quoi, ils sont différents. La spécification JLS dit : Une expression composée de type E1 op = E2 est équivalente à E1 = (T) ((E1) op (E2)), où T est le type de E1, sauf que E1 n'est évalué qu'une seule fois. Un bon exemple est d'utiliser *= ou /= : byte b = 10; b *= 5.7; System.out.println(b); // prints 57 or: or byte b = 100; b /= 2.5; System.out.println(b); // prints 40 : char ch = '0'; ch *= 1.1; System.out.println(ch); // prints '4' or: char ch = 'A'; ch *= 1.5; System.out.println(ch); // prints 'a' Alors, est-ce toujours un outil utile ? 6. Entiers aléatoires Passons maintenant à une tâche plus difficile. Ne lisez pas la solution. Voyez si vous pouvez trouver la réponse vous-même. Lorsque j'exécute le programme suivant : for (int i = 0; i < 10; i++) { System.out.println((Integer) i); } Parfois, j'obtiens le résultat suivant : 92 221 45 48 236 183 39 193 33 84 Mais comment est-ce possible ? Ok, la réponse réside dans le remplacement du cache Integer du JDK via la réflexion, puis dans l'utilisation du boxing et du déballage automatiques. Ne faites pas cela sans la permission d'un adulte ! Ou en d'autres termes : 10 choses que vous ne saviez pas sur Java - 3 7. GOTO Un de mes favoris. Java a GOTO ! Écrivez ceci : int goto = 1; et vous obtenez ceci : C'est parce que goto est un mot réservé inutilisé, juste au cas où... Mais ce n'est pas la partie la plus excitante. Ce qui est cool, c'est que vous pouvez inclure goto associé à des blocs break, continue et marqués : Sauter en avant Dans le byte code : Sauter en arrière Dans le byte code : 8. Java a des alias de type Dans d'autres langages (comme Ceylan), nous pouvons définir le type les alias sont très simples : la classe People ici est construite de telle manière qu'elle peut être interchangée avec un Set 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 ; : En Java, on ne peut pas simplement définir un alias au niveau supérieur. Mais nous pouvons le faire pour les besoins d’une classe ou d’une méthode. Supposons que nous ne soyons pas satisfaits des noms comme Integer, Long, etc. et nous voulons des noms plus courts : I et L. Facile : Dans l'exemple ci-dessus, Integer est converti en I pour la visibilité de la classe Test, tandis que Long est converti en L pour les besoins de la méthode x(). Nous pouvons maintenant appeler cette méthode ainsi : Bien entendu, cette technique ne doit pas être prise au sérieux. Dans ce cas, Integer et Long sont des types finaux, ce qui signifie que I et L sont des conversions efficaces (presque, la conversion ne se fait que dans un sens). Si nous décidions d'utiliser des types non finaux (par exemple Object), nous pourrions alors nous contenter de génériques classiques. Nous avons joué un peu et c'était suffisant. Passons à quelque chose de vraiment intéressant. 9. Certaines relations de type sont indécidables ! D'accord, maintenant cela va devenir vraiment intéressant, alors prenez une tasse de café concentré et regardons les deux types suivants : 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 >>> {} Alors, que signifient C et D ? Dans un sens, ils sont récursifs, similaires à la récursion de java.lang.Enum. Considérez : étant donné les spécifications ci-dessus, l'implémentation réelle d'enum n'est qu'un sucre syntaxique : dans cet esprit, revenons à nos deux types. Le code suivant sera-t-il compilé ? Une question difficile... et qui n'est pas réellement résolue ? C est-il un sous-type de Type ? Essayez de compiler ceci dans votre Eclipse ou Idea et ils vous diront ce qu'ils en pensent. Jetez-le à la poubelle... Certaines relations de types en Java sont indécidables ! 10. Intersection de types Java possède une fonctionnalité très intéressante appelée intersection de types. Vous pouvez déclarer un type (générique) qui est en fait l'intersection de deux types. Par exemple : le paramètre de type personnalisé T que vous associez aux instances de la classe Test doit inclure à la fois les interfaces Serialisable et Cloneable. Par exemple, String ne peut pas être restreint, mais Date peut : Cette fonctionnalité a plusieurs utilisations dans Java8, où vous pouvez convertir des types. Comment cela aide-t-il ? Presque rien, mais si vous souhaitez convertir votre expression lambda dans le type dont vous avez besoin, il n'y a pas d'autre moyen. Disons que vous avez une telle limitation folle dans votre méthode : vous voulez Runnable qui en même temps est sérialisable uniquement si vous souhaitez l'exécuter à un autre endroit et envoyer le résultat sur le réseau. Lambda et la sérialisation ajoutent un peu d'ironie. Vous pouvez sérialiser votre expression lambda si son type cible et ses arguments sont sérialisables. Mais même si cela est vrai, ils n’activent pas automatiquement l’interface sérialisable. Vous devez les amener vous-même à ce type. Mais lorsque vous lancez uniquement un cast vers Serialisable : alors le lambda ne sera plus Runnable, alors lancez-les vers les deux types : Et en conclusion : 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 est aussi puissant que mystérieux.

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