Pendahuluan: Java 8 telah dirilis selama beberapa waktu, dan semua tanda menunjukkan bahwa Java 8 adalah perubahan besar dalam distribusi. Sudah ada banyak artikel di java code geeks yang memperkenalkan fitur baru Java 8, seperti bermain dengan java 8 lambdas dan concurrency, tutorial API java 8 date API: localDateTime dan kelas abstrak versus antarmuka di era JDK 8. Artikel ini juga mengacu pada beberapa informasi lain, seperti: 15 harus membaca tutorial Java 8 dan sisi gelap Java 8. Artikel ini telah menyusun informasi di atas dan menyusunnya menjadi buku teks referensi tentang fitur -fitur baru Java 8. Saya harap Anda akan mendapatkan sesuatu.
1. Pendahuluan
Tidak ada keraguan bahwa Java 8 adalah versi terpenting dari Java sejak Java 5 (dirilis pada tahun 2004). Versi ini berisi lebih dari selusin fitur baru dalam bahasa, kompiler, perpustakaan, alat, dan JVM. Dalam artikel ini, kami akan mempelajari fitur -fitur baru ini dan menggunakan contoh -contoh praktis untuk menggambarkan skenario apa yang cocok untuk digunakan.
Tutorial ini berisi beberapa jenis masalah yang sering dihadapi pengembang Java:
bahasa
Penyusun
Perpustakaan
alat
Runtime (JVM)
2. Fitur baru bahasa Java
Java 8 adalah versi utama Java. Beberapa orang percaya bahwa meskipun fitur -fitur baru ini diharapkan oleh pengembang Java, itu juga membutuhkan banyak upaya untuk belajar. Di bagian ini, kami akan memperkenalkan sebagian besar fitur baru Java 8.
2.1 Ekspresi Lambda dan Antarmuka Fungsional
Ekspresi Lambda (juga dikenal sebagai penutupan) adalah perubahan bahasa terbesar dan paling dinanti dalam Java 8. Ini memungkinkan kita untuk meneruskan fungsi sebagai parameter ke metode, atau untuk memproses kode itu sendiri sebagai data: pengembang fungsional sangat akrab dengan konsep -konsep ini. Banyak bahasa di platform JVM (Groovy, Scala, dll.) Telah mendukung ekspresi lambda sejak kelahiran mereka, tetapi pengembang Java tidak punya pilihan selain menggunakan kelas internal anonim alih -alih ekspresi lambda.
Desain Lambda membutuhkan banyak waktu dan banyak upaya masyarakat, dan akhirnya menemukan solusi implementasi kompromi yang dapat mencapai struktur bahasa yang sederhana dan kompak. Ekspresi lambda paling sederhana dapat terdiri dari daftar parameter yang dipisahkan koma, -> simbol, dan blok pernyataan, misalnya:
Arrays.aslist ("a", "b", "d") .foreach (e -> system.out.println (e));
Dalam kode di atas, jenis parameter E diturunkan oleh penalaran kompiler, dan Anda juga dapat secara eksplisit menentukan jenis parameter, misalnya:
Arrays.aslist ("a", "b", "d") .foreach ((string e) -> system.out.println (e));
Jika ekspresi lambda membutuhkan blok pernyataan yang lebih kompleks, Anda dapat melampirkan blok pernyataan dengan kawat gigi keriting, mirip dengan tubuh fungsi di java, misalnya:
Arrays.aslist ("a", "b", "d") .foreach (e -> {System.out.print (e); System.out.print (e);});Ekspresi Lambda dapat merujuk pada anggota kelas dan variabel lokal (yang secara implisit akan mengubah variabel -variabel ini menjadi final), misalnya, dua blok kode berikut memiliki efek yang persis sama:
String pemisah = ","; arrays.aslist ("a", "b", "d") .foreach ((string e) -> system.out.print (e + pemisah));Dan
final string pemisah = ","; arrays.aslist ("a", "b", "d") .foreach ((string e) -> system.out.print (e + pemisah));Ekspresi Lambda memiliki nilai pengembalian, dan jenis nilai pengembalian juga diturunkan oleh inferensi kompiler. Jika blok pernyataan dalam ekspresi Lambda hanya memiliki satu baris, Anda tidak perlu menggunakan pernyataan pengembalian. Dua cuplikan kode berikut memiliki efek yang sama:
Arrays.aslist ("a", "b", "d") .sort ((e1, e2) -> e1.comppareto (e2));Dan
Arrays.aslist ("a", "b", "d") .sort ((e1, e2) -> {int result = e1.compareto (e2); hasil pengembalian;});Untuk membuat fungsi yang ada kompatibel dengan ekspresi Lambda, perancang Lambda mempertimbangkan banyak metode, sehingga mereka muncul dengan konsep antarmuka fungsi. Antarmuka fungsi mengacu pada antarmuka dengan hanya satu fungsi, dan antarmuka seperti itu dapat secara implisit dikonversi menjadi ekspresi lambda. java.lang.runnable dan java.util.concurrent.callable adalah contoh terbaik dari antarmuka fungsional. Dalam praktiknya, antarmuka fungsional sangat rapuh: selama pengembang menambahkan fungsi ke antarmuka, antarmuka bukan lagi antarmuka fungsional, yang mengakibatkan kegagalan kompilasi. Untuk mengatasi kerentanan tingkat kode ini dan secara eksplisit menyatakan bahwa antarmuka adalah antarmuka fungsional, Java 8 memberikan anotasi khusus @FunctionalInterface (semua antarmuka terkait di perpustakaan Java sudah memiliki anotasi ini), untuk memberikan definisi sederhana dari antarmuka fungsional:
@FunctionalInterface Public Interface Functional {void Method ();}Namun, satu hal yang perlu diperhatikan adalah bahwa metode default dan metode statis tidak akan menghancurkan definisi antarmuka fungsional, sehingga kode berikut adalah legal.
@FunctionInterface Public Interface functionaldefaultMethods {void method (); default void defaultMethod () {}}Lambda Expressions, sebagai titik penjualan terbesar Java 8, memiliki potensi untuk menarik lebih banyak pengembang untuk bergabung dengan platform JVM dan menggunakan konsep pemrograman fungsional dalam pemrograman Java murni. Jika Anda perlu tahu lebih banyak tentang ekspresi Lambda, Anda dapat merujuk pada dokumentasi resmi.
2.2 Metode antarmuka default dan statis
Java 8 menggunakan dua konsep baru untuk memperluas makna antarmuka: metode default dan metode statis. Metode default membuat antarmuka sedikit mirip dengan sifat, tetapi tujuan yang akan dicapai berbeda. Metode default memungkinkan pengembang untuk menambahkan metode baru ke antarmuka yang ada tanpa melanggar kompatibilitas biner, yaitu, tidak memaksa kelas yang mengimplementasikan antarmuka untuk mengimplementasikan metode yang baru ditambahkan secara bersamaan.
Perbedaan antara metode default dan metode abstrak adalah bahwa metode abstrak perlu diimplementasikan, sedangkan metode default tidak. Metode default yang disediakan oleh antarmuka akan diwarisi atau ditimpa oleh kelas implementasi antarmuka. Kode contoh adalah sebagai berikut:
Antarmuka Antarmuka Private Defaulable {// Sekarang memungkinkan metode default, pelaksana mungkin atau // tidak dapat menerapkan (mengganti) mereka. string default notrequired () {return "implementasi default"; }} private static class defaultableImpl mengimplementasikan defaulable {} private static class overridableImpl mengimplementasikan defaulable {@Override public string notrequired () {return "overridden implementasi";}}Antarmuka yang dapat ditiadakan menggunakan kata kunci default untuk menentukan metode default notrequired (). Kelas DefaultableImpl mengimplementasikan antarmuka ini dan mewarisi metode default dalam antarmuka ini secara default; Kelas OverridableImpl juga mengimplementasikan antarmuka ini, tetapi mengesampingkan metode default antarmuka dan menyediakan implementasi yang berbeda.
Fitur menarik lainnya yang dibawa oleh Java 8 adalah bahwa metode statis dapat didefinisikan dalam antarmuka. Kode contoh adalah sebagai berikut:
Antarmuka Private Interface DefaulableFactory {// sekarang memungkinkan metode statis statis statis dapat membuat (pemasok <defaulable> pemasok) {return supplier.get ();}}Cuplikan kode berikut mengintegrasikan skenario penggunaan metode default dan metode statis:
public static void main (string [] args) {default default = defaulableFactory.create (defaultableImpl :: new); System.out.println (defaultable.notrequired (); defaulable = defaulableFactory.create (overridableMlableMl :: new); out.out.Output kode ini adalah sebagai berikut:
Implementasi default
Implementasi yang ditimpa
Karena implementasi metode default pada JVM memberikan dukungan pada level bytecode, itu sangat efisien. Metode default memungkinkan antarmuka yang ditingkatkan tanpa merusak sistem warisan yang ada. Penerapan fitur ini di perpustakaan resmi adalah: Tambahkan metode baru ke antarmuka java.util.collection, seperti stream (), paralelstream (), foreach (), dan hapus (), dll.
Meskipun metode default memiliki begitu banyak manfaat, mereka harus digunakan dengan hati -hati dalam pengembangan aktual: dalam sistem warisan yang kompleks, metode default dapat menyebabkan kesalahan ambiguitas dan kompilasi. Jika Anda ingin mengetahui detail lebih lanjut, silakan merujuk ke dokumentasi resmi.
2.3 Referensi Metode
Referensi metode memungkinkan pengembang untuk secara langsung merujuk metode yang ada, konstruktor kelas Java atau objek instan. Referensi metode dan ekspresi lambda digunakan bersama satu sama lain, membuat konstruktor kelas Java terlihat kompak dan ringkas, tanpa banyak kode template yang kompleks.
Dalam contoh Simon, kelas mobil adalah contoh referensi metode yang berbeda, yang dapat membantu pembaca membedakan antara empat jenis referensi metode.
Public Static Class Car {Public Static Car Create (Pemasok Akhir <BOT> Pemasok) {Return Supplier.get ();} public static void coolue (mobil mobil akhir) {System.out.println ("collided" + car.toString ());} public void ikuti (mobil lain lainnya) {System.out.println () () (") ikuti public ikuti (" System.out.toid () () {System.out.printl (); {System.out.println ("Diperbaiki" + this.toString ());}}Jenis referensi untuk metode pertama adalah referensi konstruktor, sintaksnya adalah kelas :: Bentuk baru, atau lebih umum: kelas <t> :: baru. Catatan: Konstruktor ini tidak memiliki parameter.
mobil terakhir mobil = car.create (mobil :: baru); Daftar Akhir <BOT> CARS = Arrays.aslist (CAR);
Jenis referensi metode kedua adalah referensi metode statis, dan sintaksnya adalah kelas :: static_method. Catatan: Metode ini menerima parameter jenis mobil.
cars.foreach (mobil :: collide);
Metode ketiga Referensi Jenis adalah referensi ke metode anggota dari kelas tertentu, dan sintaksnya adalah Metode Kelas ::. Perhatikan bahwa metode ini tidak menentukan parameter:
cars.foreach (mobil :: perbaikan);
Jenis metode keempat yang dirujuk adalah referensi ke metode anggota dari objek instan, dan sintaks adalah contoh :: metode. Catatan: Metode ini menerima parameter tipe mobil:
Polisi mobil terakhir = car.create (mobil :: baru); cars.foreach (polisi :: ikuti);
Jalankan contoh di atas dan Anda dapat melihat output berikut di konsol (instance mobil mungkin berbeda):
Collided com.javacodegeeks.java8.method.references.methodreferences$car@7a81197d diperbaiki com.javacodegeeks.java8.method.references.methodreferences$car@7a81197d Mengikuti The The References.methodreferences$car@7a81197 com.javacodegeeks.java8.method.references.methodreferences$car@7a81197d
Jika Anda ingin memahami dan mempelajari konten yang lebih rinci, Anda dapat merujuk ke dokumentasi resmi
2.4 Komentar Ulangi
Sejak diperkenalkannya anotasi di Java 5, fitur ini telah menjadi sangat populer dan telah banyak digunakan dalam berbagai kerangka kerja dan proyek. Namun, anotasi memiliki batasan besar: anotasi yang sama tidak dapat digunakan beberapa kali di tempat yang sama. Java 8 melanggar batasan ini dan memperkenalkan konsep anotasi berulang, memungkinkan anotasi yang sama digunakan beberapa kali di tempat yang sama.
Mendefinisikan anotasi berulang di Java 8 menggunakan anotasi @Repeatable sebenarnya bukan peningkatan tingkat bahasa, tetapi trik yang dibuat oleh kompiler, dan teknologi yang mendasarinya masih sama. Anda dapat menggunakan kode berikut untuk menjelaskan:
paket com.javacodegeeks.java8.repeatable.annotations; impor java.lang.annotation.elementType; impor java.lang.annotation.Repeatable; impor java.lang.annotation.Retention; impor java.lang.annotation.RetentionPolicy; impor java.lang.annotation.target; public class RepeatingAnnotations { @Target( ElementType.TYPE ) @Retention( RetentionPolicy.RUNTIME ) public @interface Filters {Filter[] value();} @Target( ElementType.TYPE ) @Retention( RetentionPolicy.RUNTIME ) @Repeatable( Filters.class ) public @interface Filter { String value();}; @Filter ("filter1") @filter ("filter2") antarmuka publik yang dapat disaring {} public static void main (string [] args) {for (filter filter: filterable.class.getAnnotationsByType (filter.class)) {System.out.println (filter.value ();}}} {System.println (Filter.value ();Seperti yang dapat kita lihat, kelas filter di sini menggunakan anotasi @RePeatable (filter.class), dan filter adalah wadah yang menyimpan anotasi filter. Kompiler mencoba memblokir detail ini dari pengembang. Dengan cara ini, antarmuka yang dapat difilter dapat dijelaskan dengan dua anotasi filter (tidak ada informasi tentang filter yang disebutkan di sini).
Selain itu, API refleksi menyediakan metode baru: getAnnotationsByType (), yang dapat mengembalikan anotasi duplikat dari jenis tertentu, misalnya
Filterable.class.getannoation (filter.class) akan mengembalikan dua instance filter, dan output konten ke konsol adalah sebagai berikut:
filter1
filter2
Jika Anda ingin tahu lebih banyak, Anda dapat merujuk ke dokumentasi resmi.
2.5 Inferensi Jenis yang Lebih Baik
Kompiler Java 8 telah membuat peningkatan besar dalam inferensi tipe. Dalam banyak skenario, kompiler dapat menyimpulkan tipe data dari parameter tertentu, membuat kode lebih ringkas. Kode contoh adalah sebagai berikut:
paket com.javacodegeeks.java8.type.inference; nilai kelas publik <T> {public static <T> t defaultValue () {return null; } public t getordefault (nilai t, t defaultValue) {return (value! = null)? Nilai: DefaultValue;}}Kode berikut adalah aplikasi nilai tipe <string>:
paket com.javacodegeeks.java8.type.inference; TypeInference kelas publik {public static void main (string [] args) {value final <string> value = new value <> (); value.getordefault ("22", value.defaultValue ());}}Jenis nilai parameter.defaultValue () diturunkan oleh kompiler dan tidak perlu ditentukan secara eksplisit. Di Java 7, kode ini akan memiliki kesalahan kompilasi kecuali nilai. <string> DefaultValue () digunakan.
2.6 Perluas skenario aplikasi anotasi
Java 8 memperluas skenario aplikasi anotasi. Sekarang, anotasi dapat digunakan pada hampir semua elemen: variabel lokal, jenis antarmuka, superclass, dan kelas implementasi antarmuka, dan bahkan pada definisi fungsi pengecualian. Berikut beberapa contoh:
paket com.javacodegeeks.java8.annotations; impor java.lang.annotation.elementType; impor java.lang.annotation.Retention; impor java.lang.annotation.RetentionPolicy; impor java.lang.annotation.target; impor java.util.arraylist; impor java.util.collection; Anotasi kelas publik {@retention (retentionpolicy.runtime) @target ({elementType.type_use, elementType.type_parameter}) public @interface nonempty {} public class pemegang publics <@nonempty t> extends @nonempty objek {public void Method () throws @non {nepty {nepty {nepty {nepty {nepty {nepty {nepty {nepty {nonnon {nepty {nepty {neplow {nepty {Nonply {method publik () throws {nepty {nepty {nepty {nept "tidak digunakan") public static void main (string [] args) {final holder <string> holder = new @nonempt holder <string> (); @Nonempty collection <@nonempty string> string = new ArrayList <> (); }}ElementType.type_user dan elementType.type_parameter adalah dua anotasi baru yang ditambahkan ke Java 8 untuk menggambarkan skenario penggunaan anotasi. Bahasa java
Yan juga membuat perubahan yang sesuai untuk mengidentifikasi catatan yang baru ditambahkan ini.
3. Fitur Baru dari Java Compiler
3.1 Nama Parameter
Untuk mendapatkan nama parameter metode dalam program Java saat runtime, generasi yang lebih tua dari pemrogram Java harus menggunakan metode yang berbeda, seperti Paranamer Liberary. Java 8 akhirnya menormalkan fitur ini, dengan dukungan pada tingkat bahasa (menggunakan metode refleksi API dan parameter.getName ()) dan level bytecode (menggunakan kompiler Javac baru dan parameter -parameter).
paket com.javacodegeeks.java8.parameter.names; impor java.lang.reflect.method; impor java.lang.reflect.parameter; ParameterNames kelas publik {public static void main (string [] args) melempar Exception {Method Method = parameterNames.class.getMethod ("main", string []. class); untuk (parameter parameter akhir: method.getParameters ()) {System.out.println ("Parameter:" + parameter.getName ());}}}Di Java 8, fitur ini dimatikan secara default, jadi jika Anda mengkompilasi kode di atas tanpa parameter -parameters dan menjalankannya, hasil berikut akan output:
Parameter: Arg0
Jika parameter -parameter digunakan, hasil berikut akan menjadi output (hasil yang benar):
Parameter: Args
Jika Anda menggunakan Maven untuk manajemen proyek, Anda dapat mengonfigurasi parameter -parameters di item konfigurasi kompiler Maven-kompiler-plugin:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <compilerArgument>-parameters</compilerArgument> <source>1.8</source> <target>1.8</target> </configuration> </plugin>
4. Fitur baru Perpustakaan Resmi Java
Java 8 telah menambahkan banyak kelas alat baru (kelas tanggal/waktu) dan kelas alat yang ada untuk mendukung pemrograman bersamaan modern, pemrograman fungsional, dll.
4.1 Opsional
Bug yang paling umum dalam aplikasi Java adalah pengecualian nilai nol. Sebelum Java 8, Google Guava memperkenalkan kelas opsional untuk menyelesaikan NullpointerException, sehingga menghindari kode sumber yang terkontaminasi oleh berbagai pemeriksaan nol, sehingga pengembang dapat menulis kode yang lebih bersih. Java 8 juga menambahkan opsional ke perpustakaan resmi.
Opsional hanyalah hal yang mudah: menyimpan nilai tipe t atau nol. Ini menyediakan beberapa antarmuka yang berguna untuk menghindari pengecekan nol eksplisit, dan Anda dapat merujuk ke dokumentasi resmi Java 8 untuk lebih jelasnya.
Selanjutnya, mari kita lihat beberapa contoh menggunakan opsional: nilai yang mungkin kosong atau nilai dari jenis tertentu:
Opsional <string> fullName = opsional.ofnullable (null); System.out.println ("Nama lengkap diatur?" + FullName.ispresent ()); System.out.println ("Nama lengkap:" + fullname.orelset (() -> "[tidak ada]")); System.out.println (fullname.map (s -> "hey" + s + "!") .Orelse ("hey stranger!"));Jika instance opsional memiliki nilai non-nol, metode isPresent () mengembalikan true, jika tidak ia mengembalikan false; metode orelset (), dan instance opsional memegang nol, dapat menerima nilai default yang dihasilkan oleh ekspresi lambda; Metode MAP () dapat mengonversi nilai instance opsional yang ada menjadi nilai baru; Metode orelse () mirip dengan metode orelset (), tetapi mengembalikan nilai default yang diteruskan saat memegang nol.
Hasil output dari kode di atas adalah sebagai berikut:
Nama lengkap diatur? False Full Name: [None] Hei Stranger!
Mari kita lihat contoh sederhana lain:
Opsional <string> firstName = opsional.of ("tom"); System.out.println ("Nama depan diatur?" + FirstName.ispresent ()); System.out.println ("Nama Depan:" + firstName.orelset (() -> "[tidak ada]")); System.out.println (firstName.map (s -> "hey" + s + "!") .Orelse ("hey stranger!")); System.out.println ();Output dari contoh ini adalah:
Nama depan diatur? Nama Depan Benar: Tom Hey Tom!
Jika Anda ingin mengetahui detail lebih lanjut, silakan merujuk ke dokumentasi resmi.
4.2 Stream
API Stream yang baru ditambahkan (java.util.stream) memperkenalkan pemrograman fungsional dari lingkungan yang dihasilkan ke perpustakaan Java. Sejauh ini ini merupakan peningkatan terbesar dari perpustakaan Java sehingga pengembang dapat menulis kode yang lebih efisien, ringkas, dan ringkas.
API Steam sangat menyederhanakan operasi pengumpulan (kita akan melihat lebih dari sekedar koleksi nanti). Pertama, mari kita lihat kelas yang disebut tugas ini:
stream kelas publik {private enum status {open, closed}; tugas kelas akhir statis privat {status status akhir pribadi; poin integer final pribadi; tugas (status status akhir, poin integer akhir) {this.status = status; this.points = points;} public integer getPoints () {poin return;} status publik getStatus () {status return;} @Override public string toString () {return string.format ("[ %s, %d]", status, point);}}}Kelas tugas memiliki konsep fraksi (atau kompleksitas semu), dan ada dua negara bagian lain: terbuka atau tertutup. Sekarang misalkan ada koleksi tugas:
Koleksi akhir <Keck> tugas = arrays.aslist (tugas baru (status.open, 5), tugas baru (status.open, 13), tugas baru (status.closed, 8));
Pertama, mari kita lihat pertanyaan: Berapa banyak poin status terbuka yang ada dalam pengumpulan tugas ini? Sebelum Java 8, untuk menyelesaikan masalah ini, Anda perlu menggunakan foreach untuk mengulangi pengumpulan tugas; Namun, di Java 8, Anda dapat menggunakan uap untuk menyelesaikannya: sertakan daftar serangkaian elemen, dan mendukung pemrosesan berurutan dan paralel.
// Hitung titik total dari semua tugas aktif menggunakan sum () final long totalpointsofopentasks = Tasks.stream (). Filter (Tugas -> Task.getStatus () == Status.open) .maptoint (Tugas :: getPoints) .sum (); System.out.println ("Total Points:" + TotalPointsofop) .sum (); System.Out.println ("Total Points:" + TotalPointsofopePopePePeP ();Output konsol untuk menjalankan metode ini adalah:
Total Poin: 18
Ada banyak poin pengetahuan yang layak dibicarakan di sini. Pertama, set tugas diubah menjadi representasi uap; kedua, operasi filter pada uap memfilter semua tugas tertutup; Ketiga, operasi Maptoint mengubah aliran tugas menjadi koleksi integer berdasarkan tugas Tugas :: GetPoints dari setiap instance tugas; Akhirnya, jumlah dihitung dengan metode SUM untuk mendapatkan hasil akhir.
Sebelum mempelajari contoh berikutnya, Anda perlu mengingat beberapa poin pengetahuan tentang uap (klik di sini untuk lebih jelasnya). Operasi di atas uap dapat dibagi menjadi operasi menengah dan operasi yang terlambat.
Operasi menengah akan mengembalikan uap baru - melakukan operasi perantara (seperti filter) tidak akan melakukan operasi penyaringan yang sebenarnya, tetapi membuat uap baru dan menempatkan elemen yang memenuhi kondisi dalam uap asli ke dalam uap yang baru dibuat.
Operasi yang terlambat (seperti foreach atau sum) akan melintasi uap dan mendapatkan hasil atau hasil yang menyertainya; Setelah melakukan operasi yang terlambat, jalur pemrosesan uap telah diproses dan tidak dapat digunakan. Dalam hampir semua kasus, operasi terlambat segera melintasi uap.
Nilai lain dari Steam adalah dukungan kreatif untuk pemrosesan paralel. Untuk koleksi tugas di atas, kita dapat menggunakan kode berikut untuk menghitung jumlah titik dari semua tugas:
// Hitung titik total dari semua tugas final ganda totalpoints = Tasks.stream (). Parallel (). Map (Tugas -> Task.getPoints ()) // atau peta (Tugas :: GetPoints) .reduce (0, Integer :: Sum); System.out.println ("Total Points (Semua Tugas):" + Total);Di sini kami menggunakan metode paralel untuk memproses semua tugas secara paralel dan menghitung hasil akhir menggunakan metode pengurangan. Output konsol adalah sebagai berikut:
Total Points (semua tugas): 26.0
Untuk koleksi, seringkali perlu untuk mengelompokkan elemen di dalamnya sesuai dengan kondisi tertentu. Jenis tugas ini dapat diselesaikan dengan cepat menggunakan API yang disediakan oleh Steam. Kodenya adalah sebagai berikut:
// grup tugas berdasarkan status peta akhir mereka <status, daftar <Keck>> peta = Tasks.stream (). Collect (collectors.groupingby (tugas :: getStatus)); System.out.println (peta);
Output konsol adalah sebagai berikut:
{Closed = [[tertutup, 8]], open = [[buka, 5], [buka, 13]]}
Contoh pertanyaan terakhir tentang pengumpulan tugas adalah: Cara menghitung proporsi poin dari setiap tugas dalam koleksi dalam koleksi. Kode pemrosesan spesifik adalah sebagai berikut:
// Hitung berat masing -masing tugas (sebagai persen dari total poin) Koleksi akhir <string> hasil = Tasks.stream () // stream <string> .maptoint (tugas :: getPoints) // intstream .aslongstream () // longstream .maptodouble (poin -> poin / totalpoints) // doubleam. )) // longstream .maptoobj (persentase -> persentase + "%") // stream <string> .collect (collectors.tolist ()); // Daftar <string> system.out.println (hasil);
Hasil output konsol adalah sebagai berikut:
[19%, 50%, 30%]
Akhirnya, seperti yang disebutkan sebelumnya, API Steam tidak hanya dapat bertindak berdasarkan koleksi Java, tetapi operasi IO tradisional (baca data dari file atau jalur jaringan demi saluran) dapat memperoleh manfaat dari pemrosesan Steam. Berikut adalah contoh kecil:
path path akhir = file baru (nama file) .topath (); coba (stream <string> lines = file.lines (path, standardcharsets.utf_8)) {line.onclose (() -> System.out.println ("Done!")) .foreach (System.out :: println);}Metode stream Onclose mengembalikan aliran yang setara dengan pegangan ekstra. Pegangan ini akan dieksekusi ketika metode stream's close () dipanggil. API Stream, Ekspresi Lambda, dan referensi metode yang didukung oleh metode default antarmuka dan metode statis adalah respons Java 8 terhadap paradigma modern pengembangan perangkat lunak.
4.3 API Tanggal/Waktu (JSR 310)
Java 8 memperkenalkan API tanggal-waktu baru (JSR 310) untuk meningkatkan pemrosesan waktu dan tanggal. Manajemen waktu dan tanggal selalu menjadi masalah paling menyakitkan bagi pengembang Java. java.util.date dan kemudian java.util.calendar belum menyelesaikan masalah ini (bahkan lebih bingung oleh pengembang).
Karena alasan di atas, perpustakaan pihak ketiga Joda-Time lahir, yang dapat menggantikan API manajemen waktu Java. API manajemen waktu dan tanggal baru di Java 8 sangat dipengaruhi oleh joda-time dan telah menyerap banyak esensi joda-time. Paket Java.Time baru berisi semua kelas tentang tanggal, waktu, zona waktu, instan (mirip dengan tanggal tetapi tepat untuk nanodetik), durasi (durasi), dan operasi jam. API yang baru dirancang secara serius mempertimbangkan invarian kelas -kelas ini (kuliah yang dipelajari dari java.util.calendar), dan mengembalikan objek baru jika sebuah instance perlu dimodifikasi.
Mari kita lihat kelas -kelas utama dan contoh penggunaan masing -masing dalam paket java.time. Pertama, kelas jam menggunakan zona waktu untuk mengembalikan waktu dan tanggal nanodetik saat ini. Jam dapat menggantikan system.currentTimeMillis () dan timezone.getDefault ().
// Dapatkan jam sistem sebagai UTC offset jam final clock = clock.systemutc (); System.out.println (clock.instant ()); System.out.println (clock.millis ());
Output dari contoh ini adalah:
2014-04-12T15: 19: 29.282Z 1397315969360
Kedua, fokus pada kelas LocalDate dan Localtime. LocalDate hanya berisi bagian tanggal dalam sistem kalender ISO-8601; LocalTime hanya berisi bagian waktu dalam sistem kalender. Objek kedua kelas dapat dibangun menggunakan objek jam.
// Dapatkan tanggal lokal dan waktu lokal tanggal localDate = localdate.now (); final localdate dateFromClock = localDate.now (clock); System.out.println (DateFromClock); // Dapatkan tanggal lokal dan waktu lokal waktu localtime = localtime.now (); final localtime timefromclock = localtime.now (clock); System.out.println (waktu); System.out.println (TimeFromClock);
Hasil output dari contoh di atas adalah sebagai berikut:
2014-04-12 2014-04-12 11: 25: 54.568 15: 25: 54.568
Kelas LocalDateTime berisi informasi tentang LocalDate dan Localtime, tetapi tidak berisi informasi zona waktu dalam sistem kalender ISO-8601. Berikut adalah beberapa contoh tentang LocalDate dan Localtime:
// Dapatkan tanggal/waktu lokal/waktu localDatetime datetime = localDateTime.now (); final localDateTime datetimefromclock = localDateTime.now (clock); System.out.println (datetime); System.out.println (DatetImefromClock);
Hasil output dari contoh di atas adalah sebagai berikut:
2014-04-12T11: 37: 52.309 2014-04-12T15: 37: 52.309
Jika Anda memerlukan informasi data/waktu untuk zona waktu tertentu, Anda dapat menggunakan ZonedAteTime, yang memiliki tanggal dan waktu sistem tanggal ISO-8601, dan memiliki informasi zona waktu. Berikut adalah beberapa contoh menggunakan zona waktu yang berbeda:
// Dapatkan zonasi/waktu zonasi zonedDatetime zonedDatetime = zonedDateTime.now (); ZonedDateTime final zonedDatetimefromclock = zonedDateTime.now (jam); zonedDateTime final zonedDatetimefromzone = zonedDateTime.now (zonaid.of ("amerika/los_angeles")); System.out.println (ZonedDateTime); System.out.println (zonedDateTimefromClock); System.out.println (zonedDateTimefromClock); System.out.println (ZonedDateTimefromzone);Output dari contoh ini adalah:
2014-04-12T11: 47: 01.017-04: 00 [Amerika/new_york] 2014-04-12T15: 47: 01.017Z 2014-04-12T08: 47: 01.017-07: 00 [Amerika/Los_Angeles]]
Akhirnya, mari kita lihat kelas durasi, yang memiliki waktu untuk detik dan nanodetik. Ini membuatnya mudah untuk menghitung perbedaan antara dua tanggal, kode contoh adalah sebagai berikut:
// Dapatkan durasi antara dua tanggal final localDateTime dari = localDateTime.of (2014, bulan. April, 16, 0, 0, 0); localDatetime final = localDatetime.of (2015, bulan. KEPALA, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, dari, yang akan ”saat ini. durasi.
Contoh ini digunakan untuk menghitung jumlah hari dan jam antara 16 April 2014 dan 16 April 2015, dan outputnya adalah sebagai berikut:
Durasi dalam Hari: 365 Durasi dalam Jam: 8783
Kesan keseluruhan dari tanggal dan waktu baru Java 8 relatif positif, sebagian karena dampak positif waktu Joda, dan sebagian karena pejabat akhirnya mendengarkan kebutuhan pengembang. Jika Anda ingin mengetahui detail lebih lanjut, Anda dapat merujuk ke dokumentasi resmi.
4.4 Mesin JavaScript Nashorn
Java 8 menyediakan mesin Nashorn JavaScript baru, memungkinkan kami untuk mengembangkan dan menjalankan aplikasi JS di JVM. Nashorn Javascript Engine adalah versi implementasi lain dari javax.script.scriptengine. Jenis mesin skrip ini mengikuti aturan yang sama dan memungkinkan Java dan JavaScript digunakan secara interaktif. Kode contoh adalah sebagai berikut:
ScriptEngineManager Manager = new nevelEngineManager (); scriptEngine engine = manager.getEngineByName ("JavaScript"); System.out.println (engine.getClass (). GetName ()); System.out.println ("Hasil:" + engine.eval ("fungsi f () {return 1;};Output kode ini adalah sebagai berikut:
jdk.nashorn.api.scripting.nashornscriptEngine Hasil: 2
4.5 Base64
Dukungan untuk pengkodean Base64 telah ditambahkan ke perpustakaan resmi Java 8, sehingga encoding Base64 dapat dilakukan tanpa menggunakan perpustakaan pihak ketiga. Kode contoh adalah sebagai berikut:
paket com.javacodegeeks.java8.base64; impor java.nio.charset.standardcharsets; impor java.util.base64; Public Class Base64s {public static void main (string [] args) {final string text = "base64 Akhirnya di java 8!"; string final encoded = base64.getEncoder (). encodeToString (text.getbytes (standardcharsets.utf_8)); System.out.println (encoded); string final decoded = string baru (base64.getDecoder (). decode (encoded), standardcharsets.utf_8); system.out.println (decoded);}}Output dari contoh ini adalah sebagai berikut:
QMFZZTY0IGZPBMFSBHKGAW4GSMF2YSA4IQ ==
Base64 Akhirnya di Java 8!
Base64api baru juga mendukung pengkodean dan decoding URL dan tambang.
(Base64.getUrlencoder () / base64.geturldecoder (), base64.getMimeEncoder () / base64.getMimedecoder ()).
4.6 Array Paralel
Versi Java8 telah menambahkan banyak metode baru untuk mendukung pemrosesan array paralel. Metode yang paling penting adalah Parallelsort (), yang secara signifikan dapat mempercepat penyortiran array pada mesin multi-core. Contoh berikut menunjukkan metode seri parallelexxxx:
paket com.javacodegeeks.java8.parallel.arrays; impor java.util.arrays; import java.util.concurrent.threadlocalrandom; Public Class ParallelArrays {public static void main (string [] args) {long [] arrayoflong = new Long [20000]; Arrays.parallelSetAll( arrayOfLong, index -> ThreadLocalRandom.current().nextInt( 1000000 ) );Arrays.stream( arrayOfLong ).limit( 10 ).forEach( i -> System.out.print( i + " " ) );System.out.println();Arrays.parallelSort( arrayOfLong );Arrays.stream( arrayOfLong ).limit( 10 ).forEach( i -> System.out.print( i + " " ) );System.out.println();}}上述这些代码使用parallelSetAll()方法生成20000个随机数,然后使用parallelSort()方法进行排序。这个程序会输出乱序数组和排序数组的前10个元素。上述例子的代码输出的结果是:
Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378 Sorted: 39 220 263 268 325 607 655 678 723 793
4.7 并发性
基于新增的lambda表达式和steam特性,为Java 8中为java.util.concurrent.ConcurrentHashMap类添加了新的方法来支持聚焦操作;另外,也为java.util.concurrentForkJoinPool类添加了新的方法来支持通用线程池操作(更多内容可以参考我们的并发编程课程)。
Java 8还添加了新的java.util.concurrent.locks.StampedLock类,用于支持基于容量的锁――该锁有三个模型用于支持读写操作(可以把这个锁当做是java.util.concurrent.locks.ReadWriteLock的替代者)。
在java.util.concurrent.atomic包中也新增了不少工具类,列举如下:
DoubleAccumulator
DoubleAdder
LongAccumulator
LongAdder
5. 新的Java工具
Java 8提供了一些新的命令行工具,这部分会讲解一些对开发者最有用的工具。
5.1 Nashorn引擎:jjs
jjs是一个基于标准Nashorn引擎的命令行工具,可以接受js源码并执行。例如,我们写一个func.js文件,内容如下:
function f() { return 1; }; print( f() + 1 );可以在命令行中执行这个命令:jjs func.js,控制台输出结果是:
2
如果需要了解细节,可以参考官方文档。
5.2 类依赖分析器:jdeps
jdeps是一个相当棒的命令行工具,它可以展示包层级和类层级的Java类依赖关系,它以.class文件、目录或者Jar文件为输入,然后会把依赖关系输出到控制台。
我们可以利用jedps分析下Spring Framework库,为了让结果少一点,仅仅分析一个JAR文件:org.springframework.core-3.0.5.RELEASE.jar。
jdeps org.springframework.core-3.0.5.RELEASE.jar
这个命令会输出很多结果,我们仅看下其中的一部分:依赖关系按照包分组,如果在classpath上找不到依赖,则显示”not found”.
org.springframework.core-3.0.5.RELEASE.jar -> C:/Program Files/Java/jdk1.8.0/jre/lib/rt.jarorg.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)-> java.io -> java.lang -> java.lang.annotation -> java.lang.ref -> java.lang.reflect -> java.util -> java.util.concurrent -> org.apache.commons.logging not found-> org.springframework.asm not found-> org.springframework.asm.commons not foundorg.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)-> java.lang -> java.lang.annotation -> java.lang.reflect -> java.util
更多的细节可以参考官方文档。
6. JVM的新特性
使用Metaspace(JEP 122)代替持久代(PermGen space)。在JVM参数方面,使用-XX:MetaSpaceSize和-XX:MaxMetaspaceSize代替原来的-XX:PermSize和-XX:MaxPermSize。
7. Kesimpulan
通过为开发者提供很多能够提高生产力的特性,Java 8使得Java平台前进了一大步。现在还不太适合将Java 8应用在生产系统中,但是在之后的几个月中Java 8的应用率一定会逐步提高(PS:原文时间是2014年5月9日,现在在很多公司Java 8已经成为主流,我司由于体量太大,现在也在一点点上Java 8,虽然慢但是好歹在升级了)。作为开发者,现在应该学习一些Java 8的知识,为升级做好准备。