JavaRush /مدونة جافا /Random-AR /ميزات Java 8 – الدليل الشامل (الجزء الثاني)
0xFF
مستوى
Донецк

ميزات Java 8 – الدليل الشامل (الجزء الثاني)

نشرت في المجموعة
الجزء الثاني من ترجمة المقال مميزات Java 8 – الدليل الشامل . الجزء الأول هنا (قد يتغير الرابط). ميزات Java 8 - الدليل النهائي (الجزء 2) - 1

5. ميزات جديدة في مكتبات Java 8

أضافت Java 8 العديد من الفئات الجديدة ووسعت الفئات الموجودة لدعم التزامن الحديث والبرمجة الوظيفية والتاريخ/الوقت والمزيد.

5.1. فئة اختيارية

يعد NullPointerException الشهير هو السبب الأكثر شيوعًا لفشل تطبيقات Java. منذ وقت طويل، تم تقديم مشروع Google الممتاز GuavaOptional كحل NullPointerException، وبالتالي منع تلوث التعليمات البرمجية عن طريق عمليات التحقق الخالية، ونتيجة لذلك، تشجيع كتابة تعليمات برمجية أنظف. أصبحت فئة Guava المستوحاة من Google Optionalالآن جزءًا من Java 8. Optionalإنها مجرد حاوية: يمكن أن تحتوي على قيمة أو نوع ما Т، أو ببساطة تكون فارغة. إنه يوفر العديد من الطرق المفيدة بحيث لم تعد عمليات التحقق الفارغة الصريحة مبررة. الرجوع إلى الوثائق الرسمية للحصول على معلومات أكثر تفصيلا. دعونا نلقي نظرة على مثالين صغيرين للاستخدام Optional: مع وبدون قيمة فارغة.
Optional<String> fullName = Optional.ofNullable( null );
System.out.println( "Full Name is set? " + fullName.isPresent() );
System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) );
System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
isPresent()ترجع الطريقة صحيحًا إذا كان المثيل Optionalيحتوي على قيمة غير فارغة وخطأ بخلاف ذلك. تحتوي الطريقة orElseGet()على آلية احتياطية للنتيجة إذا Optionalكانت تحتوي على قيمة خالية، وتقبل الوظائف لإنشاء قيمة افتراضية. تقوم طريقة الخريطة () بتحويل القيمة الحالية Optionalوإرجاع مثيل جديد Optional. الطريقة orElse()مشابهة لـ orElseGet()، ولكن بدلاً من الوظيفة، فإنها تأخذ قيمة افتراضية. هنا هو إخراج هذا البرنامج:
Full Name is set? false
Full Name: [none]
Hey Stranger!
دعونا نلقي نظرة سريعة على مثال آخر:
Optional<String> firstName = Optional.of( "Tom" );
System.out.println( "First Name is set? " + firstName.isPresent() );
System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) );
System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
System.out.println();
وستكون النتيجة مثل هذا:
First Name is set? true
First Name: Tom
Hey Tom!
لمزيد من المعلومات التفصيلية، يرجى الرجوع إلى الوثائق الرسمية .

5.2. تيارات

تقدم واجهة Stream API ( ) المضافة حديثًا java.util.streamبرمجة بأسلوب وظيفي حقيقي في Java. إنها الإضافة الأكثر شمولاً إلى مكتبة Java وتسمح لمطوري Java بأن يكونوا أكثر كفاءة بشكل ملحوظ كما تمكنهم من إنشاء تعليمات برمجية فعالة ونظيفة وموجزة. تجعل Stream API عملية معالجة المجموعات أسهل بكثير (على سبيل المثال لا الحصر، كما سنرى لاحقًا). لنأخذ فصلًا بسيطًا كمثال Task.
public class Streams  {
    private enum Status {
        OPEN, CLOSED
    };

    private static final class Task {
        private final Status status;
        private final Integer points;

        Task( final Status status, final Integer points ) {
            this.status = status;
            this.points = points;
        }

        public Integer getPoints() {
            return points;
        }

        public Status getStatus() {
            return status;
        }

