JavaRush /وبلاگ جاوا /Random-FA /ویژگی های جاوا 8 – راهنمای نهایی (قسمت 2)
0xFF
مرحله
Донецк

ویژگی های جاوا 8 – راهنمای نهایی (قسمت 2)

در گروه منتشر شد
قسمت دوم ترجمه مقاله Java 8 Features – The ULTIMATE Guide . بخش اول اینجاست (لینک ممکن است تغییر کند). ویژگی های جاوا 8 – راهنمای نهایی (قسمت 2) - 1

5. ویژگی های جدید در کتابخانه های جاوا 8

جاوا 8 بسیاری از کلاس‌های جدید را اضافه کرده و کلاس‌های موجود را گسترش داده است تا از همزمانی مدرن، برنامه‌نویسی کاربردی، تاریخ/زمان و موارد دیگر پشتیبانی کند.

5.1. کلاس اختیاری

NullPointerException معروف تا حد زیادی شایع ترین علت خرابی برنامه های جاوا است. مدت ها پیش، پروژه عالی گوگل GuavaOptional به عنوان یک راه حل ارائه شد NullPointerException، در نتیجه از آلوده شدن کد توسط چک های پوچ و در نتیجه تشویق به نوشتن کدهای پاک تر جلوگیری کرد. کلاس Guava الهام گرفته از Google Optionalاکنون بخشی از جاوا 8 است. Optionalاین کلاس فقط یک ظرف است: می تواند حاوی یک مقدار یا نوعی باشد Тیا به سادگی null باشد. بسیاری از روش‌های مفید را ارائه می‌کند تا بررسی‌های تهی صریح دیگر توجیه نشوند. برای اطلاعات بیشتر به اسناد رسمی مراجعه کنید. بیایید به دو مثال کوچک از استفاده نگاه کنیم Optional: با و بدون null.
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!" ) );
اگر نمونه حاوی مقدار غیر تهی باشد، متد trueisPresent() را برمی‌گرداند و در غیر این صورت false . این روش حاوی مکانیزم بازگشتی برای نتیجه است که اگر حاوی null باشد، توابعی را برای تولید یک مقدار پیش فرض می پذیرد. متد map () مقدار فعلی را تبدیل می کند و یک نمونه جدید برمی گرداند . روش مشابه است ، اما به جای یک تابع، یک مقدار پیش فرض می گیرد. این هم خروجی این برنامه: OptionalorElseGet()OptionalOptionalOptionalorElse()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) برنامه نویسی سبک تابعی واقعی را در جاوا معرفی می کند. این تا حد زیادی جامع ترین افزونه به کتابخانه جاوا است و به توسعه دهندگان جاوا اجازه می دهد تا به طور قابل توجهی کارآمدتر باشند و همچنین آنها را قادر می سازد تا کد کارآمد، تمیز و مختصر ایجاد کنند. 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 )
);
اولین سوالی که می خواهیم بدانیم این است که وظایف OPEN در حال حاضر شامل چند امتیاز است؟ قبل از جاوا 8، راه حل معمول برای این کار استفاده از تکرار کننده بود foreach. اما در جاوا 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 را به جریان s تبدیل می کند . در نهایت، تمام نقاط با استفاده از روش جمع‌بندی می‌شوند که نتیجه نهایی را ارائه می‌کند. قبل از رفتن به نمونه های بعدی، نکاتی در مورد موضوعات وجود دارد که باید در نظر داشته باشید (جزئیات بیشتر در اینجا ). عملیات به عملیات میانی و نهایی تقسیم می شود . عملیات میانی یک جریان جدید را برمی گرداند. آنها همیشه تنبل هستند؛ هنگام انجام عملیات میانی مانند ، در واقع فیلترینگ را انجام نمی دهند، بلکه یک جریان جدید ایجاد می کنند که پس از تکمیل، حاوی عناصر جریان اصلی است که با محمول داده شده مطابقت دارد. عملیات محدود ، مانند و ، می تواند از طریق یک جریان عبور داده شود تا نتیجه یا عارضه جانبی ایجاد شود. پس از اتمام عملیات نهایی، جریان استفاده شده در نظر گرفته می شود و نمی توان دوباره از آن استفاده کرد. تقریباً در همه موارد، عملیات پایانی تمایل به تکمیل پیمایش خود از طریق منبع داده اساسی دارند. یکی دیگر از ویژگی های ارزشمند thread ها پشتیبانی از فرآیندهای موازی خارج از جعبه است. بیایید به این مثال نگاه کنیم که مجموع نمرات همه مسائل را پیدا می کند. IntegerTask::getPointsTasksumstreamfilterforEachsum
// 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 API فقط برای مجموعه های جاوا نیست. یک عملیات ورودی/خروجی معمولی، مانند خواندن فایل های متنی خط به خط، کاندیدای بسیار خوبی برای استفاده از پردازش جریان است. در اینجا یک مثال کوچک برای اثبات این موضوع وجود دارد.
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 به همراه روش‌های لامبدا و مرجع به همراه روش‌های پیش‌فرض و استاتیک در جاوا 8 پاسخی به پارادایم‌های توسعه نرم‌افزار مدرن هستند. برای اطلاعات بیشتر، لطفاً به اسناد رسمی مراجعه کنید .

5.3. API تاریخ/زمان (JSR 310)

جاوا 8 با ارائه API جدید تاریخ و زمان (JSR 310) ظاهر جدیدی را به مدیریت تاریخ و زمان می بخشد . دستکاری تاریخ و زمان یکی از بدترین نقاط دردسر برای توسعه دهندگان جاوا است. استاندارد java.util.Dateزیر java.util.Calendarبه طور کلی وضعیت را بهبود نمی بخشد (شاید حتی آن را گیج کننده تر می کند). Joda-Time اینگونه متولد شد : یک جایگزین عالی تاریخ/زمان API برای جاوا . API جدید Date/Time در جاوا 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فقط بخشی از timecode را شامل می شود.
// получить местную 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در سیستم تقویم ISO-8601 به هم پیوسته LocaleDateو LocalTimeحاوی تاریخ و زمان است، اما بدون منطقه زمانی. یک مثال ساده در زیر آورده شده است.
// 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
برداشت کلی از تاریخ/زمان جدید در جاوا 8 بسیار بسیار مثبت است. تا حدی به این دلیل که تغییرات بر اساس یک پایه آزمایش شده در نبرد (Joda-Time) است، تا حدی به این دلیل که این بار این موضوع به طور جدی مورد بررسی قرار گرفت و صدای توسعه دهندگان شنیده شد. برای جزئیات، لطفاً به اسناد رسمی مراجعه کنید .

5.4. موتور جاوا اسکریپت Nashorn

جاوا 8 با موتور جاوا اسکریپت جدید Nashorn ارائه می شود که به شما امکان می دهد انواع خاصی از برنامه های جاوا اسکریپت را در JVM توسعه و اجرا کنید. موتور جاوا اسکریپت Nashorn به سادگی پیاده سازی دیگری از javax.script.ScriptEngine است که از مجموعه قوانین یکسانی پیروی می کند تا به جاوا و جاوا اسکریپت اجازه تعامل داشته باشد. در اینجا یک مثال کوچک است.
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 با انتشار جاوا 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. آرایه های موازی

نسخه جاوا 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(). این برنامه 10 عنصر اول را قبل و بعد از مرتب سازی چاپ می کند تا نشان دهد که آرایه واقعا مرتب شده است. یک نمونه خروجی برنامه ممکن است به این شکل باشد (توجه داشته باشید که عناصر آرایه تصادفی هستند).
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.util.concurrent.locks.StampedLockبرای ارائه قفل مبتنی بر قابلیت با سه حالت دسترسی برای کنترل خواندن/نوشتن اضافه شده است (می توان آن را جایگزین بهتری برای حالت نه چندان خوب در نظر گرفت java.util.concurrent.locks.ReadWriteLock). کلاس های جدیدی که به بسته اضافه شده اند java.util.concurrent.atomic:
  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • LongAdder

6. ویژگی های جدید در محیط اجرا جاوا (JVM)

این منطقه PermGenبازنشسته شده است و با Metaspace (JEP 122) جایگزین شده است. گزینه های JVM -XX:PermSizeو به ترتیب با و -XX:MaxPermSizeجایگزین شده اند . -XX:MetaSpaceSize-XX:MaxMetaspaceSize

7. نتیجه گیری

آینده اینجاست: جاوا 8 پلتفرم خود را با ارائه ویژگی هایی که به توسعه دهندگان امکان بهره وری بیشتر را می دهد، به جلو حرکت داده است. هنوز برای انتقال سیستم های تولید به جاوا 8 خیلی زود است، اما پذیرش باید به آرامی در چند ماه آینده شروع به رشد کند. با این حال، اکنون زمان آن فرا رسیده است که پایگاه کد خود را برای سازگاری با جاوا 8 آماده کنید و زمانی که جاوا 8 به اندازه کافی ایمن و پایدار است، آماده باشید تا تغییرات جاوا 8 را وارد کنید. به عنوان گواهی بر پذیرش جاوا 8 توسط جامعه، Pivotal اخیراً Spring Framework را با پشتیبانی تولید جاوا 8 منتشر کرد . می توانید نظرات خود را در مورد ویژگی های جدید و هیجان انگیز جاوا 8 در نظرات ارائه دهید.

8. منابع

برخی از منابع اضافی که جنبه های مختلف ویژگی های جاوا 8 را به طور عمیق مورد بحث قرار می دهند:
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION