JavaRush /Java Blogu /Random-AZ /Java 8 Xüsusiyyətləri – Ən Yaxşı Bələdçi (2-ci Hissə)
0xFF
Səviyyə
Донецк

Java 8 Xüsusiyyətləri – Ən Yaxşı Bələdçi (2-ci Hissə)

Qrupda dərc edilmişdir
Java 8 Features – ULTIMATE Guide məqaləsinin tərcüməsinin ikinci hissəsi . Birinci hissə buradadır (link dəyişə bilər). Java 8 Xüsusiyyətləri – Ən Yaxşı Bələdçi (2-ci Hissə) - 1

5. Java 8 kitabxanalarında yeni funksiyalar

Java 8 müasir paralellik, funksional proqramlaşdırma, tarix/saat və s. daha yaxşı dəstəkləmək üçün bir çox yeni siniflər əlavə etdi və mövcud sinifləri genişləndirdi.

5.1. Sinif Könüllü

Məşhur NullPointerException Java proqramlarının uğursuzluqlarının ən çox yayılmış səbəbidir. Uzun müddət əvvəl Google-un mükəmməl layihəsi GuavaOptional həll yolu kimi təqdim etdi NullPointerExceptionvə bununla da kodun boş yoxlamalarla çirklənməsinin qarşısını aldı və nəticədə daha təmiz kodun yazılmasını təşviq etdi. Google-dan ilham alan Guava sinfi Optionalindi Java 8-in bir hissəsidir. OptionalBu, sadəcə bir konteynerdir: o, dəyər və ya hər hansı bir növü ehtiva edə bilər Тvə ya sadəcə null ola bilər. O, bir çox faydalı metodlar təqdim edir ki, açıq boş yoxlamalar artıq əsaslandırılmır. Daha ətraflı məlumat üçün rəsmi sənədlərə baxın . İki kiçik istifadə nümunəsinə baxaq Optional: null ilə və olmadan.
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!" ) );
Nümunədə qeyri-null dəyər varsa , metod doğru , əks halda isə yalanisPresent() qaytarır . Metod, defolt dəyəri yaratmaq üçün funksiyaları qəbul edən null ehtiva edərsə, nəticə üçün geri qaytarma mexanizmini ehtiva edir . Xəritə () metodu cari dəyəri çevirir və yeni bir nümunə qaytarır . Metod -a bənzəyir , lakin funksiya əvəzinə standart dəyər alır. Bu proqramın çıxışı: OptionalorElseGet()OptionalOptionalOptionalorElse()orElseGet()
Full Name is set? false
Full Name: [none]
Hey Stranger!
Başqa bir nümunəyə qısaca nəzər salaq:
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();
Nəticə belə olacaq:
First Name is set? true
First Name: Tom
Hey Tom!
Daha ətraflı məlumat üçün rəsmi sənədlərə müraciət edin .

5.2. Axınlar

Yeni əlavə edilmiş Stream API ( java.util.stream) Java-da real funksional stil proqramlaşdırmasını təqdim edir. Bu, Java kitabxanasına indiyə qədər ən əhatəli əlavədir və Java tərtibatçılarına əhəmiyyətli dərəcədə daha səmərəli olmağa imkan verir və həmçinin onlara səmərəli, təmiz və qısa kod yaratmağa imkan verir. Stream API kolleksiyaların işlənməsini xeyli asanlaşdırır (lakin daha sonra görəcəyimiz kimi onlarla məhdudlaşmır). Nümunə olaraq sadə bir sinif götürək 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 );
        }
    }
}
Tapşırıqda bəzi məqamlar (yaxud psevdo çətinliklər) var və ya AÇIQ , ya da BAP ola bilər . Oynamaq üçün kiçik problemlər toplusunu təqdim edək.
final Collection<Task> tasks = Arrays.asList(
    new Task( Status.OPEN, 5 ),
    new Task( Status.OPEN, 13 ),
    new Task( Status.CLOSED, 8 )
);
Öyrənmək istədiyimiz ilk sual odur ki, AÇIQ tapşırıqlar hazırda neçə nöqtədən ibarətdir? Java 8-dən əvvəl bunun üçün adi həll iteratordan istifadə etmək olardı foreach. Lakin Java 8-də cavab axınlardır: ardıcıl və paralel məcmu əməliyyatları dəstəkləyən elementlər ardıcıllığı.
// Подсчет общего количества очков всех активных задач с использованием sum()
final long totalPointsOfOpenTasks = tasks
    .stream()
    .filter( task -> task.getStatus() == Status.OPEN )
    .mapToInt( Task::getPoints )
    .sum();

System.out.println( "Total points: " + totalPointsOfOpenTasks );
Və konsol çıxışı belə görünəcək:
Total points: 18
Gəlin burada nə baş verdiyinə baxaq. Birincisi, tapşırıq kolleksiyası axın təqdimatına çevrilir. Əməliyyat sonra bütün tapşırıqları QAPALIfilter statusu ilə süzür . Növbəti addımda əməliyyat hər bir nümunə üçün metoddan istifadə edərək axın s-ni axın s-ə çevirir . Nəhayət, son nəticəni təmin edən metoddan istifadə edərək bütün xallar cəmlənir . Növbəti nümunələrə keçməzdən əvvəl, yadda saxlamaq üçün mövzular haqqında bəzi qeydlər var (daha ətraflı burada ). Əməliyyatlar aralıqson əməliyyatlara bölünür . Aralıq əməliyyatlar yeni axını qaytarır. Onlar həmişə tənbəl olurlar; kimi ara əməliyyatları yerinə yetirərkən , onlar əslində süzgəcdən keçirmirlər, əksinə yeni axın yaradırlar ki, bu da tamamlandıqdan sonra verilmiş predikata uyğun gələn orijinal axının elementlərini ehtiva edir. Sonlu əməliyyatlar , məsələn və kimi , nəticə və ya yan təsir yaratmaq üçün axın vasitəsilə keçirilə bilər. Son əməliyyat başa çatdıqdan sonra axın istifadə edilmiş sayılır və yenidən istifadə edilə bilməz. Demək olar ki, bütün hallarda, son əməliyyatlar əsas məlumat mənbəyi vasitəsilə öz keçidini tamamlayır. İplərin başqa bir dəyərli xüsusiyyəti qutudan kənar paralel proseslərə dəstəkdir. Bütün məsələlərin xallarının cəmini tapan bu misala baxaq. mapToIntTaskIntegerTask::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 );
Bu, birinci nümunəyə çox bənzəyir, istisna olmaqla, biz bütün tapşırıqları paralel olaraq emal etməyə və metoddan istifadə edərək son nəticəni hesablamağa çalışırıq reduce. Budur konsol çıxışı:
Total points (all tasks): 26.0
Çox vaxt elementləri müəyyən bir meyara görə qruplaşdırmağa ehtiyac var. Nümunə mövzuların bu işdə necə kömək edə biləcəyini nümayiş etdirir.
// Группировка задач по их статусу
final Map<Status, List<Task>> map = tasks
    .stream()
    .collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );
Konsol çıxışı aşağıdakı kimi olacaq:
{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
Problem nümunələri ilə başa çatdırmaq üçün ümumi xallara əsasən topludakı hər bir problemin ümumi faizini (və ya çəkisini) hesablayaq:
// Подсчет веса каждой задачи (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 );
Konsol çıxışı belə olacaq:
[19%, 50%, 30%]
Nəhayət, daha əvvəl qeyd etdiyimiz kimi, Stream API yalnız Java kolleksiyaları üçün deyil. Mətn fayllarını sətir-sətir oxumaq kimi tipik bir I/O əməliyyatı axın emalından istifadə üçün çox yaxşı namizəddir. Bunu sübut etmək üçün kiçik bir nümunə təqdim edirik.
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 );
}
onConsoleMövzuda çağırılan metod əlavə özəl işləyici ilə ekvivalent ipi qaytarır. close()Şəxsi işləyici bir mövzuda metod çağırıldıqda çağırılır . Stream API, lambdalaristinad metodları ilə yanaşı Java 8-də defolt və statik metodlar müasir proqram təminatının inkişafı paradiqmalarına cavabdır. Daha ətraflı məlumat üçün rəsmi sənədlərə müraciət edin .

5.3. Tarix/Saat API (JSR 310)

Java 8 yeni Tarix və Saat API (JSR 310) təqdim etməklə tarix və vaxt idarəçiliyinə yeni görünüş gətirir . Tarix və vaxt manipulyasiyası Java tərtibatçıları üçün ən pis ağrı nöqtələrindən biridir. java.util.DateAşağıdakı standart java.util.Calendarümumiyyətlə vəziyyəti yaxşılaşdırmadı (bəlkə də onu daha çaşdırıcı etdi). Joda-Time belə yarandı: Java üçün əla tarix/vaxt API alternativi . Java 8-də (JSR 310) yeni Tarix/Saat API-si Joda-Time- dan çox təsirlənir və ondan ən yaxşısını alır. Yeni paket tarix, vaxt, tarix/saat, vaxt zonaları, müddətlər və vaxt manipulyasiyası üçün bütün siniflərijava.time ehtiva edir . API dizaynı dəyişməzliyi çox ciddi qəbul etdi: dəyişikliklərə icazə verilmir (dan çətin bir dərs ). Dəyişiklik tələb olunarsa, müvafiq sinfin yeni nümunəsi qaytarılacaq. Əsas siniflərə və onlardan istifadə nümunələrinə baxaq. Saat qurşağından istifadə edərək cari ani, tarix və vaxta girişi təmin edən birinci sinif . və əvəzinə istifadə edilə bilər . java.util.CalendarClockClockSystem.currentTimeMillis()TimeZone.getDefault()
// Получить системное время How смещение UTC
final Clock clock = Clock.systemUTC();
System.out.println( clock.instant() );
System.out.println( clock.millis() );
Konsol çıxışı nümunəsi:
2014-04-12T15:19:29.282Z
1397315969360
Baxacağımız digər yeni siniflər LocaleDateLocalTime. LocaleDateISO-8601 təqvim sistemində saat qurşağı olmayan yalnız tarix hissəsini ehtiva edir. Müvafiq olaraq, LocalTimevaxt kodunun yalnız bir hissəsini ehtiva edir>.
// получить местную 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 );
Konsol çıxışı nümunəsi:
2014-04-12
2014-04-12
11:25:54.568
15:25:54.568
LocalDateTimeISO-8601 təqvim sistemində birləşdirir LocaleDatevə tarix və vaxtı ehtiva edir, lakin saat qurşağı yoxdur. LocalTimeSadə bir nümunə aşağıda verilmişdir.
// Get the local date/time
final LocalDateTime datetime = LocalDateTime.now();
final LocalDateTime datetimeFromClock = LocalDateTime.now( clock );

System.out.println( datetime );
System.out.println( datetimeFromClock );
Konsol çıxışı nümunəsi:
2014-04-12T11:37:52.309
2014-04-12T15:37:52.309
Müəyyən bir saat qurşağı üçün tarix/vaxt lazımdırsa, ZonedDateTime. O, ISO-8601 təqvim sistemində tarix və vaxtı ehtiva edir. Burada müxtəlif vaxt zonaları üçün bəzi nümunələr verilmişdir.
// Получение даты/времени для временной зоны
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 );
Konsol çıxışı nümunəsi:
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]
Və nəhayət, sinifə nəzər salaq Duration: saniyə və nanosaniyələrdə vaxt aralığı. Bu, iki tarix arasında hesablamağı çox sadə edir. Bunu necə edəcəyinə baxaq:
// Получаем разницу между двумя датами
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() );
Yuxarıdakı nümunə iki tarix, 16 aprel 2014-cü il16 aprel 2015-ci il tarixləri arasındakı müddəti (gün və saatlarla) hesablayır . Konsol çıxışının bir nümunəsidir:
Duration in days: 365
Duration in hours: 8783
Java 8-də yeni tarix/vaxt haqqında ümumi təəssürat çox, çox müsbətdir. Qismən dəyişikliklərin döyüşdə sınaqdan keçirilmiş təmələ (Joda-Time) əsaslandığına görə, qismən ona görə ki, bu dəfə məsələyə ciddi şəkildə yenidən baxılıb və tərtibatçıların səsi eşidildi. Təfərrüatlar üçün rəsmi sənədlərə müraciət edin .

5.4. Nashorn JavaScript mühərriki

Java 8 , JVM-də müəyyən növ JavaScript proqramlarını inkişaf etdirməyə və işə salmağa imkan verən yeni Nashorn JavaScript mühərriki ilə gəlir . Nashorn JavaScript mühərriki sadəcə olaraq Java və JavaScript-in qarşılıqlı əlaqəsinə icazə vermək üçün eyni qaydalar toplusuna əməl edən javax.script.ScriptEngine-in başqa bir tətbiqidir. Budur kiçik bir nümunə.
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;" ) );
Konsol çıxışı nümunəsi:
jdk.nashorn.api.scripting.NashornScriptEngine
Result: 2

5.5. Baza 64

Nəhayət, Base64 kodlaşdırmasına dəstək Java 8-in buraxılması ilə Java standart kitabxanasına daxil oldu. İstifadəsi çox asandır, nümunə bunu göstərir.
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 );
    }
}
Proqramın konsol çıxışı həm kodlanmış, həm də deşifrə olunmuş mətni göstərir:
QmFzZTY0IGZpbmFsbHkgaW4gSmF2YSA4IQ==
Base64 finally in Java 8!
URL-dostu kodlayıcılar/dekoderlər, eləcə də MIME-dost kodlayıcılar/dekoderlər ( Base64.getUrlEncoder()/ Base64.getUrlDecoder(), Base64.getMimeEncoder()/ Base64.getMimeDecoder()) üçün də siniflər var.

5.6. Paralel massivlər

Java 8 buraxılışı paralel massivlərin işlənməsi üçün bir çox yeni metodlar əlavə edir. Bəlkə də bunlardan ən vacibi parallelSort()çox nüvəli maşınlarda çeşidlənməni xeyli sürətləndirə bilər. Aşağıdakı kiçik nümunə yeni metodlar ailəsini ( parallelXxx) fəaliyyətdə nümayiş etdirir.
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();
    }
}
Bu kiçik kod parçası parallelSetAll()massivi 20.000 təsadüfi dəyərlə doldurmaq üçün bir üsuldan istifadə edir. Bundan sonra tətbiq olunur parallelSort(). Proqram massivin həqiqətən çeşidləndiyini göstərmək üçün çeşidləmədən əvvəl və sonra ilk 10 elementi çap edir. Nümunə proqram çıxışı belə görünə bilər (Qeyd edək ki, massivin elementləri təsadüfidir).
Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378
Sorted: 39 220 263 268 325 607 655 678 723 793

5.7. Paralellik

java.util.concurrent.ConcurrentHashMapYeni əlavə edilmiş axın obyektləri və lambda ifadələri əsasında ümumi əməliyyatları dəstəkləmək üçün sinfə yeni metodlar əlavə edilmişdir . Həmçinin, paylaşılan birləşməni dəstəkləmək üçün sinfə yeni metodlar əlavə edilmişdir java.util.concurrent.ForkJoinPool(həmçinin Java paralelliyi üzrə pulsuz kursumuza baxın ). Oxuma/yazma nəzarəti üçün üç giriş rejimi ilə qabiliyyətə əsaslanan kilidləməni təmin etmək üçün yeni sinif java.util.concurrent.locks.StampedLockəlavə edildi (o qədər də yaxşı olmayana daha yaxşı alternativ hesab edilə bilər java.util.concurrent.locks.ReadWriteLock). Paketə əlavə olunan yeni siniflər java.util.concurrent.atomic:
  • İkiqat akkumulyator
  • DoubleAdder
  • Uzun akkumulyator
  • LongAdder

6. Java iş vaxtı mühitində (JVM) yeni funksiyalar

Sahə PermGenləğv edildi və Metaspace (JEP 122) ilə əvəz olundu. JVM variantları -XX:PermSizevə müvafiq olaraq və -XX:MaxPermSizeilə əvəz edilmişdir . -XX:MetaSpaceSize-XX:MaxMetaspaceSize

7. Nəticə

Gələcək buradadır: Java 8, tərtibatçıların daha məhsuldar olmasına imkan verən funksiyalar təqdim edərək platformasını irəliyə apardı. İstehsal sistemlərini Java 8-ə köçürmək hələ tezdir, lakin növbəti bir neçə ay ərzində qəbuletmə yavaş-yavaş artmağa başlamalıdır. Bununla belə, indi kod bazanızı Java 8 uyğunluğu üçün hazırlamağa başlamaq və kifayət qədər təhlükəsiz və sabit olduqda Java 8 dəyişikliklərini daxil etməyə hazır olmaq vaxtıdır. Cəmiyyətin Java 8-i qəbul etməsinin sübutu olaraq Pivotal bu yaxınlarda Java 8 üçün istehsal dəstəyi ilə Spring Framework-ü buraxdı . Java 8-də maraqlı yeni funksiyalar haqqında öz məlumatınızı şərhlərdə verə bilərsiniz.

8. Mənbələr

Java 8 xüsusiyyətlərinin müxtəlif aspektlərini ətraflı müzakirə edən bəzi əlavə mənbələr:
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION