المقدمة: تم إصدار Java 8 لبعض الوقت ، وجميع العلامات تشير إلى أن Java 8 هو تغيير كبير في التوزيع. هناك بالفعل العديد من المقالات حول المهوسون Code Java الذين يقدمون ميزات جديدة لـ Java 8 ، مثل اللعب مع Java 8 Lambdas و Concurrency ، Java 8 Time Api Tutorial: LocalDateTime and Abstract interface في JDK 8 ERA. تشير هذه المقالة أيضًا إلى بعض المعلومات الأخرى ، مثل: 15 يجب قراءة دروس Java 8 والجانب المظلم من Java 8. وقد جمعت هذه المقالة المعلومات أعلاه وجمعها في كتاب مدرسي مرجعي على الميزات الجديدة لـ Java 8. آمل أن تكسب شيئًا ما.
1. مقدمة
ليس هناك شك في أن Java 8 هو أهم نسخة من Java منذ Java 5 (تم إصدارها في عام 2004). يحتوي هذا الإصدار على أكثر من عشرة ميزات جديدة باللغات والمترجمات والمكتبات والأدوات و JVM. في هذه المقالة ، سوف نتعلم هذه الميزات الجديدة ونستخدم أمثلة عملية لتوضيح السيناريوهات المناسبة للاستخدام.
يحتوي هذا البرنامج التعليمي على عدة أنواع من المشكلات التي يواجهها مطورو Java غالبًا:
لغة
المترجم
مكتبة
أداة
وقت التشغيل (JVM)
2. ميزات جديدة للغة جافا
Java 8 هي نسخة رئيسية من Java. يعتقد بعض الناس أنه على الرغم من أن هذه الميزات الجديدة متوقعة من قبل مطوري Java ، فإنها تتطلب أيضًا الكثير من الجهد للتعلم. في هذا القسم ، سنقدم معظم الميزات الجديدة لجافا 8.
2.1 تعبيرات Lambda والواجهات الوظيفية
تعتبر تعبيرات Lambda (المعروفة أيضًا باسم الإغلاق) أكبر التغييرات اللغوية الأكثر توقعًا في Java 8. إنها تسمح لنا بتمرير وظائف كمعلمات إلى طريقة ، أو لمعالجة الكود نفسه كبيانات: المطورين الوظيفيين على دراية بهذه المفاهيم. دعمت العديد من اللغات على منصات JVM (Groovy ، Scala ، وما إلى ذلك) تعبيرات Lambda منذ ولادتها ، ولكن لم يكن لدى مطوري Java خيار سوى استخدام الطبقات الداخلية المجهولة بدلاً من تعبيرات Lambda.
استغرق تصميم Lambda الكثير من الوقت والكثير من الجهود المجتمعية ، وأخيراً وجد حل تنفيذ التسوية الذي يمكن أن يحقق بنية لغة بسيطة ومدمجة. يمكن أن يتكون أبسط تعبير lambda من قائمة معلمات مفصولة بفاصلة ، -> الرمز ، وكتلة العبارات ، على سبيل المثال:
Arrays.aslist ("A" ، "B" ، "D").
في الكود أعلاه ، يتم اشتقاق نوع المعلمة E بواسطة منطق المترجم ، ويمكنك أيضًا تحديد نوع المعلمة بشكل صريح ، على سبيل المثال:
Arrays.aslist ("A" ، "B" ، "D").
إذا كان تعبير Lambda يتطلب كتلة بيانات أكثر تعقيدًا ، فيمكنك إرفاق كتلة العبارة ذات الأقواس المجعد ، على غرار جسم وظيفة في Java ، على سبيل المثال:
Arrays.aslist ("A" ، "B" ، "D").يمكن أن تشير تعبيرات Lambda إلى أعضاء الفصل والمتغيرات المحلية (والتي ستحول هذه المتغيرات ضمنيًا إلى نهائي) ، على سبيل المثال ، أن كتل الرمزتين التالية لها نفس التأثير:
String Seplator = "،" ؛ arrays.aslist ("A" ، "B" ، "D").و
Final String Selepator = "،" ؛ arrays.aslist ("A" ، "B" ، "D").تعبيرات Lambda لها قيم إرجاع ، ونوع قيم الإرجاع مشتقة أيضًا من خلال استدلال التحويل البرمجي. إذا كان لدى كتلة البيان في تعبير Lambda سطر واحد فقط ، فيمكنك ألا تحتاج إلى استخدام عبارة الإرجاع. قصاصات الرمز التالية لها نفس التأثير:
Arrays.aslist ("A" ، "B" ، "D") .Sort ((E1 ، E2) -> e1.compareto (e2)) ؛و
Arrays.aslist ("A" ، "B" ، "D") .Sort ((E1 ، E2) -> {int result = e1.compareto (e2) ؛ return return ؛}) ؛من أجل جعل الوظائف الموجودة متوافقة بشكل جيد مع تعبيرات Lambda ، نظر مصممو Lambda في العديد من الطرق ، لذلك توصلوا إلى مفهوم واجهة الوظيفة. تشير واجهة الوظيفة إلى واجهة مع وظيفة واحدة فقط ، ويمكن تحويل هذه الواجهة ضمنيًا إلى تعبير Lambda. java.lang.runnable و java.util.concurrent.callable هي أفضل الأمثلة على الواجهات الوظيفية. في الممارسة العملية ، تكون الواجهات الوظيفية هشة للغاية: طالما أن المطور يضيف وظيفة إلى الواجهة ، لم تعد الواجهة واجهة وظيفية ، مما يؤدي إلى فشل التجميع. من أجل التغلب على تعرض مستوى مستوى الرمز هذا وذكر صراحة أن الواجهة هي واجهة وظيفية ، توفر Java 8 عرضًا توضيحيًا خاصًا للواكب (جميع الواجهات ذات الصلة في مكتبة Java لديها بالفعل هذا التعليق) ، لإعطاء تعريف بسيط للواجهة الوظيفية:
functionalInterface الواجهة العامة الوظيفية {void method () ؛}ومع ذلك ، هناك شيء واحد يجب ملاحظة أن الطريقة الافتراضية والطريقة الثابتة لن تدمر تعريف الواجهة الوظيفية ، وبالتالي فإن الكود التالي قانوني.
functionalInterface الواجهة العامة processionaldefaultMethods {void method () ؛ void الافتراضي defaultMethod () {}}تعبيرات Lambda ، باعتبارها أكبر نقطة بيع في Java 8 ، لديها القدرة على جذب المزيد من المطورين للانضمام إلى منصة JVM واستخدام مفهوم البرمجة الوظيفية في برمجة Java الخالصة. إذا كنت بحاجة إلى معرفة المزيد عن تعبيرات Lambda ، فيمكنك الرجوع إلى الوثائق الرسمية.
2.2 الطرق الافتراضية والثابتة للواجهات
يستخدم Java 8 مفهومين جديدين لتوسيع معنى الواجهة: الطريقة الافتراضية والطريقة الثابتة. الطريقة الافتراضية تجعل الواجهة مشابهة بعض الشيء للسمات ، ولكن الأهداف التي سيتم تحقيقها مختلفة. تتيح الطريقة الافتراضية للمطورين إضافة طرق جديدة إلى واجهات موجودة دون كسر التوافق الثنائي ، أي عدم فرض فئات تنفذ الواجهة لتنفيذ الطريقة المضافة حديثًا في نفس الوقت.
الفرق بين الطريقة الافتراضية وطريقة تجريدية هو أنه يجب تنفيذ طريقة تجريدية ، في حين أن الطريقة الافتراضية لا. سيتم موروثة الأساليب الافتراضية التي توفرها الواجهة أو الكتابة فوقها بواسطة فئة تنفيذ الواجهة. رمز المثال هو كما يلي:
الواجهة الخاصة التي يمكن أن تسمح الآن واجهات {// الآن تسمح بالأساليب الافتراضية ، يجوز للمنفذ أو / / / / لا يجوز لها تنفيذ (تجاوز). السلسلة الافتراضية notrequired () {return "التنفيذ الافتراضي" ؛ }} الفئة الثابتة الخاصة التي تنفذ DefaultableImpl تنفذ {} private static static verridableImpl تنفذ {Override public string notrequired () {return "التنفيذ التجويعي" ؛}}تستخدم الواجهة القابلة للتطبيق الافتراضي للكلمة الرئيسية لتحديد طريقة افتراضية Notrequired (). تنفذ فئة DefaultableIMPL هذه الواجهة وترث الطرق الافتراضية في هذه الواجهة افتراضيًا ؛ تقوم فئة OverRidableImpl أيضًا بتنفيذ هذه الواجهة ، ولكنها تتجاوز الطرق الافتراضية للواجهة وتوفر تطبيقًا مختلفًا.
ميزة أخرى مثيرة للاهتمام التي قدمها Java 8 هي أنه يمكن تعريف الطرق الثابتة في واجهات. رمز المثال هو كما يلي:
الواجهة الخاصة defaulablefactory {// واجهات تسمح الآن الأساليب الثابتة بإنشاء (المورد <sultable> المورد) {return supplier.get () ؛}}يدمج مقتطف الكود التالي سيناريوهات الاستخدام للطرق الافتراضية والطرق الثابتة:
public static void main (string [] args) {defaulaable default = defaulablefactory.create (defaultableImpl :: new) ؛ system.out.println (defaultable.notrequired ()إخراج هذا الرمز كما يلي:
التنفيذ الافتراضي
التنفيذ المتجاوز
نظرًا لأن تنفيذ الطريقة الافتراضية على JVM يوفر الدعم على مستوى Bytecode ، فهو فعال للغاية. تسمح الطريقة الافتراضية بوجود واجهات محسّنة دون كسر نظام الميراث الحالي. تطبيق هذه الميزة في المكتبة الرسمية هو: إضافة طرق جديدة إلى واجهة java.util.collection ، مثل Stream () ، parallelstream () ، foreach () ، و removeif () ، إلخ.
على الرغم من أن الأساليب الافتراضية لها العديد من الفوائد ، إلا أنه ينبغي استخدامها بحذر في التطوير الفعلي: في أنظمة الميراث المعقدة ، يمكن أن تسبب الطرق الافتراضية أخطاء الغموض والتجميع. إذا كنت تريد معرفة المزيد من التفاصيل ، فيرجى الرجوع إلى الوثائق الرسمية.
2.3 مرجع الطريقة
تتيح مرجع الطريقة للمطورين الرجوع مباشرة إلى الأساليب الحالية ، أو منشئات فئة Java أو كائنات مثيل. يتم استخدام مراجع الطريقة وتعبيرات Lambda بالاقتران مع بعضها البعض ، مما يجعل مُنشئ فئة Java يبدو مضغوطًا وموجزًا ، دون الكثير من رمز القالب المعقد.
في مثال سيمون ، تعد فئة السيارات مثالًا على مراجع الطريقة المختلفة ، والتي يمكن أن تساعد القراء على التمييز بين أربعة أنواع من مراجع الطريقة.
سيارة فئة ثابتة عامة {Care Public Static Car Create (المورد النهائي <Car> المورد) {return supplier.get () ؛} زميل الفراغ الثابت العام (السيارة النهائية) {system.out.println ("إصلاح" + this.toString ()) ؛}}نوع المرجع للطريقة الأولى هو مرجع مُنشئ ، بناء الجملة هو الفئة :: جديدة ، أو أكثر عامة: فئة <T> :: جديدة. ملاحظة: لا يحتوي هذا المنشئ على معلمات.
السيارة النهائية للسيارة = car.create (Car :: New) ؛ القائمة النهائية <Car> cars = arrays.aslist (Car) ؛
نوع مرجع الطريقة الثانية هو مرجع طريقة ثابتة ، وبناء الجملة هو الفئة :: static_method. ملاحظة: تقبل هذه الطريقة معلمة نوع السيارة.
cars.foreach (Car :: Collide) ؛
تشير الطريقة الثالثة إلى أن النوع هو إشارة إلى طريقة عضو في فئة معينة ، وبناء الجملة هو الفئة ::. لاحظ أن هذه الطريقة لا تحدد المعلمة:
cars.foreach (سيارة :: إصلاح) ؛
الطريقة الرابعة التي يتم الرجوع إليها هي إشارة إلى طريقة عضو في كائن مثيل ، وبناء الجملة هو مثيل :: الطريقة. ملاحظة: تقبل هذه الطريقة معلمة نوع السيارة:
شرطة السيارة النهائية = car.create (Car :: New) ؛ cars.foreach (Police :: Follow) ؛
قم بتشغيل المثال أعلاه ويمكنك رؤية الإخراج التالي في وحدة التحكم (قد يكون مثيل السيارة مختلفًا):
collided com.javacodegeeks.java8.method.references.methodReferences@7a81197d تم إصلاح com.javacodegekes.java8.method.references.methodreferences. com.javacodegeeks.java8.method.references.methodreferences@7a81197d
إذا كنت ترغب في فهم ومعرفة المزيد من المحتوى التفصيلي ، فيمكنك الرجوع إلى الوثائق الرسمية
2.4 كرر التعليقات
منذ إدخال التعليقات التوضيحية في Java 5 ، أصبحت هذه الميزة شائعة جدًا وتم استخدامها على نطاق واسع في مختلف الأطر والمشاريع. ومع ذلك ، فإن التعليقات التوضيحية لها قيود كبيرة: لا يمكن استخدام نفس التعليقات التوضيحية عدة مرات في نفس المكان. Java 8 يكسر هذا القيد ويقدم مفهوم التعليقات التوضيحية المتكررة ، مما يسمح باستخدام التعليقات التوضيحية نفسها عدة مرات في نفس المكان.
إن تحديد التعليقات التوضيحية المتكررة في Java 8 باستخدام التعليقات التوضيحية القابلة للتراجع ليس في الواقع تحسينًا على مستوى اللغة ، ولكنه خدعة قام بها المترجم ، ولا تزال التكنولوجيا الأساسية هي نفسها. يمكنك استخدام الكود التالي لشرح:
حزمة com.javacodegeeks.java8.repeatable.annotations ؛ استيراد java.lang.annotation.elementType ؛ استيراد java.lang.annotation.repeatable ؛ استيراد java.lang.annotation.Reentering ؛ استيراد java.lang.annotation.RetentionPolicy ؛ استيراد java.lang.annotation.target ؛ الطبقة العامة تكرار {target (elementType.type) reentention (enthypolicy.runtime) public interface filters {filter [] value () ؛} target (elementType.type) reateRine (ententionpolicy.runtime) repeatable (filters.class) filter ("filter1") filter ("filter2") الواجهة العامة القابلة للتصفية {} public static void main (string [] args) {for (filter filter: filterable.class.getAnnotationsbytype (filter.class)) {System.Println (filter.value () ؛كما نرى ، تستخدم فئة المرشح هنا التعليق التوضيحي (Filters.Class) REPEETABLE (المرشحات. يحاول المترجم منع هذه التفاصيل من المطورين. وبهذه الطريقة ، يمكن شرح الواجهة القابلة للتصفية بتعليقات مرشحين (لم يتم ذكر أي معلومات حول المرشحات هنا).
بالإضافة إلى ذلك ، توفر واجهة برمجة تطبيقات الانعكاس طريقة جديدة: getAnnotationsByType () ، والتي يمكن أن تعيد التعليقات التوضيحية المكررة لنوع معين ، على سبيل المثال
ستعيد filterable.class.getannoation (filters.class) حالتين مرشحين ، ومخرجات المحتوى إلى وحدة التحكم هي كما يلي:
Filter1
Filter2
إذا كنت تريد معرفة المزيد ، فيمكنك الرجوع إلى الوثائق الرسمية.
2.5 استنتاج أفضل نوع
قام برنامج التحويل البرمجي Java 8 بإجراء تحسينات كبيرة في الاستدلال النوع. في العديد من السيناريوهات ، يمكن للمترجم استنتاج نوع البيانات للمعلمة معينة ، مما يجعل الكود أكثر إيجازًا. رمز المثال هو كما يلي:
حزمة com.javacodegeeks.java8.type.Inference ؛ قيمة الفئة العامة <T> {public static <T> t DefaultValue () {return null ؛ } p public t getordefault (t value ، t defaultValue) {return (value! = null)؟ القيمة: DefaultValue ؛}}الرمز التالي هو تطبيق من قيمة النوع <string>:
حزمة com.javacodegeeks.java8.type.Inference ؛ الفئة العامة من نوع الفئة العامة {public static void main (string [] args) {value final <string> value = new value <> () ؛ value.getordefault ("22" ، value.defaultValue ()) ؛}}يتم اشتقاق نوع المعلمة value.defaultValue () بواسطة التحويل البرمجي ولا يحتاج إلى تحديد صراحة. في Java 7 ، سيكون لهذا الرمز خطأ في التجميع ما لم يكن القيمة. <string> DefaultValue () يستخدم.
2.6 توسيع سيناريوهات التطبيق من التعليقات التوضيحية
Java 8 يوسع سيناريوهات التطبيق من التعليقات التوضيحية. الآن ، يمكن استخدام التعليقات التوضيحية على أي عنصر تقريبًا: المتغيرات المحلية ، وأنواع الواجهة ، والطبقات الفائقة ، وفئات تنفيذ الواجهة ، وحتى في تعريفات الاستثناء للوظائف. فيما يلي بعض الأمثلة:
حزمة com.javacodegeeks.java8.annotations ؛ استيراد java.lang.annotation.elementType ؛ استيراد java.lang.annotation.Reentering ؛ استيراد java.lang.annotation.RetentionPolicy ؛ استيراد java.lang.annotation.target ؛ استيراد java.util.arraylist ؛ استيراد java.util.collection ؛ التعليقات التوضيحية للطبقة العامة {retention (attreentionpolicy.runtime) target ({elementType.type_use ، elementtype.type_parameter}) public interface nonempty {} public static static <nononempty t> "غير مستخدم") الفراغ الثابت العام (String [] args) {Hinal Holder <String> Holder = NewNonempty حامل <string> () ؛ Collection NonEmpty <NonEmpty strings> strings = new ArrayList <> () ؛ }}elementType.type_user و elementType.type_parameter هما شرحان جديدان تمت إضافتهما إلى Java 8 لوصف سيناريوهات الاستخدام للشروح. لغة جافا
قام يان أيضًا بإجراء تغييرات مماثلة لتحديد هذه الملاحظات المضافة حديثًا.
3. ميزات جديدة لمرجم Java
3.1 اسم المعلمة
من أجل الحصول على أسماء المعلمات للطرق في برامج Java في وقت التشغيل ، يجب على الأجيال القديمة من مبرمجي Java استخدام طرق مختلفة ، مثل Paranamer Liberary. تقوم Java 8 أخيرًا بتطبيع هذه الميزة ، مع الدعم على مستوى اللغة (باستخدام API الانعكاس وطريقة parameter.getName ()) ومستوى Bytecode (باستخدام برنامج التحويل البرمجي Javac الجديد ومعلمة الملامين).
حزمة com.javacodegeeks.java8.parameter.names ؛ استيراد java.lang.reflect.method ؛ استيراد java.lang.reflect.parameter ؛ أسماء parameternames الفئة العامة {public static void main (string [] args) reswes {method method = parameTernames.class.getMethod ("main" ، string []. class) ؛ لـ (المعلمة المعلمة النهائية: method.getParameters ()) {system.out.println ("المعلمة:" + parameter.getName ()) ؛}}}}في Java 8 ، يتم إيقاف هذه الميزة افتراضيًا ، لذلك إذا قمت بتجميع الكود أعلاه بدون المعلمة -المبرممة وتشغيلها ، فسيتم إخراج النتائج التالية:
المعلمة: Arg0
إذا تم استخدام المعلمة -Parameters ، فستتم إخراج النتيجة التالية (النتيجة الصحيحة):
المعلمة: args
إذا كنت تستخدم Maven لإدارة المشروع ، فيمكنك تكوين المعلمة -Parameters في عنصر تكوين برنامج التحويل البرمجي Maven-Compiler-Plugin:
<reclosin> <rougiD> org.apache.maven.plugins </groupId> <ArtifactId> maven-compiler-plugin </stifactid> <splection> 3.1 </sophuration> 1.8 <sugment> <suplerArgument> -Parameters </supilerargument> </ource> </target>
4. ميزات جديدة من مكتبة جافا الرسمية
أضافت Java 8 العديد من فئات الأدوات الجديدة (فئات التاريخ/الوقت) وفئات الأدوات الموجودة لدعم البرمجة المتزامنة الحديثة ، البرمجة الوظيفية ، إلخ.
4.1 اختياري
الأخطاء الأكثر شيوعًا في تطبيقات Java هي استثناءات القيمة الفارغة. قبل Java 8 ، قدمت Google Guava فئة Optionals لحل NullPointerException ، وبالتالي تجنب الكود المصدري الملوث بالشيكات الفارغة المختلفة ، بحيث يمكن للمطورين كتابة رمز نظافة. تضيف Java 8 أيضًا اختياريًا إلى المكتبة الرسمية.
الاختياري هو مجرد شيء سهل: تخزين قيمة من النوع T أو NULL. يوفر بعض الواجهات المفيدة لتجنب التحقق الصريح الفارغ ، ويمكنك الرجوع إلى وثائق Java 8 الرسمية لمزيد من التفاصيل.
بعد ذلك ، دعونا نلقي نظرة على بعض الأمثلة على استخدام اختياري: قيمة قد تكون فارغة أو قيمة لنوع معين:
اختياري <String> fullname = potortal.ofnullable (null) ؛ system.out.println ("تم تعيين الاسم الكامل؟" + fullname.ispresent ()) ؛ system.out.println ("الاسم الكامل:" + fullname.orelseget (() -> "[none]") ؛ system.out.println (fullname.map (s -> "hey" + s + "!") .orelse ("Hey Stranger!") ؛إذا كان المثيل الاختياري يحتفظ بقيمة غير فنية ، فإن طريقة iSpresent () تُرجع صحيحة ، وإلا فإنها تعيد خطأ ؛ طريقة orelseget () ، والمثال الاختياري يحمل فارغة ، يمكن أن يقبل القيمة الافتراضية التي تم إنشاؤها بواسطة تعبير Lambda ؛ يمكن للطريقة MAP () تحويل قيمة المثيل الاختياري الحالي إلى قيمة جديدة ؛ تشبه طريقة orelse () طريقة orelseget () ، ولكنها تُرجع القيمة الافتراضية التي تم تمريرها عند الضغط على NULL.
نتائج الإخراج للرمز أعلاه هي كما يلي:
تم تعيين الاسم الكامل؟ الاسم الكامل الخاطئ: [لا شيء] يا غريب!
لنلقي نظرة على مثال بسيط آخر:
اختياري <String> firstName = اختياري (tom ") ؛ System.out.println ("تم تعيين الاسم الأول؟" + firstName.ispresent ()) ؛ System.out.println ("الاسم الأول:" + firstName.orelSeget (() -> "[none]") ؛ System.out.println (firstName.map (s -> "Hey" + S + "!") .orelse ("Hey Stranger!") ؛ system.out.println () ؛إخراج هذا المثال هو:
تم تعيين الاسم الأول؟ الاسم الأول الحقيقي: توم هاي توم!
إذا كنت تريد معرفة المزيد من التفاصيل ، فيرجى الرجوع إلى الوثائق الرسمية.
4.2 تيارات
تقدم API Stream API (java.util.stream) المضافة حديثًا برمجة وظيفية للبيئة التي تم إنشاؤها في مكتبة Java. هذا هو إلى حد بعيد أكبر تحسن لمكتبات Java حتى يتمكن المطورون من كتابة رمز أكثر كفاءة وموجزة ومدمجة.
تقوم API Steam بتبسيط عمليات التجميع بشكل كبير (سنرى أكثر من مجموعات فقط لاحقًا). أولاً ، دعونا نلقي نظرة على هذه الفئة المسمى Task:
تدفقات الفئة العامة {private enum status {open ، leable} ؛ مهمة فئة نهائية ثابتة {حالة الحالة النهائية الخاصة ؛ نقاط Integer النهائية الخاصة ؛ المهمة (حالة الحالة النهائية ، نقاط عدد صحيح نهائي) {this.status = status ؛ this.points = points ؛} public integer getPoints () {return points ؛} الحالة العامة getStatus () {return status ؛} Override public tostring () {return string.format ("[٪ s ، ٪ d]" ، الحالة ، النقاط) ؛}}}يحتوي فئة المهام على مفهوم للكسر (أو التعقيد الزائف) ، وهناك دولتان أخريان: مفتوحان أو مغلقان. لنفترض الآن أن هناك مجموعة من المهام:
المجموعة النهائية <Stask> المهام = Arrays.Aslist (مهمة جديدة (الحالة. open ، 5) ، مهمة جديدة (status.Open ، 13) ، مهمة جديدة (status.closed ، 8)) ؛
أولاً ، دعونا نلقي نظرة على سؤال: كم عدد نقاط الحالة المفتوحة الموجودة في مجموعة المهام هذه؟ قبل Java 8 ، لحل هذه المشكلة ، تحتاج إلى استخدام Foreach للحلق على جمع المهام ؛ ومع ذلك ، في Java 8 ، يمكنك استخدام Steams لحلها: قم بتضمين قائمة بسلسلة من العناصر ، ودعم المعالجة المتسلسلة والموازاة.
// حساب إجمالي النقاط لجميع المهام النشطة باستخدام SUM () Final TotalPointSOFOPENTASKS = المهام. مرشح
إخراج وحدة التحكم لتشغيل هذه الطريقة هو:
إجمالي النقاط: 18
هناك العديد من نقاط المعرفة تستحق الحديث عنها هنا. أولاً ، يتم تحويل مجموعة المهام إلى تمثيل بخار ؛ ثانياً ، عملية التصفية على مرشحات البخار من جميع المهام المغلقة ؛ ثالثًا ، تقوم عملية Mappoint بتحويل دفق المهام إلى مجموعة عدد صحيح استنادًا إلى طريقة Task :: GetPoints لكل مثيل مهمة ؛ أخيرًا ، يتم حساب المبلغ بواسطة طريقة SUM للحصول على النتيجة النهائية.
قبل تعلم المثال التالي ، تحتاج إلى تذكر بعض نقاط المعرفة حول Steams (انقر هنا لمزيد من التفاصيل). يمكن تقسيم العمليات فوق البخار إلى عمليات وسيطة والعمليات المتأخرة.
ستعيد العمليات الوسيطة بخار جديد - لن يؤدي إجراء عملية وسيطة (مثل المرشح) عملية التصفية الفعلية ، ولكنها إنشاء بخار جديد ووضع العناصر التي تلبي الظروف في البخار الأصلي في البخار الذي تم إنشاؤه حديثًا.
العمليات المتأخرة (مثل foreach أو sum) ستجتاز البخار والحصول على النتائج أو النتائج المصاحبة ؛ بعد إجراء العمليات المتأخرة ، تمت معالجة خط معالجة البخار ولا يمكن استخدامه. في جميع الحالات تقريبًا ، تعبر العمليات المتأخرة البخار على الفور.
قيمة أخرى للبخار هي الدعم الإبداعي للمعالجة الموازية. بالنسبة لمجموعة المهام المذكورة أعلاه ، يمكننا استخدام الكود التالي لحساب مجموع نقاط جميع المهام:
// حساب إجمالي النقاط لجميع المهام النهائية المزدوجة نقاط التوسيع = المهام.
هنا نستخدم الطريقة المتوازية لمعالجة جميع المهام بالتوازي وحساب النتيجة النهائية باستخدام طريقة تقليل. إخراج وحدة التحكم على النحو التالي:
إجمالي النقاط (جميع المهام): 26.0
بالنسبة للمجموعة ، غالبًا ما يكون من الضروري تجميع العناصر فيه وفقًا لشروط معينة. يمكن إكمال هذا النوع من المهمة بسرعة باستخدام واجهة برمجة التطبيقات التي توفرها Steam. الرمز كما يلي:
// Group Casks by Satuth Final Map <Status ، List <Task>> map = tasks.stream (). collection (collectors.groupingby (Task :: getStatus)) ؛ system.out.println (map) ؛
إخراج وحدة التحكم كما يلي:
{مغلق = [[مغلق ، 8]] ، مفتوح = [[مفتوح ، 5] ، [مفتوح ، 13]]}
السؤال الأخير حول مجموعة المهام هو: كيفية حساب نسبة نقاط كل مهمة في المجموعة في المجموعة. رمز المعالجة المحدد هو كما يلي:
// احسب وزن كل مهام (كنسبة مئوية من إجمالي النقاط) المجموعة النهائية <string> النتيجة = المهام. )) // longstream .maptoObj (النسبة المئوية -> النسبة المئوية + "٪") // تيار <string> .collect (collectors.toList ()) ؛ // list <string> system.out.println (result) ؛
نتائج إخراج وحدة التحكم هي كما يلي:
[19 ٪ ، 50 ٪ ، 30 ٪]
أخيرًا ، كما ذكرنا سابقًا ، لا يمكن لـ Steam API التصرف على مجموعات Java فحسب ، بل يمكن أن تستفيد عمليات IO التقليدية (قراءة البيانات من ملف أو خط الشبكة عن طريق السطر) من معالجة البخار. هنا مثال صغير:
مسار المسار النهائي = ملف جديد (اسم الملف) .Topath () ؛ جرب (دفق <string> lines = files.lines (path ، standardcharsets.utf_8)) {lines.onclose (() -> system.out.println ("done!").يقوم Stream Method OnClose بإرجاع دفق مكافئ بمقبض إضافي. سيتم تنفيذ هذا المقبض عند استدعاء طريقة الإغلاق (). إن API Stream ، وتعبيرات Lambda ، ومراجع الطريقة التي تدعمها الطرق الافتراضية للواجهة والأساليب الثابتة هي استجابة Java 8 للنموذج الحديث لتطوير البرمجيات.
4.3 تاريخ/وقت واجهة برمجة التطبيقات (JSR 310)
تقدم Java 8 واجهة برمجة تطبيقات جديدة للوقت (JSR 310) لتحسين معالجة الوقت والتواريخ. لطالما كانت إدارة الوقت والتاريخ هي المشكلة الأكثر إيلامًا لمطوري Java. java.util.date و java.util.calendar في وقت لاحق لم يحل هذه المشكلة (حتى أكثر الخلط من قبل المطورين).
بسبب الأسباب المذكورة أعلاه ، وُلدت مكتبة Joda-Time لجهة خارجية ، والتي يمكن أن تحل محل واجهة برمجة تطبيقات Java's Management API. تتأثر واجهة برمجة تطبيقات الوقت والتاريخ الجديد في Java 8 بعمق في وقت Joda ، وقد استوعبت الكثير من جوهر Joda-time. تحتوي حزمة Java.time الجديدة على جميع الفئات حول التاريخ والوقت والمنطقة الزمنية والفورية (على غرار تاريخها ولكنها دقيقة مع النانو ثانية) والمدة (المدة) وعمليات الساعة. تعتبر واجهة برمجة التطبيقات المصممة حديثًا على محمل الجد ثبات هذه الفئات (المحاضرات المستفادة من java.util.calendar) ، وتُرجع كائنًا جديدًا إذا احتاجت إلى تعديل مثيل.
دعنا نلقي نظرة على الفصول الرئيسية وأمثلة استخدامها في حزمة Java.time. أولاً ، تستخدم فئة الساعة منطقة زمنية لإرجاع الوقت والتاريخ الحاليين. يمكن أن تحل الساعة محل System.CurrentTimeMillis () و TimeZone.getDefault ().
// احصل على ساعة النظام كـ UTC Offset Clock Final Clock = clock.systemutc () ؛ system.out.println (clock.instant ()) ؛ system.out.println (clock.millis ()) ؛
إخراج هذا المثال هو:
2014-04-12T15: 19: 29.282Z 1397315969360
ثانياً ، التركيز على فصول LocalDate و LocalTime. يحتوي LocalDate فقط على جزء التاريخ في نظام تقويم ISO-8601 ؛ يحتوي المحلي على الجزء الزمني فقط في نظام التقويم. يمكن بناء كائنات كلا الفئتين باستخدام كائنات الساعة.
// احصل على التاريخ المحلي والوقت المحلي النهائي تاريخ المحلية = localdate.now () ؛ النهائي المحلي dateFromClock = localDate.Now (clock) ؛ system.out.println (DateFromClock) ؛ // احصل على التاريخ المحلي والوقت المحلي للوقت المحلي = localtime.now () ؛ Final TimeFromClock = localtime.now (clock) ؛ system.out.println (time) ؛ system.out.println (timefromclock) ؛
نتائج الإخراج للمثال أعلاه هي كما يلي:
2014-04-12 2014-04-12 11: 25: 54.568 15: 25: 54.568
تحتوي فئة LocalDateTime على معلومات حول LocalDate و LocalTime ، ولكنها لا تحتوي على معلومات المنطقة الزمنية في نظام تقويم ISO-8601. فيما يلي بعض الأمثلة حول LocalDate و LocalTime:
// احصل على التاريخ/الوقت المحلي المحلي dateTime = localDateTime.Now () ؛ FinaldateTime DateTimeFromClock = localDateTime.Now (clock) ؛ System.out.println (DateTime) ؛ System.out.println (DateTimeFromClock) ؛
نتائج الإخراج للمثال أعلاه هي كما يلي:
2014-04-12T11: 37: 52.309 2014-04-12T15: 37: 52.309
إذا كنت بحاجة إلى معلومات عن البيانات/الوقت للحصول على منطقة زمنية محددة ، فيمكنك استخدام ZonedateTime ، الذي يحتفظ بتاريخ ووقت نظام تاريخ ISO-8601 ، وله معلومات زمنية. فيما يلي بعض الأمثلة على استخدام المناطق الزمنية المختلفة:
// احصل على تاريخ/الوقت ZonedDateTime ZonedDateTime = ZonedDateTime.Now () ؛ ZonedDateTime النهائي ZonedDateTimeFromClock = ZonedDateTime.Now (الساعة) ؛ ZonedDateTime النهائي ZonedDateTimeFromzone = ZonedDateTime.Now (Zoneid.of ("America/los_angeles")) ؛ System.out.println (ZonedDateTime) ؛ System.out.println (ZonedDateTimeFromClock) ؛ System.out.println (ZonedDateTimeFromClock) ؛ System.out.println (ZonedDateTimeFromzone) ؛إخراج هذا المثال هو:
2014-04-12T11: 47: 01.017-04: 00 [أمريكا/new_york] 2014-04-12T15: 47: 01.017Z 2014-04-12T08: 47: 01.017-07: 00 [أمريكا/los_angeles]
أخيرًا ، دعونا نلقي نظرة على فئة المدة ، التي تتمثل في الوقت إلى الثواني والثانية. هذا يجعل من السهل حساب الفرق بين تاريخين ، رمز المثال هو كما يلي:
// الحصول على المدة بين تاريخين محليين نهائيين من = localDateTime.of (2014 ، month.April ، 16 ، 0 ، 0 ، 0) ؛ Final LocalDatetime to = localDateTime.of (2015 ، month.april ، 16 ، 23 ، 59 ، 59) ؛ mater.todays ()) ؛ system.out.println ("المدة في الساعات:" + مدة. tothours ()) ؛يستخدم هذا المثال لحساب عدد الأيام والساعات بين 16 أبريل 2014 و 16 أبريل 2015 ، والمخرجات كما يلي:
المدة في الأيام: 365 مدة في الساعات: 8783
إن الانطباع العام عن تاريخ ووقت Java 8 الجديد إيجابي نسبيًا ، ويرجع ذلك جزئيًا إلى التأثير الإيجابي لوقت Joda ، وجزئيًا لأن المسؤول استمع أخيرًا إلى احتياجات المطورين. إذا كنت تريد معرفة المزيد من التفاصيل ، فيمكنك الرجوع إلى الوثائق الرسمية.
4.4 محرك ناشورن جافا سكريبت
توفر Java 8 محرك Nashorn JavaScript الجديد ، مما يسمح لنا بتطوير وتشغيل تطبيقات JS على JVM. Nashorn JavaScript Engine هو نسخة تنفيذ أخرى من Javax.script.scriptengine. يتبع هذا النوع من محرك البرنامج النصي نفس القواعد ويسمح باستخدام Java و JavaScript بشكل تفاعلي. رمز المثال هو كما يلي:
ScriptenGeneManager Manager = New ScriptenGeneManager () ؛ Scriptengine Engine = Manager.getEngineByName ("JavaScript") ؛ System.out.println (Engine.getClass ().إخراج هذا الرمز كما يلي:
jdk.nashorn.api.scripting.nashornscriptengine النتائج: 2
4.5 base64
تمت إضافة دعم ترميز BASE64 إلى مكتبة Java 8 الرسمية ، بحيث يمكن تنفيذ ترميز BASE64 دون استخدام مكتبة طرف ثالث. رمز المثال هو كما يلي:
حزمة com.javacodegeeks.java8.base64 ؛ استيراد java.nio.charset.standardCharsets ؛ استيراد java.util.base64 ؛ الفئة العامة base64s {public static void main (string [] args) {Final String text = "base64 أخيرًا في Java 8!" ؛ سلسلة نهائية مشفرة = base64.getencoder (). encodetoString (text.getBytes (standardcharsets.utf_8)) ؛ system.out.println (مشفرة) ؛ سلسلة نهائية decoded = new String (base64.getDecoder (). decode (مشفر) ، standardcharsets.utf_8) ؛ system.out.println (decoded) ؛}}إخراج هذا المثال كما يلي:
qmfzzty0igzpbmfsbhkgaw4gsmf2ysa4iq ==
BASE64 أخيرًا في Java 8!
يدعم BASE64API الجديد ترميز وتفكك عناوين URL والمناجم.
(BASE64.GETURLENCODER () / BASE64.GETURLDECODER () ، BASE64.GETMIMEENCODER () / BASE64.GETMIMEDECODER ()).
4.6 صفائف موازية
أضاف إصدار Java8 العديد من الطرق الجديدة لدعم معالجة الصفيف الموازية. الطريقة الأكثر أهمية هي Parallelsort () ، والتي يمكن أن تسرع بشكل كبير فرز الصفيف على الآلات متعددة النواة. يوضح المثال التالي طريقة سلسلة Parallexxxx:
package com.javacodegeeks.java8.paralled.arrays ؛ import java.util.arrays ؛ import java.util.concurrent.throadlocalrandom ؛ الطبقة العامة الموازية {public static void main (string [] args) {long [] arrayoflong = new long [20000] ؛ Arrays.Parallelsetall (arrayoflong ، index -> threadlocalrandom.current (). nextint (1000000)) ؛ arrays.stream (arrayoflong) .limit (10). ) .limit (10). foreach (i -> system.out.print (i + "")上述这些代码使用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. 结论
通过为开发者提供很多能够提高生产力的特性,Java 8使得Java平台前进了一大步。现在还不太适合将Java 8应用在生产系统中,但是在之后的几个月中Java 8的应用率一定会逐步提高(PS:原文时间是2014年5月9日,现在在很多公司Java 8已经成为主流,我司由于体量太大,现在也在一点点上Java 8,虽然慢但是好歹在升级了)。作为开发者,现在应该学习一些Java 8的知识,为升级做好准备。