서문 : Java 8은 한동안 공개되었으며 모든 징후는 Java 8이 분포의 주요 변화임을 나타냅니다. Java Code Geeks에는 Java 8 Lambdas 및 동시성, Java 8 Date Time API 튜토리얼 : LocalDateTime 및 Abstract 클래스 대 JDK 8 ERA와 같은 Java 8의 새로운 기능을 소개하는 Java Code Geeks에 이미 많은 기사가 있습니다. 이 기사는 다음과 같은 다른 정보를 말합니다. 15는 Java 8 튜토리얼과 Java 8의 어두운면을 읽어야합니다.이 기사는 위의 정보를 편집하여 Java 8의 새로운 기능에 대한 참조 교과서에 편집했습니다. 나는 당신이 무언가를 얻을 수 있기를 바랍니다.
1. 소개
Java 8이 Java 5 (2004 년에 출시) 이후 Java의 가장 중요한 버전이라는 것은 의심의 여지가 없습니다. 이 버전에는 언어, 컴파일러, 라이브러리, 도구 및 JVM의 12 가지가 넘는 새로운 기능이 포함되어 있습니다. 이 기사에서는 이러한 새로운 기능을 배우고 실제 예제를 사용하여 사용하기에 적합한 시나리오를 설명합니다.
이 튜토리얼에는 Java 개발자가 종종 직면하는 몇 가지 유형의 문제가 포함되어 있습니다.
언어
컴파일러
도서관
도구
런타임 (JVM)
2. 자바 언어의 새로운 기능
Java 8은 Java의 주요 버전입니다. 어떤 사람들은 Java 개발자가 이러한 새로운 기능을 기대하지만 학습하려면 많은 노력이 필요하다고 생각합니다. 이 섹션에서는 Java 8의 새로운 기능의 대부분을 소개합니다.
2.1 람다 표현 및 기능적 인터페이스
Lambda 표현식 (클로저라고도 함)은 Java 8에서 가장 크고 가장 예상되는 언어 변경 사항입니다. 기능을 메소드로 매개 변수로 전달하거나 코드 자체를 데이터로 처리 할 수 있습니다. 기능 개발자는 이러한 개념에 매우 익숙합니다. JVM 플랫폼 (Groovy, Scala 등)의 많은 언어는 출생 이후 Lambda 표현을 지원했지만 Java 개발자는 Lambda 표현 대신 익명의 내부 클래스를 사용할 수밖에 없습니다.
Lambda의 디자인에는 많은 시간과 많은 지역 사회 노력이 필요했으며, 단순하고 소형 언어 구조를 달성 할 수있는 타협 구현 솔루션을 찾았습니다. 가장 간단한 람다 표현식은 쉼표로 구분 된 매개 변수 목록, -> 기호 및 명령문 블록으로 구성 될 수 있습니다.
arrays.aslist ( "a", "b", "d") .foreach (e-> system.out.println (e));
위 코드에서 매개 변수 E 유형은 컴파일러 추론에 의해 도출되며, 예를 들어 매개 변수 유형을 명시 적으로 지정할 수도 있습니다.
arrays.aslist ( "a", "b", "d") .foreach ((String e) -> system.out.println (e));
Lambda 표현에보다 복잡한 문장 블록이 필요한 경우 Java의 기능 본문과 유사한 Curly Braces와 함께 명령문 블록을 동봉 할 수 있습니다.
arrays.aslist ( "a", "b", "d") .foreach (e-> {system.out.print (e); system.out.print (e);});Lambda 표현식은 클래스 멤버 및 로컬 변수 (이 변수를 최종으로 암시 적으로 변환 함)를 참조 할 수 있습니다. 예를 들어, 다음 두 코드 블록은 정확히 동일한 효과를 갖습니다.
문자열 분리기 = ","; arrays.aslist ( "a", "b", "d") .foreach ((String e) -> system.out.print (e + separator));
그리고
최종 문자열 분리기 = ","; arrays.aslist ( "a", "b", "d") .foreach ((String e) -> system.out.print (e + separator));
Lambda 표현식에는 반환 값이 있으며, 반환 값의 유형은 또한 컴파일러 추론에 의해 도출된다. Lambda 표현식의 명령문 블록에 한 줄만있는 경우 return 문을 사용할 필요가 없습니다. 다음 두 코드 스 니펫은 동일한 효과를 갖습니다.
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 result;});Lambda Designers는 기존 기능을 Lambda Expressions와 잘 호환하기 위해 많은 방법을 고려하여 기능 인터페이스의 개념을 고안했습니다. 함수 인터페이스는 하나의 함수 만있는 인터페이스를 나타냅니다. 이러한 인터페이스는 람다 표현식으로 암시 적으로 변환 될 수 있습니다. java.lang.runnable 및 java.util.concurrent.callable은 기능적 인터페이스의 가장 좋은 예입니다. 실제로 기능 인터페이스는 매우 깨지기 쉽습니다. 개발자가 인터페이스에 함수를 추가하는 한 인터페이스가 더 이상 기능 인터페이스가 아니기 때문에 컴파일 실패가 발생합니다. 이 코드 레벨 취약점을 극복하고 인터페이스가 기능 인터페이스임을 명시 적으로 명시하기 위해 Java 8은 기능적 인터페이스의 간단한 정의를 제공하기 위해 특수 주석 @functionalinterface (Java 라이브러리의 모든 관련 인터페이스에 이미이 주석이 있습니다)를 제공합니다.
@functionalInterface public 인터페이스 기능 {void method ();}그러나 주목해야 할 것은 기본 메소드와 정적 메소드가 기능 인터페이스의 정의를 파괴하지 않으므로 다음 코드는 합법적이라는 것입니다.
@functionalInterface public interface functionalDefaultMethods {void method (); 기본 void defaultMethod () {}}Java 8의 가장 큰 판매 지점 인 Lambda Expressions는 더 많은 개발자가 JVM 플랫폼에 가입하고 순수한 Java 프로그래밍에서 기능 프로그래밍 개념을 사용할 수있는 잠재력을 가지고 있습니다. Lambda Expressions에 대해 더 알아야 할 경우 공식 문서를 참조하십시오.
2.2 인터페이스의 기본 및 정적 방법
Java 8은 두 가지 새로운 개념을 사용하여 인터페이스의 의미를 확장합니다 : 기본 메소드 및 정적 메소드. 기본 메소드는 인터페이스를 특성과 약간 비슷하지만 달성 할 목표는 다릅니다. 기본 메소드를 사용하면 개발자가 이진 호환성을 깨지 않고 기존 인터페이스에 새로운 메소드를 추가 할 수 있습니다. 즉, 새로 추가 된 메소드를 동시에 구현하도록 인터페이스를 구현하는 클래스를 강요하지 않습니다.
기본 메소드와 추상 방법의 차이점은 추상 방법을 구현해야하지만 기본 메소드는 그렇지 않다는 것입니다. 인터페이스가 제공하는 기본 메소드는 인터페이스 구현 클래스에서 상속 또는 덮어 쓰기됩니다. 예제 코드는 다음과 같습니다.
개인 인터페이스 기본값 {// 인터페이스는 이제 기본 메소드를 허용하면 구현자가 //를 구현하지 않을 수도 있습니다. 기본 문자열 notrequired () {return "default 구현"; }} private static class defaultableimpl은 defaulable {} private static class reveridableimpl empaulable {@override public string notrequired () {return "retainedden 구현";}}기본 인터페이스는 키워드 기본값을 사용하여 기본 메소드 NotRequired ()를 정의합니다. DefaultableIMPL 클래스는이 인터페이스를 구현하고 기본적 으로이 인터페이스의 기본 메소드를 상속합니다. OverridableIMPL 클래스는 또한이 인터페이스를 구현하지만 인터페이스의 기본 메소드를 대체하고 다른 구현을 제공합니다.
Java 8이 가져온 또 다른 흥미로운 기능은 정적 메소드가 인터페이스에서 정의 될 수 있다는 것입니다. 예제 코드는 다음과 같습니다.
개인 인터페이스 defaulableFactory {// 인터페이스는 이제 정적 메소드를 허용합니다. 정적 기본화 가능 만들기 (공급 업체 <defaulable> supplier) {return supplier.get ();}}다음 코드 스 니펫은 기본 메소드와 정적 메소드의 사용 시나리오를 통합합니다.
public static void main (string [] args) {defaulaable default = defaulableFactory.create (defaultableImpl :: new); System.out.println (defaultable.notRequired ()); defaulable actory.create (retainiMpl :: new);};};이 코드의 출력은 다음과 같습니다.
기본 구현
우선 구현
JVM에서 기본 메소드를 구현하면 바이트 코드 수준에서 지원을 제공하므로 매우 효율적입니다. 기본 메소드는 기존 상속 시스템을 깨지 않고 개선 된 인터페이스를 허용합니다. 공식 라이브러리 에서이 기능을 적용하는 것은 다음과 같습니다. java.util.collection interface (예 : stream), parallelstream (), foreach () 및 removeif () 등과 같은 새로운 메소드를 추가하십시오.
기본 방법에는 많은 이점이 있지만 실제 개발에주의해서 사용해야합니다. 복잡한 상속 시스템에서는 기본 메소드가 모호성과 컴파일 오류를 유발할 수 있습니다. 자세한 내용을 알고 싶다면 공식 문서를 참조하십시오.
2.3 메소드 참조
메소드 참조를 사용하면 개발자가 기존 메소드, Java 클래스 생성자 또는 인스턴스 객체를 직접 참조 할 수 있습니다. 메소드 참조 및 람다 표현식은 서로 함께 사용되므로 복잡한 템플릿 코드가없는 많은 템플릿 코드없이 Java 클래스의 생성자가 컴팩트하고 간결하게 보이게 만듭니다.
Simon의 예에서, 자동차 클래스는 다른 방법 참조의 예이며, 독자는 네 가지 유형의 메소드 참조를 구별하는 데 도움이 될 수 있습니다.
공개 정적 클래스 카 {공공 정적 자동차 제작 (최종 공급 업체 <CAR> 공급 업체) {return Supplier.get ();} 공개 정적 무효 동료 (최종 자동차) {System.out.println ( "Collided" + Car.tostring ());} public void follow (Final Car) {system.out.println ( ") {System.out.println ( "수리" + this.toString ());}}첫 번째 방법에 대한 참조 유형은 생성자 참조이며 구문은 클래스 :: new 또는 더 일반적인 형태입니다 : class <t> :: new. 참고 :이 생성자에는 매개 변수가 없습니다.
최종 자동차 자동차 = Car.Create (CAR :: NEW); 최종 목록 <car> cars = arrays.aslist (car);
두 번째 방법의 참조 유형은 정적 메소드 참조이며 구문은 class :: static_method입니다. 참고 :이 메소드는 자동차 유형 매개 변수를 허용합니다.
cars.foreach (car :: 충돌);
세 번째 방법은 유형을 참조하여 특정 클래스의 멤버 메소드에 대한 참조이며 구문은 클래스 :: 메소드입니다. 이 메소드는 매개 변수를 정의하지 않습니다.
cars.foreach (car :: 수리);
네 번째 방법 참조 유형은 인스턴스 객체의 멤버 메소드에 대한 참조이며 구문은 인스턴스 :: 메소드입니다. 참고 :이 메소드는 자동차 유형 매개 변수를 허용합니다.
Final Car Police = Car.create (Car :: New); Cars.Foreach (Police :: Follow);
위의 예제를 실행하면 콘솔에서 다음 출력을 볼 수 있습니다 (자동차 인스턴스가 다를 수 있음).
collided com.javacodegeeks.java8.method.references.methodreferences$ car@7a81197d com.javacodegeeks.java8.java8.references.methodreferences excar@7a81197d 다음 com.javacodegeeks.java8.method.references.methodreferences$CAR@7A81197D
자세한 내용을 이해하고 배우려면 공식 문서를 참조하십시오.
2.4 댓글을 반복하십시오
Java 5에 주석이 도입 된 이후,이 기능은 매우 인기가 있으며 다양한 프레임 워크 및 프로젝트에서 널리 사용되었습니다. 그러나 주석은 큰 제한을 가지고 있습니다. 같은 장소에서 동일한 주석을 여러 번 사용할 수 없습니다. Java 8 은이 한계를 깨뜨리고 반복 주석의 개념을 소개하여 동일한 주석을 동일한 장소에서 여러 번 사용할 수 있습니다.
@repeatable 주석을 사용하여 Java 8에서 반복 주석을 정의하는 것은 실제로 언어 수준의 개선이 아니라 컴파일러가 만든 트릭이며 기본 기술은 여전히 동일합니다. 다음 코드를 사용하여 설명 할 수 있습니다.
package com.javacodegeeks.java8.repeatable.annotations; import java.lang.annotation.elementtype; java.lang.annotation.repeatable import; java.lang.annotation.trention import; java.lang.annotation.retentionpolicy import; Java.lang.annotation.target import; public class repeatingAnnotations {@target (elementtype.type) @retention (retentionpolicy.runtime) public @interface filters {filter [] value ()} @target (eleMpertype.type) @reretention (rendentpolicy.runtime) @repeatable (filters.class) public {string valler (class); @Filter ( "Filter1") @Filter ( "Filter2") public Interface Filterable {} public static void main (string [] args) {for (필터 필터 : 필터링 가능.class.getAntationSype (filter.class)) {system.out.println (filter.value ());}}}}}}}}}}우리가 볼 수 있듯이, 여기의 필터 클래스는 @repeatable (filters.class) 주석을 사용하며 필터는 필터 주석을 저장하는 컨테이너입니다. 컴파일러는 개발자로부터 이러한 세부 사항을 차단하려고합니다. 이러한 방식으로, 필터링 가능한 인터페이스에 두 개의 필터 주석으로 주석을 달 수 있습니다 (여기에는 필터에 대한 정보가 언급되지 않음).
또한 반사 API는 새로운 방법을 제공합니다. getAnnotationsBytype ()는 특정 유형의 중복 주석을 반환 할 수 있습니다.
Filterable.class.getAnnoation (Filters.class)은 두 개의 필터 인스턴스를 반환하고 콘솔에 컨텐츠 출력은 다음과 같습니다.
필터 1
필터 2
더 알고 싶다면 공식 문서를 참조하십시오.
2.5 더 나은 유형 추론
Java 8 컴파일러는 유형 추론을 크게 향상 시켰습니다. 많은 시나리오에서 컴파일러는 특정 매개 변수의 데이터 유형을 추론하여 코드를보다 간결하게 만들 수 있습니다. 예제 코드는 다음과 같습니다.
패키지 com.javacodegeeks.java8.type.inference; public class value <t> {public static <t> t defaultValue () {return null; } public t getOrdefault (t 값, t defaultValue) {return (value! = null)? 값 : DefaultValue;}}다음 코드는 유형 값 <string>의 응용 프로그램입니다.
패키지 com.javacodegeeks.java8.type.inference; public class acepinference {public static void main (String [] args) {최종 값 <문자열> value = new Value <> (); value.getordefault ( "22", value.defaultValue ());}}매개 변수 값의 유형 .defaultValue ()는 컴파일러에 의해 도출되며 명시 적으로 지정할 필요가 없습니다. Java 7에서는이 코드에 값이없는 한이 코드에 컴파일 오류가 발생합니다. <string> defaultValue ()가 사용됩니다.
2.6 주석의 응용 프로그램 시나리오를 넓 힙니다
Java 8은 주석의 응용 프로그램 시나리오를 확대합니다. 이제 주석은 로컬 변수, 인터페이스 유형, 슈퍼 클래스 및 인터페이스 구현 클래스, 심지어 함수의 예외 정의와 같은 거의 모든 요소에서 사용될 수 있습니다. 몇 가지 예는 다음과 같습니다.
패키지 com.javacodegeeks.java8.annotations; import java.lang.annotation.elementtype; java.lang.annotation.trention import; java.lang.annotation.retentionpolicy import; Java.lang.annotation.target import; java.util.arraylist 가져 오기; java.util.collection import; 공개 클래스 주석 {@retention (rendentionpolicy.runtime) @target ({elementtype.type_use, elementtype.type_parameter}) public @interface nonempty {} public static class holder <@nonempty t> public void method () @nonempty} @s} @s} @s} @suboid void method () "사용되지 않은") public static void main (String [] args) {Final Holder <string> holder = new @nonempty holder <string> (); @nonempty collection <@nonempty String> strings = new ArrayList <> (); }}elementtype.type_user 및 elementtype.type_parameter는 주석의 사용 시나리오를 설명하기 위해 Java 8에 추가 된 두 가지 새로운 주석입니다. 자바 언어
Yan은 또한 새로 추가 된 메모를 식별하기 위해 해당 변경을 수행했습니다.
3. Java 컴파일러의 새로운 기능
3.1 매개 변수 이름
런타임에 Java 프로그램에서 메소드의 매개 변수 이름을 얻으려면 이전 세대의 Java 프로그래머는 Paranamer Liberary와 같은 다른 방법을 사용해야합니다. Java 8은 최종적 으로이 기능을 언어 레벨 (반사 API 및 Parameter.getName () 메소드 사용) 및 바이트 코드 레벨 (새로운 Javac 컴파일러 및 -Parameters 매개 변수 사용)에서 지원 하여이 기능을 정규화합니다.
패키지 com.javacodegeeks.java8.parameter.names; import java.lang.reflect.method; import java.lang.reflect.parameter; public class parameternames {public static void main (String [] args)은 예외 {method method = parameternames.class.getMethod ( "main", String []. class); for (final parameter parameter : method.getParameters ()) {System.out.println ( "매개 변수 :" + parameter.getName ());}}}Java 8에서는이 기능이 기본적으로 꺼져 있으므로 -Parameters 매개 변수없이 위의 코드를 컴파일하면 다음 결과가 출력됩니다.
매개 변수 : Arg0
-Parameters 매개 변수를 사용하면 다음 결과가 출력됩니다 (올바른 결과).
매개 변수 : args
프로젝트 관리에 Maven을 사용하는 경우 Maven-Compiler-Plugin 컴파일러 구성 항목에서 -Parameters 매개 변수를 구성 할 수 있습니다.
<Plugin> <groupId> org.apache.maven.plugins </groupid> <ArtifactId> Maven-Compiler-Plugin </artifactid> <bersion> 3.1 </version> <confilerargument> --parameters </compilerargument> <source> </source> </target> 1.8 </sport>>
4. Java 공식 도서관의 새로운 기능
Java 8은 현대 동시 프로그래밍, 기능 프로그래밍 등을 지원하기 위해 많은 새로운 도구 클래스 (날짜/시간 클래스)와 확장 기존 도구 클래스를 추가했습니다.
4.1 선택 사항
Java 응용 프로그램에서 가장 일반적인 버그는 Null 값 예외입니다. Java 8 이전에 Google Guava는 NullPointerException을 해결하기 위해 옵션 클래스를 도입하여 다양한 NULL 검사로 오염되는 소스 코드를 피하여 개발자가 클리너 코드를 작성할 수 있습니다. Java 8은 또한 공식 도서관에 선택 사항을 추가합니다.
선택 사항은 쉬운 일입니다. T 또는 NULL의 값을 저장하십시오. 명백한 널 확인을 피하기 위해 유용한 인터페이스를 제공하며 자세한 내용은 공식 Java 8 문서를 참조 할 수 있습니다.
다음으로 선택 사항을 사용하는 몇 가지 예 : 비어있을 수있는 값 또는 특정 유형의 값을 살펴 보겠습니다.
옵션 <string> fullName = 옵션 .ofNullable (null); System.out.println ( "full name is set?" + fullName.ispresent ()); System.out.println ( "전체 이름 :" + fullName.orelSeget (() -> "[none]"); System.out.println (fullName.map (s-> "Hey" + S + "!") .Orelse ( "Hey Stranger!"));
옵션 인스턴스가 널 값이 아닌 값을 갖는 경우 ispresent () 메소드는 true를 반환하고 그렇지 않으면 false를 반환합니다. orelseget () 메소드 및 옵션 인스턴스는 NULL을 유지하며 Lambda 표현식으로 생성 된 기본값을 허용 할 수 있습니다. Map () 메소드는 기존 선택 인스턴스의 값을 새 값으로 변환 할 수 있습니다. Orelse () 메소드는 orelseget () 메소드와 유사하지만 NULL을 잡을 때 전달 된 기본값을 반환합니다.
위 코드의 출력 결과는 다음과 같습니다.
전체 이름이 설정 되었습니까? 잘못된 이름 : [없음] Hey Stranger!
또 다른 간단한 예를 살펴 보겠습니다.
옵션 <string> firstName = Optional.of ( "Tom"); System.out.println ( "이름이 set?" + firstName.ispresent ()); System.out.println ( "이름 :" + firstName.OrelSeget (() -> "[none]"); System.out.println (FirstName.map (S-> "Hey" + S + "!") .Orelse ( "Hey Stranger!")); System.out.println ();
이 예제의 출력은 다음과 같습니다.
이름이 설정 되었습니까? 진정한 이름 : Tom Hey Tom!
자세한 내용을 알고 싶다면 공식 문서를 참조하십시오.
4.2 스트림
새로 추가 된 스트림 API (java.util.stream)는 생성 된 환경의 기능적 프로그래밍을 Java 라이브러리에 소개합니다. 이는 개발자가보다 효율적이고 간결하며 컴팩트 한 코드를 작성할 수 있도록 Java 라이브러리의 가장 큰 개선입니다.
Steam API는 수집 작업을 크게 단순화합니다 (나중에 컬렉션 이상을 볼 수 있습니다). 먼저,이 클래스라는 작업을 살펴 보겠습니다.
공개 클래스 스트림 {private enum status {open, closed}; 개인 정적 최종 클래스 작업 {개인 최종 상태 상태; 개인 최종 정수 포인트; 작업 (최종 상태 상태, 최종 정수 포인트) {this.status = 상태; this.points = points;} public integer getPoints () {return points;} public status getStatus () {return status;} @override public string () {return string.format ( "[ %s, %d]", 상태, 포인트);}}}작업 클래스에는 분수 개념 (또는 의사 복잡성)이 있으며 개방 또는 닫는 두 가지 상태가 있습니다. 이제 작업 컬렉션이 있다고 가정합니다.
최종 컬렉션 <asking> tasks = arrays.aslist (new task (status.open, 5), new task (status.open, 13), new task (status.closed, 8));
먼저 질문을 살펴 보겠습니다.이 작업 컬렉션에는 몇 개의 오픈 스테이트 포인트가 있습니까? Java 8 이전 에이 문제를 해결하려면 Foreach를 사용하여 작업 컬렉션을 루프해야합니다. 그러나 Java 8에서는 증기를 사용하여이를 해결할 수 있습니다. 일련의 요소 목록을 포함하고 순차적이고 병렬 처리를 지원하십시오.
// sum () 최종 긴 총 포인트를 사용하여 모든 활성 작업의 총점을 계산합니다. tatks.stream (). filter (task-> task.getStatus () == status.open) .maptoint (task :: getPoints) .sum (); System.out.println ( " + TotalPointSfopOnasks);
이 방법을 실행하기위한 콘솔 출력은 다음과 같습니다.
총점 : 18
여기서 이야기할만한 지식 포인트가 많이 있습니다. 먼저, 작업 세트는 증기 표현으로 변환됩니다. 둘째, 증기의 필터 작동은 모든 닫힌 작업을 필터링합니다. 셋째, maptoint 조작은 작업 스트림을 task :: 각 작업 인스턴스의 getpoints 메소드를 기반으로 정수 컬렉션으로 변환합니다. 마지막으로, 합은 최종 결과를 얻기 위해 합계에 의해 계산됩니다.
다음 예제를 배우기 전에 Steam에 대한 몇 가지 지식을 기억해야합니다 (자세한 내용은 여기를 클릭하십시오). Steam 위의 작업은 중간 작업 및 후기 작업으로 나눌 수 있습니다.
중간 작업은 새로운 증기를 반환합니다. 중간 작업 (필터 등)을 수행하면 실제 필터링 작업이 수행되지 않지만 새 증기를 만들고 원래 증기의 조건을 충족하는 요소를 새로 생성 된 증기에 넣습니다.
후기 작업 (예 : Foreach 또는 Sum)은 증기를 가로 지르고 결과 또는 동반 결과를 얻습니다. 늦은 작업을 수행 한 후에는 증기 처리 라인이 처리되었으며 사용할 수 없습니다. 거의 모든 경우에, 늦은 작업은 즉시 증기를 가로 지르고 있습니다.
Steam의 또 다른 가치는 병렬 처리에 대한 창의적 지원입니다. 위의 작업 수집의 경우 다음 코드를 사용하여 모든 작업의 점수를 계산할 수 있습니다.
// 모든 작업의 총점 계산 최종 이중 총 포인트 = tasks.stream (). parallel (). map (task-> task.getPoints ()) // 또는 map (task :: getPoints) .reduce (0, integer :: sum); System.out.println ( "Total Points (모든 작업) :" + TotalPoints);
여기서 병렬 메소드를 사용하여 모든 작업을 병렬로 처리하고 감소 방법을 사용하여 최종 결과를 계산합니다. 콘솔 출력은 다음과 같습니다.
총점 (모든 작업) : 26.0
컬렉션의 경우 특정 조건에 따라 요소를 그룹화해야합니다. 이 유형의 작업은 Steam에서 제공 한 API를 사용하여 빠르게 완료 할 수 있습니다. 코드는 다음과 같습니다.
// 상태 최종 맵 <상태에 따라 그룹 작업 <상태, list <asking>> map = tasks.stream (). collect (collectors.groupingby (task :: getStatus)); system.out.println (map);
콘솔의 출력은 다음과 같습니다.
{closed = [[Closed, 8]], Open = [[Open, 5], [Open, 13]]}}
작업 컬렉션에 대한 마지막 예제는 다음과 같습니다. 컬렉션의 컬렉션에서 각 작업의 포인트 비율을 계산하는 방법. 특정 처리 코드는 다음과 같습니다.
// 각 작업의 중량을 계산합니다 (전체 포인트의 백분율) 최종 컬렉션 <String> result = tasks.stream () // stream <string> .maptoint (task :: getPoints) // intstream .aslongstream () // longstream .maptodouble ) // longstream .maptoobj (백분율 -> 백분율 + "%") // stream <string> .collect (collectors.tolist ()); // list <string> system.out.println (결과);
콘솔 출력 결과는 다음과 같습니다.
[19%, 50%, 30%]
마지막으로, 앞에서 언급했듯이 Steam API는 Java 컬렉션에서 작용할 수있을뿐만 아니라 전통적인 IO 작업 (파일 또는 네트워크 라인의 데이터 읽기)은 Steam 처리의 혜택을 누릴 수 있습니다. 다음은 작은 예입니다.
최종 경로 경로 = 새 파일 (filename) .topath (); try (stream <string> lines.lines (Path, Standardcharsets.utf_8)) {lines.onclose (() -> system.out.println ( "done!")) .foreach (system.out :: println);}스트림 메소드 onclose는 추가 핸들이있는 동등한 스트림을 반환합니다. 이 핸들은 스트림의 Close () 메소드가 호출되면 실행됩니다. 스트림 API, Lambda 표현식 및 인터페이스 기본 메소드 및 정적 메소드에서 지원하는 메소드 참조는 소프트웨어 개발의 현대 패러다임에 대한 Java 8의 응답입니다.
4.3 날짜/시간 API (JSR 310)
Java 8은 시간 및 날짜의 처리를 개선하기 위해 새로운 날짜 시간 API (JSR 310)를 소개합니다. 시간과 날짜 관리는 항상 Java 개발자에게 가장 고통스러운 문제였습니다. java.util.date 및 이후 java.util.calendar는이 문제를 해결하지 못했습니다 (개발자들에 의해 더 혼란스러워).
위의 이유로 인해 타사 도서관 조다 타임이 탄생하여 Java의 Time Management API를 대체 할 수 있습니다. Java 8의 새로운 Time and Date Management API는 Joda Time의 영향을 많이받으며 Joda Time의 많은 본질을 흡수했습니다. 새로운 Java.Time 패키지에는 날짜, 시간, 시간대, 즉석 (날짜와 유사하지만 나노초와 정확히), 지속 시간 (지속 시간) 및 시계 작업에 대한 모든 클래스가 포함되어 있습니다. 새로 설계된 API는 이러한 클래스의 불변 (java.util.calendar에서 배운 강의)을 심각하게 고려하고 인스턴스를 수정 해야하는 경우 새 개체를 반환합니다.
Java.Time 패키지의 주요 클래스와 각각의 사용 예제를 살펴 보겠습니다. 첫째, 클럭 클래스는 시간대를 사용하여 현재 나노초 시간과 날짜를 반환합니다. 클럭은 System.CurrentTimeMillis () 및 TimeZone.getDefault ()를 대체 할 수 있습니다.
// 시스템 클록을 UTC 오프셋으로 오프셋으로 가져옵니다. 최종 클럭 시계 = clock.systemutc (); system.out.println (clock.instant ()); system.out.println (clock.millis ());
이 예제의 출력은 다음과 같습니다.
2014-04-12T15 : 19 : 29.282Z 1397315969360
둘째, LocalDate 및 LocalTime 수업에 중점을 둡니다. LocalDate는 ISO-8601 캘린더 시스템의 날짜 부분 만 포함합니다. LocalTime에는 캘린더 시스템의 시간 부분 만 포함됩니다. 두 클래스의 객체는 클럭 객체를 사용하여 구축 할 수 있습니다.
// 현지 날짜 및 현지 시간 최종 시간을 가져옵니다. LocalDate.now (); 최종 LocalDate DatefromClock = localDate.now (Clock); System.out.println (DateFromClock); // 로컬 날짜 및 로컬 타임 최종 시간을 가져옵니다. localtime 시간 = localtime.now (); 최종 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 클래스에는 LocalDate 및 LocalTime에 대한 정보가 포함되어 있지만 ISO-8601 캘린더 시스템에는 시간대 정보가 포함되어 있지 않습니다. LocalDate 및 LocalTime에 대한 몇 가지 예는 다음과 같습니다.
// 로컬 날짜/시간 마지막 LocalDateTime DateTime = localDateTime.now (); 최종 LocalDateTime DateTimeFromClock = LocalDateTime.now (클록); System.out.println (dateTime); System.out.println (DateTimeFromClock);
위의 예제의 출력 결과는 다음과 같습니다.
2014-04-12T11 : 37 : 52.309 2014-04-12T15 : 37 : 52.309
특정 시간대에 데이터/시간 정보가 필요한 경우 ISO-8601 날짜 시스템의 날짜와 시간을 보유하고 시간대 정보가있는 ZoneDateTime을 사용할 수 있습니다. 다음은 다른 시간대를 사용하는 몇 가지 예입니다.
// ZONED 날짜/시간 최종 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 [America/New_York] 2014-04-12T15 : 47 : 01.017Z 2014-04-12T08 : 47 : 01.017-07 : 00 [America/Los_angeles]
마지막으로, 시간을 몇 초로, 나노초로 유지하는 지속 시간 클래스를 살펴 보겠습니다. 이렇게하면 두 날짜의 차이를 쉽게 계산할 수 있습니다. 예제 코드는 다음과 같습니다.
// 두 날짜 사이의 기간을 얻는다. duration.todays ()); system.out.println ( "시간 시간 :" + duration.tohours ());
이 예제는 2014 년 4 월 16 일과 2015 년 4 월 16 일 사이의 일과 시간을 계산하는 데 사용되며 출력은 다음과 같습니다.
기간의 기간 : 시간 365 시간 시간 : 8783
Java 8의 새로운 날짜와 시간에 대한 전반적인 인상은 부분적으로 Joda Time의 긍정적 인 영향으로 인해 비교적 긍정적이며, 공무원이 마침내 개발자의 요구를 듣기 때문입니다. 자세한 내용을 알고 싶다면 공식 문서를 참조하십시오.
4.4 Nashorn JavaScript 엔진
Java 8은 새로운 Nashorn JavaScript 엔진을 제공하여 JVM에서 JS 응용 프로그램을 개발하고 실행할 수 있습니다. Nashorn JavaScript 엔진은 Javax.script.scriptengine의 또 다른 구현 버전입니다. 이 유형의 스크립트 엔진은 동일한 규칙을 따르며 Java 및 JavaScript를 대화식으로 사용할 수 있습니다. 예제 코드는 다음과 같습니다.
ScriptengeManager Manager = New ScriptengineManager (); Scriptengeine Engine = Manager.GetEngineByName ( "javaScript"); System.out.println (Engine.getClass (). getName ()); System.out.println ( "result :" + Engine.eval ( "function f () {return 1;});이 코드의 출력은 다음과 같습니다.
jdk.nashorn.api.scripting.nashornscriptengine 결과 : 2
4.5 Base64
Base64 인코딩에 대한 지원은 공식 Java 8 라이브러리에 추가되어 Base64 인코딩을 타사 라이브러리를 사용하지 않고 수행 할 수 있습니다. 예제 코드는 다음과 같습니다.
패키지 com.javacodegeeks.java8.base64; java.nio.charset.standardcharsets 가져 오기; java.util.base64 가져 오기; Public Class Base64S {public static void main (String [] args) {Final String Text = "Base64 마지막으로 Java 8에서!"; 최종 문자열 encoded = base64.getEncoder (). encodetoString (text.getBytes (Standardcharsets.utf_8)); System.out.println (인코딩); 최종 문자열 decoded = new String (base64.getDecoder (). decode (encoded), 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 ()로, 멀티 코어 머신의 배열 정렬 속도를 크게 높일 수 있습니다. 다음 예제는 ParallelExxxx 시리즈의 방법을 보여줍니다.
package com.javacodegeeks.java8.parallel.arrays; import java.util.arrays; import java.util.concurrent.threadlocalrandom; 공개 클래스 병렬 구조 {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. 결론
通过为开发者提供很多能够提高生产力的特性,Java 8使得Java平台前进了一大步。现在还不太适合将Java 8应用在生产系统中,但是在之后的几个月中Java 8的应用率一定会逐步提高(PS:原文时间是2014年5月9日,现在在很多公司Java 8已经成为主流,我司由于体量太大,现在也在一点点上Java 8,虽然慢但是好歹在升级了)。作为开发者,现在应该学习一些Java 8的知识,为升级做好准备。