JavaRush /Java Blog /Random-ID /10 hal yang tidak Anda ketahui tentang Java
minuteman
Level 32

10 hal yang tidak Anda ketahui tentang Java

Dipublikasikan di grup Random-ID
Jadi, apakah Anda baru saja mulai bekerja dengan Java? Ingat hari-hari ketika disebut "Oak", ketika orientasi objek masih menjadi topik hangat, ketika orang-orang C++ berpikir Java tidak punya peluang, dan ketika belum ada yang pernah mendengar tentang applet? Saya dapat berasumsi bahwa Anda bahkan tidak mengetahui setengah dari hal-hal berikut. Mari kita mulai minggu ini dengan beberapa kejutan keren tentang cara kerja Java. 10 hal yang tidak Anda ketahui tentang Java - 11. Tidak ada pengecualian yang dicentang. Itu benar! JVM tidak tahu tentang hal seperti itu, hanya bahasa Java yang tahu. Saat ini semua orang setuju bahwa pengecualian yang dicentang adalah sebuah kesalahan. Seperti yang dikatakan Bruce Eckel dalam pembicaraan terakhirnya di GeeCON di Praha, tidak ada bahasa lain karena Java menggunakan pengecualian yang dicentang, bahkan Java 8 tidak lagi mencakupnya di Streams API baru (yang bisa menjadi sedikit gangguan ketika lambda Anda menggunakan IO atau JDBC ). Apakah Anda ingin bukti bahwa JVM tidak mengetahui hal seperti itu? Coba kode berikut: Ini tidak hanya akan dikompilasi, tetapi juga akan memunculkan SQLException, Anda bahkan tidak perlu menggunakan @SneakyThrows Lombok untuk ini. 2. Anda dapat memiliki metode kelebihan beban yang hanya berbeda pada tipe pengembaliannyapublic 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; } } Ini tidak dapat dikompilasi, bukan? class Test { Object x() { return "abc"; } String x() { return "123"; } } Benar. Bahasa Java tidak mengizinkan dua metode untuk ditimpa secara setara dalam kelas yang sama pada waktu yang sama, terlepas dari perbedaan tipe lemparan atau pengembaliannya. Tapi tunggu sebentar. Periksa kembali dokumentasi untuk Class.getMethod(String, Class…). Dikatakan: Perhatikan bahwa ada kemungkinan bahwa ada lebih dari satu metode yang sesuai dalam satu kelas, karena meskipun bahasa Java melarang beberapa metode dengan tanda tangan yang sama tetapi tipe pengembalian yang berbeda, Java Virtual Machine tidak melarangnya. Fleksibilitas dalam mesin virtual ini dapat digunakan untuk mengimplementasikan berbagai fitur bahasa. Misalnya, pengembalian kovarian dapat dilakukan dengan metode jembatan; Metode jembatan dan metode yang diganti akan memiliki tanda tangan yang sama tetapi tipe pengembaliannya berbeda. Wah, itu masuk akal. Sebenarnya ada cukup banyak hal yang terjadi ketika Anda menulis yang berikut ini: Lihatlah bytecode yang dihasilkan: Jadi t sebenarnya adalah sebuah objek dalam bytecode. Hal ini dipahami dengan baik. Metode jembatan sintetis sebenarnya dihasilkan oleh kompiler karena tipe kembalian Parent.x() dapat diharapkan pada bagian panggilan tertentu. Menambahkan obat generik tanpa metode jembatan seperti itu tidak lagi dapat dilakukan dalam representasi biner. Jadi, mengubah JVM untuk mengizinkan fitur seperti itu menghasilkan lebih sedikit rasa sakit (yang juga memungkinkan penggantian metode kovarian sebagai efek samping...) Cerdas bukan? 3. Berikut ini adalah array dua dimensi. Ini sebenarnya benar. Bahkan jika penganalisis mental Anda tidak dapat segera memahami tipe pengembalian dari metode yang dijelaskan di atas, semuanya tetap sama! Seperti potongan kode berikutnya. Apakah menurut Anda ini gila? Banyaknya kesempatan untuk menulis juga sungguh meledakkan imajinasi! Ketik anotasi. Perangkat yang misterinya nomor dua setelah kekuatannya. Atau dengan kata lain: Ketika saya membuat komitmen terakhir saya sebelum liburan 4 minggu saya. Saya memberi Anda izin untuk menggunakannya sesuka Anda. 4. Anda tidak akan mendapatkan kondisional Jadi, Anda pikir Anda sudah mengetahui segalanya tentang kondisional saat Anda mulai menggunakannya? Biarkan saya mengecewakan Anda - Anda salah. Sebagian besar dari Anda akan berpikir bahwa dua contoh berikut ini setara: setara dengan ini? TIDAK. Mari kita gunakan tes cepat. Program ini akan menampilkan keluaran berikut: Ya! Operator kondisional akan melakukan cast tipe jika diperlukan. Karena jika tidak, Anda akan mengharapkan program tersebut mengeluarkan NullPointerException? 5. Anda juga tidak akan mendapatkan operator penugasan gabungan. Apakah kecerdikan cukup? Mari kita lihat dua cuplikan kode berikut: 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 hal yang tidak Anda ketahui tentang 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; Secara intuitif, keduanya seharusnya setara, bukan? Tapi tahukah Anda - mereka berbeda. Spesifikasi JLS mengatakan: Ekspresi gabungan tipe E1 op = E2 setara dengan E1 = (T) ((E1) op (E2)), di mana T adalah tipe E1, kecuali E1 dievaluasi hanya sekali. Contoh yang baik adalah dengan menggunakan *= or /= : byte b = 10; b *= 5.7; System.out.println(b); // prints 57 or: byte b = 100; b /= 2.5; System.out.println(b); // prints 40 or: 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' Jadi, apakah ini masih merupakan alat yang berguna? 6. Bilangan bulat acak Sekarang untuk tugas yang lebih sulit. Jangan membaca solusinya. Lihat apakah Anda dapat menemukan jawabannya sendiri. Ketika saya menjalankan program berikut: for (int i = 0; i < 10; i++) { System.out.println((Integer) i); } Kadang-kadang saya mendapatkan output berikut: 92 221 45 48 236 183 39 193 33 84 Tapi bagaimana ini mungkin? Oke, jawabannya terletak pada mengganti cache Integer JDK melalui refleksi, lalu menggunakan auto-boxing dan auto-unboxing. Jangan lakukan ini tanpa izin orang dewasa! Atau dengan kata lain: 10 hal yang tidak Anda ketahui tentang Java - 3 7. GOTO Salah satu favoritku. Java memiliki GOTO! Tulis ini: int goto = 1; dan Anda mendapatkan ini: Itu karena goto adalah kata khusus yang tidak digunakan, untuk berjaga-jaga... Tapi itu bukan bagian yang menarik. Yang keren adalah Anda dapat memasukkan goto berpasangan dengan break, continue dan blok yang ditandai: Melompat maju Dalam kode byte: Melompat mundur Dalam kode byte: 8. Java memiliki alias tipe Dalam bahasa lain (seperti Ceylon), kita dapat mendefinisikan tipe alias sangat mudah: Kelas People di sini dibuat sedemikian rupa sehingga dapat dipertukarkan dengan 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 ; : Di Java, kita tidak bisa begitu saja mendefinisikan alias di tingkat atas. Tapi kita bisa melakukan ini untuk kebutuhan kelas atau metode. Anggaplah kita tidak senang dengan nama seperti Integer, Long, dll. dan kita menginginkan nama yang lebih pendek: I dan L. Mudah: Pada contoh di atas, Integer diubah menjadi I untuk visibilitas kelas Test, sedangkan Long diubah menjadi L untuk kebutuhan metode x(). Sekarang kita bisa menyebut metode ini seperti ini: Tentu saja teknik ini tidak boleh dianggap serius. Dalam hal ini, Integer dan Long adalah tipe final, yang berarti I dan L adalah konversi yang efisien (hampir konversi hanya berjalan satu arah). Jika kita memutuskan untuk menggunakan tipe non-final (misalnya Object), maka kita dapat menggunakan obat generik biasa. Kami bermain sedikit dan itu sudah cukup. Mari beralih ke sesuatu yang sangat menarik. 9. Beberapa tipe hubungan tidak dapat diputuskan! Oke, sekarang ini akan menjadi sangat menarik, jadi ambillah secangkir kopi pekat dan mari kita lihat dua jenis berikut ini: 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 >>> {} Jadi apa maksudnya C dan D? Dalam arti tertentu, mereka bersifat rekursif, mirip dengan rekursi di java.lang.Enum. Pertimbangkan: Mengingat spesifikasi di atas, implementasi sebenarnya dari enum hanyalah gula sintaksis: Dengan mengingat hal tersebut, mari kembali ke dua tipe kita. Apakah kode berikut akan dikompilasi? Sebuah pertanyaan yang sulit... dan sebenarnya belum terselesaikan? Apakah C merupakan subtipe dari Type ? Coba kompilasi ini di Eclipse atau Idea Anda dan mereka akan memberi tahu Anda apa yang mereka pikirkan. Buang sia-sia... Beberapa tipe hubungan di Java tidak dapat diputuskan! 10. Tipe Persimpangan Java memiliki fitur yang sangat menarik yang disebut tipe persimpangan. Anda dapat mendeklarasikan tipe (generik) yang sebenarnya merupakan perpotongan dua tipe. Misalnya: Parameter tipe kustom T yang Anda kaitkan dengan instance kelas Test harus menyertakan antarmuka Serializable dan Cloneable. Misalnya, String tidak bisa dibatasi, tapi Tanggal bisa: Fitur ini memiliki banyak kegunaan di Java8, di mana Anda bisa mentransmisikan tipe. Bagaimana hal ini membantu? Hampir tidak ada, tetapi jika Anda ingin memasukkan ekspresi lambda ke tipe yang Anda butuhkan, maka tidak ada cara lain. Katakanlah Anda memiliki batasan yang sangat gila dalam metode Anda: Anda ingin Runnable yang pada saat yang sama dapat diserialkan hanya jika Anda ingin menjalankannya di tempat lain dan mengirimkan hasilnya melalui jaringan. Lambda dan serialisasi menambah sedikit ironi. Anda dapat membuat serial ekspresi lambda Anda jika tipe target dan argumennya dapat diserialkan. Namun meskipun ini benar, mereka tidak secara otomatis mengaktifkan antarmuka Serializable. Anda harus membawanya sendiri ke tipe ini. Tetapi ketika Anda hanya melakukan transmisi ke Serializable: maka lambda tidak lagi menjadi Runnable, jadi transmisikan keduanya ke kedua jenis: Dan sebagai kesimpulan: 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 sama kuatnya dengan misteriusnya.

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