Dinge, die Sie [vielleicht] nicht über Java wussten

Grüße, Leser!


Dieser Artikel wird meinen Bewusstseinsstrom über Produktivität verwässern. Lassen Sie uns über lustige Dinge in Java und in Ihrer Umgebung sprechen, von denen Sie wahrscheinlich nichts wussten. Ich selbst habe kürzlich einige der oben genannten Dinge gelernt, daher glaube ich, dass die meisten Leser zumindest ein paar interessante Momente für sich selbst finden werden.


assert kann 2 Argumente annehmen


Normalerweise wird assert verwendet, um eine Bedingung zu überprüfen, und löst einen AssertionError wenn die Bedingung nicht erfüllt ist. Am häufigsten sieht der Scheck so aus:


 assert list.isEmpty(); 

Es kann jedoch so sein:


 assert list.isEmpty() : list.toString(); 

Der intelligente Leser hat bereits vermutet, dass der zweite Ausdruck (übrigens faul) einen Wert vom Typ Object zurückgibt, der an AssertionError und dem Benutzer zusätzliche Informationen zu dem Fehler liefert. Eine formellere Beschreibung finden Sie im entsprechenden Abschnitt der Sprachspezifikation: https://docs.oracle.com/javase/specs/jls/se13/html/jls-14.html#jls-14.10


Seit fast 6 ½ Jahren arbeite ich mit Java und habe die erweiterte Verwendung des assert Schlüsselworts nur einmal gesehen.


strictfp


Dies ist kein Schimpfwort - dies ist ein wenig bekanntes Schlüsselwort. Gemäß der Dokumentation beinhaltet seine Verwendung eine strikte Arithmetik für Gleitkommazahlen:


 public interface NonStrict { float sum(float a, float b); } 

kann in verwandelt werden


 public strictfp interface Strict { float sum(float a, float b); } 

Dieses Schlüsselwort kann auch auf einzelne Methoden angewendet werden:


 public interface Mixed { float sum(float a, float b); strictfp float strictSum(float a, float b); } 

Lesen Sie mehr über seine Verwendung im Wiki-Artikel . Kurzum: Sobald dieses Schlüsselwort hinzugefügt wurde, um die Portabilität zu gewährleisten, wurde as Die Genauigkeit der Verarbeitung von Gleitkommazahlen auf verschiedenen Prozessoren kann unterschiedlich sein.


Fortsetzen kann ein Argument nehmen


Ich habe es letzte Woche erfahren. Normalerweise schreiben wir so:


 for (Item item : items) { if (item == null) { continue; } use(item); } 

Eine solche Verwendung setzt implizit eine Rückkehr zum Beginn des Zyklus und zum nächsten Durchlauf voraus. Mit anderen Worten kann der obige Code wie folgt umgeschrieben werden:


 loop: for (Item item : items) { if (item == null) { continue loop; } use(item); } 

Sie können jedoch gegebenenfalls vom Zyklus zum externen Zyklus zurückkehren:


 @Test void test() { outer: for (int i = 0; i < 20; i++) { for (int j = 10; j < 15; j++) { if (j == 13) { continue outer; } } } } 

Beachten Sie, dass der Zähler i bei der Rückkehr zum outer Punkt nicht zurückgesetzt wird, sodass die Schleife endlich ist.


Wenn Sie die vararg-Methode ohne Argumente aufrufen, wird trotzdem ein leeres Array erstellt


Wenn wir uns den Aufruf einer solchen Methode von außen ansehen, gibt es anscheinend nichts zu befürchten:


 @Benchmark public Object invokeVararg() { return vararg(); } 

Wir haben nichts in die Methode eingegeben, oder? Aber wenn Sie von innen schauen, ist nicht alles so rosig:


 public Object[] vararg(Object... args) { return args; } 