        @Override
        public String toString() {
            return String.format( "[%s, %d]", status, points );
        }
    }
}
تحتوي المهمة على بعض النقاط (أو الصعوبات الزائفة) ويمكن أن تكون إما مفتوحة أو مغلقة . دعونا نقدم مجموعة صغيرة من المشاكل للعب بها.
final Collection<Task> tasks = Arrays.asList(
    new Task( Status.OPEN, 5 ),
    new Task( Status.OPEN, 13 ),
    new Task( Status.CLOSED, 8 )
);
السؤال الأول الذي ننوي معرفته هو كم عدد النقاط التي تحتوي عليها المهام المفتوحة حاليًا؟ قبل Java 8، كان الحل المعتاد لذلك هو استخدام المكرر foreach. ولكن في Java 8، الإجابة هي التدفقات: سلسلة من العناصر التي تدعم العمليات التجميعية المتسلسلة والمتوازية.
// Подсчет общего количества очков всех активных задач с использованием sum()
final long totalPointsOfOpenTasks = tasks
    .stream()
    .filter( task -> task.getStatus() == Status.OPEN )
    .mapToInt( Task::getPoints )
    .sum();

System.out.println( "Total points: " + totalPointsOfOpenTasks );
وسيبدو إخراج وحدة التحكم كما يلي:
Total points: 18
دعونا نلقي نظرة على ما يحدث هنا. أولاً، يتم تحويل مجموعة المهام إلى تمثيل متدفق. تقوم العملية بعد ذلك filterبتصفية كافة المهام ذات الحالة المغلقة . في الخطوة التالية، تقوم العملية mapToIntبتحويل الدفق Tasks إلى الدفق Integers باستخدام طريقة Task::getPointsلكل مثيل Task. وأخيرا، يتم جمع جميع النقاط باستخدام الطريقة sumالتي تعطي النتيجة النهائية. قبل الانتقال إلى الأمثلة التالية، هناك بعض الملاحظات حول المواضيع التي يجب وضعها في الاعتبار (مزيد من التفاصيل هنا ). streamتنقسم العمليات إلى عمليات متوسطة ونهائية . العمليات الوسيطة ترجع دفقًا جديدًا. إنهم دائمًا كسالى؛ عند إجراء عمليات وسيطة مثل ، فإنهم لا يقومون فعليًا بالتصفية، ولكن بدلاً من ذلك يقومون بإنشاء دفق جديد، والذي، عند اكتماله، يحتوي على عناصر الدفق الأصلي التي تطابق المسند المحدد. يمكن تمرير العمليات المحدودة ، مثل و ، عبر تيار لإنتاج نتيجة أو تأثير جانبي. بمجرد اكتمال العملية النهائية، يعتبر الدفق مستخدمًا ولا يمكن استخدامه مرة أخرى. في جميع الحالات تقريبًا، تميل العمليات النهائية إلى إكمال اجتيازها عبر مصدر البيانات الأساسي. ميزة أخرى قيمة للخيوط هي دعم العمليات المتوازية خارج الصندوق. دعونا نلقي نظرة على هذا المثال، الذي يجد مجموع درجات جميع المسائل. filterforEachsum
// Calculate total points of all tasks
final double totalPoints = tasks
   .stream()
   .parallel()
   .map( task -> task.getPoints() ) // or map( Task::getPoints )
   .reduce( 0, Integer::sum );

System.out.println( "Total points (all tasks): " + totalPoints );
هذا مشابه جدًا للمثال الأول، إلا أننا نحاول معالجة جميع المهام بالتوازي وحساب النتيجة النهائية باستخدام الطريقة reduce. هنا هو إخراج وحدة التحكم:
Total points (all tasks): 26.0
في كثير من الأحيان تكون هناك حاجة لتجميع العناصر وفقا لمعيار معين. يوضح المثال كيف يمكن أن تساعد المواضيع في هذا.
// Группировка задач по их статусу
final Map<Status, List<Task>> map = tasks
    .stream()
    .collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );
سيكون إخراج وحدة التحكم كما يلي:
{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
للانتهاء من أمثلة المشكلات، دعنا نحسب النسبة المئوية الإجمالية (أو الوزن) لكل مشكلة في المجموعة بناءً على إجمالي النقاط:
// Подсчет веса каждой задачи (How процент от общего количества очков)
final Collection<String> result = tasks
    .stream()                                        // Stream<String>
    .mapToInt( Task::getPoints )                     // IntStream
    .asLongStream()                                  // LongStream
    .mapToDouble( points -> points / totalPoints )   // DoubleStream
    .boxed()                                         // Stream<Double>
    .mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
    .mapToObj( percentage -> percentage + "%" )      // Stream<String>
    .collect( Collectors.toList() );                 // List<String>

System.out.println( result );
سيكون إخراج وحدة التحكم مثل هذا:
[19%, 50%, 30%]
وأخيرًا، كما أشرنا سابقًا، فإن واجهة برمجة تطبيقات Stream ليست مخصصة لمجموعات Java فقط. تعتبر عملية الإدخال/الإخراج النموذجية، مثل قراءة الملفات النصية سطرًا تلو الآخر، مرشحًا جيدًا جدًا لاستخدام معالجة الدفق. هنا مثال صغير لإثبات ذلك.
final Path path = new File( filename ).toPath();
try( Stream<String> lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
    lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
}
تقوم الطريقة onConsole، التي يتم استدعاؤها على خيط، بإرجاع خيط مكافئ مع معالج خاص إضافي. يتم استدعاء المعالج الخاص عند close()استدعاء أسلوب ما في سلسلة رسائل. تعد Stream API جنبًا إلى جنب مع lambdas والأساليب المرجعية جنبًا إلى جنب مع الأساليب الافتراضية والثابتة في Java 8 هي الحل لنماذج تطوير البرامج الحديثة. لمزيد من المعلومات التفصيلية، يرجى الرجوع إلى الوثائق الرسمية .

5.3. واجهة برمجة تطبيقات التاريخ/الوقت (JSR 310)

يقدم Java 8 مظهرًا جديدًا لإدارة التاريخ والوقت من خلال توفير واجهة برمجة تطبيقات التاريخ والوقت الجديدة (JSR 310) . يعد التلاعب بالتاريخ والوقت أحد أسوأ نقاط الضعف التي يواجهها مطورو Java. المعايير java.util.Dateالتالية java.util.Calendarلم تحسن الوضع بشكل عام (وربما جعلت الأمر أكثر إرباكًا). هذه هي الطريقة التي ولدت بها Joda-Time : بديل رائع لواجهة برمجة تطبيقات التاريخ/الوقت لـ Java . تتأثر واجهة برمجة تطبيقات التاريخ/الوقت الجديدة في Java 8 (JSR 310) بشدة بـ Joda-Time وتأخذ الأفضل منها. java.timeتحتوي الحزمة الجديدة على كافة فئات التاريخ والوقت والتاريخ/الوقت والمناطق الزمنية والمدد ومعالجة الوقت . أخذ تصميم واجهة برمجة التطبيقات (API) مبدأ الثبات على محمل الجد: فالتغييرات غير مسموح بها (درس صعب تعلمته من java.util.Calendar). إذا كان التعديل مطلوبًا، فسيتم إرجاع مثيل جديد للفئة المقابلة. دعونا نلقي نظرة على الفئات الرئيسية وأمثلة على استخدامها. الفئة الأولى Clock، والتي توفر الوصول إلى اللحظة الحالية والتاريخ والوقت باستخدام المنطقة الزمنية. Clockيمكن استخدامها بدلا من System.currentTimeMillis()و TimeZone.getDefault().
// Получить системное время How смещение UTC
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );
System.out.println( clock.millis() );
مثال على إخراج وحدة التحكم:
2014-04-12T15:19:29.282Z
1397315969360
الفئات الجديدة الأخرى التي سننظر إليها هي LocaleDateو LocalTime. LocaleDateيحتوي فقط على جزء التاريخ بدون المنطقة الزمنية في نظام التقويم ISO-8601. وبناء على ذلك، LocalTimeفهو يحتوي فقط على جزء من الرمز الزمني>.
// получить местную date и время время
final LocalDate date = LocalDate.now();
final LocalDate dateFromClock = LocalDate.now( clock );

System.out.println( date );
System.out.println( dateFromClock );

// получить местную date и время время
final LocalTime time = LocalTime.now();
final LocalTime 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يتسلسل LocaleDateو LocalTimeويحتوي على التاريخ والوقت، ولكن بدون منطقة زمنية، في نظام التقويم ISO-8601. ويرد مثال بسيط أدناه.
// Get the local date/time
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );

System.out.println( datetime );
System.out.println( datetimeFromClock );
مثال على إخراج وحدة التحكم:
2014-04-12T11:37:52.309
2014-04-12T15:37:52.309
في حال كنت بحاجة إلى التاريخ/الوقت لمنطقة زمنية محددة، ZonedDateTime. أنه يحتوي على التاريخ والوقت في نظام التقويم ISO-8601. فيما يلي بعض الأمثلة لمناطق زمنية مختلفة.
// Получение даты/времени для временной зоны
final ZonedDateTime zonedDatetime = ZonedDateTime.now();
final ZonedDateTime zonedDatetimeFromClock = ZonedDateTime.now( clock );
final ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now( ZoneId.of( "America/Los_Angeles" ) );

System.out.println( zonedDatetime );
System.out.println( zonedDatetimeFromClock );
System.out.println( zonedDatetimeFromZone );
مثال على إخراج وحدة التحكم:
2014-04-12T11:47:01.017-04:00[America/New_York]
2014-04-12T15:47:01.017Z
2014-04-12T08:47:01.017-07:00[America/Los_Angeles]
وأخيرًا، دعونا نلقي نظرة على الفصل الدراسي Duration: النطاق الزمني بالثواني والنانو ثانية. وهذا يجعل الحساب بين تاريخين بسيطًا جدًا. دعونا نرى كيفية القيام بذلك:
// Получаем разницу между двумя датами
final LocalDateTime from = LocalDateTime.of( 2014, Month.APRIL, 16, 0, 0, 0 );
final LocalDateTime to = LocalDateTime.of( 2015, Month.APRIL, 16, 23, 59, 59 );

final Duration duration = Duration.between( from, to );
System.out.println( "Duration in days: " + duration.toDays() );
System.out.println( "Duration in hours: " + duration.toHours() );
يحسب المثال أعلاه المدة (بالأيام والساعات) بين تاريخين، 16 أبريل 2014 و 16 أبريل 2015 . فيما يلي مثال على إخراج وحدة التحكم:
Duration in days: 365
Duration in hours: 8783
الانطباع العام عن التاريخ/الوقت الجديد في Java 8 إيجابي للغاية. ويرجع ذلك جزئيًا إلى أن التغييرات تستند إلى أساس تم اختباره في المعركة (Joda-Time)، وجزئيًا لأنه تمت إعادة النظر بجدية في المشكلة وتم سماع أصوات المطورين. لمزيد من التفاصيل، يرجى الرجوع إلى الوثائق الرسمية .

5.4. محرك جافا سكريبت ناشورن

يأتي Java 8 مزودًا بمحرك Nashorn JavaScript الجديد ، والذي يسمح لك بتطوير وتشغيل أنواع معينة من تطبيقات JavaScript على JVM. محرك Nashorn JavaScript هو ببساطة تطبيق آخر لـ javax.script.ScriptEngine الذي يتبع نفس مجموعة القواعد للسماح لـ Java وJavaScript بالتفاعل. وهنا مثال صغير.
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName( "JavaScript" );

System.out.println( engine.getClass().getName() );
System.out.println( "Result:" + engine.eval( "function f() { return 1; }; f() + 1;" ) );
مثال على إخراج وحدة التحكم:
jdk.nashorn.api.scripting.NashornScriptEngine
Result: 2

5.5. قاعدة64

أخيرًا، وجد دعم ترميز Base64 طريقه إلى مكتبة Java القياسية مع إصدار Java 8. إنه سهل الاستخدام للغاية، والمثال يوضح ذلك.
package com.javacodegeeks.java8.base64;