Erfahrung bestätigt Bedenken:


 Benchmark Mode Cnt Score Error Units invokeVararg avgt 20 3,715 ± 0,092 ns/op invokeVararg:·gc.alloc.rate.norm avgt 20 16,000 ± 0,001 B/op invokeVararg:·gc.count avgt 20 257,000 counts 

Sie können ein unnötiges Array ohne Argumente entfernen, indem Sie null :


 @Benchmark public Object invokeVarargWithNull() { return vararg(null); } 

Der Müllsammler fühlt sich wirklich besser:


 invokeVarargWithNull avgt 20 2,415 ± 0,067 ns/op invokeVarargWithNull:·gc.alloc.rate.norm avgt 20 ≈ 10⁻⁵ B/op invokeVarargWithNull:·gc.count avgt 20 ≈ 0 counts 

Code mit null sieht sehr hässlich aus, der Compiler (und die Idee) werden schwören, also verwenden Sie diesen Ansatz in wirklich heißem Code und versehen Sie ihn mit Kommentaren.


Der Switch-Case-Ausdruck unterstützt java.lang.Class nicht


Dieser Code kann einfach nicht kompiliert werden:


 String to(Class<?> clazz) { switch (clazz) { case String.class: return "str"; case Integer.class: return "int"; default: return "obj"; } } 

Beschäftige dich damit.


Feinheiten der Zuweisung und Class.isAssignableFrom ()


Es gibt einen Code:


 int a = 0; Integer b = 10; a = b; //    

Überlegen Sie nun, welchen Wert diese Methode zurückgibt:


 boolean check(Integer b) { return int.class.isAssignableFrom(b.getClass()); } 

Nach dem Lesen des Namens der Class.isAssignableFrom() -Methode entsteht der falsche Eindruck, dass der Ausdruck int.class.isAssignableFrom(b.getClass()) true int.class.isAssignableFrom(b.getClass()) . Wir können einer Variablen vom Typ int eine Variable vom Typ Integer zuweisen, oder?


Die check() -Methode gibt jedoch false , da in der Dokumentation eindeutig Folgendes angegeben ist:


 /** * Determines if the class or interface represented by this * {@code Class} object is either the same as, or is a superclass or * superinterface of, the class or interface represented by the specified * {@code Class} parameter. It returns {@code true} if so; * otherwise it returns {@code false}. If this {@code Class} // <---- !!! * object represents a primitive type, this method returns * {@code true} if the specified {@code Class} parameter is * exactly this {@code Class} object; otherwise it returns * {@code false}. * */ @HotSpotIntrinsicCandidate public native boolean isAssignableFrom(Class<?> cls); 

Obwohl int nicht der Nachfolger von Integer (und umgekehrt), ist eine mögliche gegenseitige Zuordnung ein Merkmal der Sprache, und um Benutzer nicht irrezuführen, wird in der Dokumentation ein besonderer Vorbehalt gemacht.


Moral: wenn es scheint - müssen getauft werden müssen die Dokumentation erneut lesen.


Eine weitere nicht offensichtliche Tatsache ergibt sich aus diesem Beispiel:


 assert int.class != Integer.class; 

Die Klasse int.class ist eigentlich Integer.TYPE , und um dies zu sehen, schauen Sie sich an, in was dieser Code kompiliert wird:


 Class<?> toClass() { return int.class; } 

Whack:


 toClass()Ljava/lang/Class; L0 LINENUMBER 11 L0 GETSTATIC java/lang/Integer.TYPE : Ljava/lang/Class; ARETURN 

Wenn Sie den Quellcode für java.lang.Integer sehen Sie java.lang.Integer :


 @SuppressWarnings("unchecked") public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int"); 

Wenn Sie den Aufruf von Class.getPrimitiveClass("int") könnten Class.getPrimitiveClass("int") versucht sein, ihn auszuschneiden und durch Class.getPrimitiveClass("int") ersetzen:


 @SuppressWarnings("unchecked") public static final Class<Integer> TYPE = int.class; 

Das Erstaunlichste ist, dass das JDK mit ähnlichen Änderungen (für alle Grundelemente) zusammengesetzt wird und die virtuelle Maschine gestartet wird. Sie wird zwar nicht lange arbeiten:


 java.lang.IllegalArgumentException: Component type is null at jdk.internal.misc.Unsafe.allocateUninitializedArray(java.base/Unsafe.java:1379) at java.lang.StringConcatHelper.newArray(java.base/StringConcatHelper.java:458) at java.lang.StringConcatHelper.simpleConcat(java.base/StringConcatHelper.java:423) at java.lang.String.concat(java.base/String.java:1968) at jdk.internal.util.SystemProps.fillI18nProps(java.base/SystemProps.java:165) at jdk.internal.util.SystemProps.initProperties(java.base/SystemProps.java:103) at java.lang.System.initPhase1(java.base/System.java:2002) 

Der Fehler wird hier angezeigt:


 class java.lang.StringConcatHelper { @ForceInline static byte[] newArray(long indexCoder) { byte coder = (byte)(indexCoder >> 32); int index = (int)indexCoder; return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, index << coder); //<-- } } 

Mit den genannten Änderungen gibt byte.class null zurück und byte.class die Sicherheit.


Spring Data JPA kündigt ein teilweise funktionsfähiges Repository an


Ich schließe den Artikel mit einem merkwürdigen Fehler, der an der Kreuzung von Spring Date und Hibernate aufgetreten ist. Denken Sie daran, wie wir ein Repository deklarieren, das einer bestimmten Entität dient:


 @Entity public class SimpleEntity { @Id private Integer id; @Column private String name; } public interface SimpleRepository extends JpaRepository<SimpleEntity, Integer> { } 

Erfahrene Benutzer wissen, dass Spring Date beim Auslösen des Kontexts alle Repositorys überprüft und sofort die gesamte Anwendung zerstört, wenn beispielsweise eine Abfragekurve beschrieben werden soll:


 public interface SimpleRepository extends JpaRepository<SimpleEntity, Integer> { @Query(", ,  ?") Optional<SimpleEntity> findLesserOfTwoEvils(); } 

Nichts hindert uns jedoch daran, ein Repository mit einem linken Schlüsseltyp zu deklarieren:


 public interface SimpleRepository extends JpaRepository<SimpleEntity, Long> { } 

Dieses Repository wird nicht nur erweitert, sondern auch teilweise in Betrieb sein. Beispielsweise findAll() die findAll() -Methode "mit einem Paukenschlag". Es wird jedoch erwartet, dass Methoden, die den Schlüssel verwenden, mit einem Fehler fehlschlagen:


 IllegalArgumentException: Provided id of the wrong type for class SimpleEntity. Expected: class java.lang.Integer, got class java.lang.Long 

Die Sache ist, dass Spring Date die Klassen des Entitätsschlüssels und den Schlüssel des daran angehängten Repositorys nicht vergleicht . Dies geschieht nicht aus einem guten Leben heraus, sondern aufgrund der Unfähigkeit von Hibernate, in bestimmten Fällen den richtigen Schlüsseltyp auszugeben: https://hibernate.atlassian.net/browse/HHH-10690


In meinem Leben habe ich dies nur einmal org.springframework.data.jpa.repository.query.PartTreeJpaQueryIntegrationTests$UserRepository : In Tests (Trolley) von Spring Dates selbst, zum Beispiel org.springframework.data.jpa.repository.query.PartTreeJpaQueryIntegrationTests$UserRepository in Long eingegeben und Integer in der Entität User . Und es funktioniert!


Das ist alles, ich hoffe meine Rezension war nützlich und interessant für Sie.


Ich gratuliere Ihnen zum neuen Jahr und wünsche Ihnen, tiefer und weiter zu graben!

Source: https://habr.com/ru/post/de482364/


All Articles