import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class Base64s {
    public static void main(String[] args) {
        final String text = "Base64 finally in Java 8!";

        final String encoded = Base64
            .getEncoder()
            .encodeToString( text.getBytes( StandardCharsets.UTF_8 ) );
        System.out.println( encoded );

        final String decoded = new String(
            Base64.getDecoder().decode( encoded ),
            StandardCharsets.UTF_8 );
        System.out.println( decoded );
    }
}
يُظهر مخرج وحدة التحكم الخاصة بالبرنامج النص المشفر والمفكك:
QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!
هناك أيضًا فئات لبرامج التشفير/وحدات فك التشفير الصديقة لعنوان URL، بالإضافة إلى برامج التشفير/وحدات فك التشفير الصديقة لـ MIME ( Base64.getUrlEncoder()/ Base64.getUrlDecoder()، Base64.getMimeEncoder()/ Base64.getMimeDecoder()).

5.6. المصفوفات المتوازية

يضيف إصدار Java 8 العديد من الأساليب الجديدة لمعالجة المصفوفات المتوازية. ولعل أهمها هو parallelSort()الذي يمكنه تسريع عملية الفرز على الأجهزة متعددة النواة بشكل كبير. يوضح المثال الصغير أدناه عائلة الأساليب ( parallelXxx) الجديدة قيد التنفيذ.
package com.javacodegeeks.java8.parallel.arrays;

import 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(). يقوم البرنامج بطباعة العناصر العشرة الأولى قبل وبعد الفرز لإظهار أن المصفوفة مرتبة بالفعل. قد يبدو مثال لمخرجات البرنامج بهذا الشكل (لاحظ أن عناصر المصفوفة عشوائية).
Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378
Sorted: 39 220 263 268 325 607 655 678 723 793

5.7. تماثل

تمت إضافة طرق جديدة إلى الفصل java.util.concurrent.ConcurrentHashMapلدعم العمليات المجمعة بناءً على كائنات الدفق المضافة حديثًا وتعبيرات لامدا. تمت أيضًا إضافة طرق جديدة إلى الفصل java.util.concurrent.ForkJoinPoolلدعم التجميع المشترك (راجع أيضًا الدورة التدريبية المجانية الخاصة بنا حول توافق Java ). تمت إضافة فئة جديدة java.util.concurrent.locks.StampedLockلتوفير قفل قائم على القدرة مع ثلاثة أوضاع وصول للتحكم في القراءة/الكتابة (يمكن اعتباره بديلاً أفضل للوضع غير الجيد java.util.concurrent.locks.ReadWriteLock). الفئات الجديدة التي تم إضافتها إلى الحزمة java.util.concurrent.atomic:
  • تراكم مزدوج
  • DoubleAdder
  • تراكم طويل
  • LongAdder

6. ميزات جديدة في بيئة تشغيل Java (JVM)

PermGenتم سحب المنطقة واستبدالها بـ Metaspace (JEP 122). -XX:PermSizeتم -XX:MaxPermSizeاستبدال خيارات JVM بـ -XX:MetaSpaceSizeو -XX:MaxMetaspaceSizeعلى التوالي.

7. الخاتمة

المستقبل هنا: لقد ساهم Java 8 في نقل نظامه الأساسي إلى الأمام من خلال تقديم ميزات تتيح للمطورين أن يكونوا أكثر إنتاجية. لا يزال من السابق لأوانه نقل أنظمة الإنتاج إلى Java 8، ولكن من المفترض أن يبدأ التبني في النمو ببطء خلال الأشهر القليلة المقبلة. ومع ذلك، فقد حان الوقت لبدء إعداد قاعدة التعليمات البرمجية الخاصة بك للتوافق مع Java 8 والاستعداد لدمج تغييرات Java 8 عندما تكون آمنة ومستقرة بدرجة كافية. كدليل على قبول المجتمع لـ Java 8، أصدرت Pivotal مؤخرًا Spring Framework مع دعم الإنتاج لـ Java 8 . يمكنك تقديم مدخلاتك حول الميزات الجديدة والمثيرة في Java 8 في التعليقات.

8. المصادر

بعض الموارد الإضافية التي تناقش الجوانب المختلفة لميزات Java 8 بعمق:
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION