자바 9에서 추가된 기능들
제 블로그는 제가 공부한 내용들을 기록하고 나중에 기억이 안날때를 대비해 제가 보기 위한 용도로 작성하고 있습니다.
그럼에도 불구하고 구독해주신분들이 계셔서 감사,,
1. 확장된 try-with-resources
개발자가 직접 닫아줘야했던 자원을 try( ) 안에 선언하여 try 로직이 끝날때 자동으로 해당 자원을 닫는 기능이다.
이때 자원은 AutoCloseable 구현하고 있어야 함
try (Resource resource = new Resource()) {
// job
}
자바 8까지는 try() 바깥에서 선언한 변수에 대해서는 try-with-resources 구문을 사용할 수 없었지만 자바9 부터 final 혹은 초기화한 이후 값이 변경되지 않은 변수인 경우에 try() 안에 변수 이름을 한번 더 쓰는 것으로 사용할 수 있다.
2. @SafeVarargs 어노테이션 + private 메소드
자바9 이전에는 private 메소드와 함께 사용할 수 없었는데, 메소드를 호출할 때 매개변수의 개수를 자유롭게 넣을 수 있고 해당 값들이 담겨있는 배열이 생성된다.
public class Main {
@SafeVarargs
public static <T> List<T> flatten(List<T> ...lists) {
Object[] obj = lists;
obj[0] = Arrays.asList("Hello, World!");
List<T> result = new ArrayList<>();
for (List<T> list : lists) {
result.addAll(list);
}
return result;
}
public static void main(String[] args) {
List<Integer> list1 = Arrays.asList(1, 2, 3);
List<Integer> list2 = Arrays.asList(4, 5, 6);
List<Integer> result = flatten(list1, list2);
System.out.println(result);
}
}
이 코드가 나는 제네릭인데 T 가 Integer 타입으로 들어가서 String 타입의 리스트 추가는 런타임오류로 잡힐거라 생각했는데, 예상 외로 동작하고 문자열과 숫자가 모두 들어간 배열이 출력되서 충격이었다.
이런 식으로 코드를 작성할 수 있다보니 어노테이션 없이 해당 코드를 실행하면 안전하지 않은 코드를 실행한다는 warning 이 출력된다.
이때 문제가 생기지 않는 코드를 작성했다면 해당 warn 메시지가 뜨지 않도록 @SafeVarags 어노테이션을 사용할 수 있다.
자바9부터 private 함수에도 사용할 수 있게 되었다.
3. 익명 inner class + diamond syntax
내부 클래스를 익명 클래스로 만들 때, diamond syntax 를 사용할 수 있게 되었다는 점이다.
diamond syntax : 자바 7에서 소개된 기능
제네릭 클래스를 인스턴스화할 때 우항의 타입 생략
List<Integer> numbers = new ArrayList<>();
내부 클래스는 클래스 안에 위치한 클래스로, 외부 참조가 있는 내부 클래스와 외부참조가 없는 클래스로 분리 가능
public class Main {
// 외부 참조를 할 수 있는 내부 클래스
public class ReferenceClass {}
// 외부 참조를 할 수 없는 내부 클래스
public static class NoReferenceClass {}
}
자바 9부터 내부 클래스를 익명 클래스로 만들 때, diamond syntax 사용이 가능해졌다.
public class Main {
public static void main(String[] args) {
InnerClass<Integer> ic = new InnerClass<>(3) {};
}
public static class InnerClass<T> {
private final T t;
public InnerClass(T t) {
this.t = t;
}
}
}
4. 인터페이스에서 private 메소드를 사용할 수 있도록 변경
기존 인터페이스를 수정하게되면 하위 호환성이 깨지게 되는 문제가 있었다. 그래서 비슷한 이름의 다른 인터페이스를 만들고, 이 차이를 묻는 사람들이 생기게 된다.
자바 8에서 default 메소드 구현이 가능해지면서 여러 default 메소드들끼리 공통된 코드로 존재하게 되었고, 자바9에서는 private 메서드 구현을 가능하게 하여 공통된 코드를 분리할 수 있게 되었다.
5. under score 네이밍 불가
int _ = 1;
_ 만으로 변수, 함수 이름을 지을 수 없게 변경됨
6. Collection 기능 추가
6-1. Collection 을 만드는 방법 추가
List.of() 정적 팩토리 메소드가 생겼다.
List<Integer> oldList = Arrays.asList(1, 2);
List<Integer> newList = List.of(1, 2);
Set<Integer> oldSet = new HashSet<>(Arrays.asList(1,2));
Set<Integer> newSet = Set.of(1,2);
Map<String, Integer> newMap = Map.of("A", 1, "B", 2);
Map.ofEntries(entry("A", 1), entry("B", 2));
정적 팩토리 메소드 of 를 이용해 만들어진 Collection 은 불변이기 때문에 값을 추가하거나 제거할 수 없다. (UnsupportedOperationException 발생)
과거 불변 컬렉션을 만들기 위해 사용했던 Collections.unmodifiableList 와는 구현체가 다름
Collections.unmodifiableList -> 구현체: UnmodifiableRandomAccessList, UnmodifiableList
List.of -> 구현체: ImmutableCollections.ListN 이 구현체로 사용된다.
정적 팩토리 메소드를 사용하면 좀더 메모리 효율적인 코드 작성이 가능하다.
7. Optional, Stream 기능 추가
Optional 기능
ifPresentOrElse(action, emptyAction)
기존에 있던 ifPresent(action) 의 기능이 더 확대된 것
두 함수를 받아 값이 존재하면 앞의 함수를, 없으면 뒤의 함수를 실행
or(supplier)
Optional 에서 체이닝을 통해 값이 존재하지 않을 경우 다른 Optional 로 변환할 수 있도록 or API 가 등장
getOptionalNum().or(() -> Optional.of(3));
stream()
Optional 을 stream 으로 변환하는 함수
값이 존재하면, 해당 값 한개를 가진 stream 이 되고, 없으면 빈 스트림이 됌
Stream 기능
takeWhile(predicate)
filter(predicate) 와 기능이 비슷하지만, predicate 결과가 false 가 나오는 순간 뒤의 데이터는 모두 버린다.
dropWhile(predicate)
takeWhile(predicate) 과 비슷하지만 반대이다. predicate 이 false 가 나오는 순간 뒤의 데이터는 모두 살린다.
Stream.of(10, 5, 15, 3, 20)
.dropWhile(num -> num <= 10)
.collect(Collectors.toList());
10, 5는 버리고 15가 나오는 순간 뒤의 데이터를 모두 남긴다.
ofNullable(t)
정적 팩토리 메소드
null 인 경우 빈 스트림, 아니면 해당 값을 가진 Stream 반환
iterate
기존 iterate 기능 개선
기존
Stream.iterate(0, i -> i + 2)
.limit(5)
.forEach(System.out::println);
기존 함수는 Stream 을 만들기 위해 초기값, 변화 값 두가지 매개변수를 넣었고, 유한 스트림을 위해 Limit() 함수 필요
개선
Stream.iterate(0, i -> i < 10, i -> i + 2)
.forEach(System.out::println);
유한 스트림을 위한 종료 조건을 매개변수로 넣을 수 있게 됨
limit() 을 사용하지 않아도 된다.
8. CompletableFuture API 기능 추가
비동기 프로그래밍에 사용되는 CompletableFuture
CompletableFutre 복사 기능
- completableFuture: 비동기 작업의 결과물을 표현하는 객체
default Executor 를 가져오는 기능
- default Executor: 비동기 작업들이 실행되는 스레드 풀
orTimeout()
이 메소드를 이용하면 CompletableFuture 에서 실행하고 있는 작업에 대한 타임아웃을 걸 수 있다.
public static void main(String[] args) throws Exception {
// 10 초 기다렸다 '작업 완료' 출력하는 Runnable
Runnable sleep = () -> {
try {
Thread.sleep(10_000L);
System.out.println(System.currentTimeMillis() + " - 작업 완료");
} catch(InterruptedException e) {}
};
System.out.println(System.currentTimeMillis() + " - 작업 실행");
// sleep 작업 실행하고 1초 넘으면 timeout
CompletableFuture<Void> future = CompletableFuture.runAsync(sleep).orTimeout(1, TimeUnit.SECONDS);
// 작업이 완료될 때까지 메인 스레드를 blocking
future.get();
}
위 예제를 실행시키면 작업 실행 출력 이후 timeout exception 발생하고, CompletableFuture 는 예외와 함께 종료된다.
비슷한 함수는 completeOnTimeout() 이 있다. 이 함수는 orTimeout 과 사용법이 같지만, 타임아웃이 되면, 에러와 함께 CompletableFuture 가 종료되는 대신 매개변수로 받은 값을 반환하는 CompletableFuture 를 만들어 낸다.
orTimeout()
- 정해진 시간안에 작업이 완료되지 않으면, 예외 발생
completeOnTimeout()
- 정해진 시간 안에 작업이 완료되지 않으면, 예외 발생
DelayedExecutor - 지연 기능
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) throws Exception {
// 5초 후에 작업을 실행하는 Executor(실행기)
Executor executor = CompletableFuture.delayedExecutor(5, TimeUnit.SECONDS);
Runnable sleep = () -> {
System.out.println(System.currentTimeMillis() + " - 작업 완료");
};
System.out.println(System.currentTimeMillis() + " - 작업 실행");
// sleep 작업을 executor 에서 실행
CompletableFuture<Void> future = CompletableFuture.runAsync(sleep, executor);
future.get();
}
}
9. Process API 추가
// 지금 실행하는 java process 의 PID 를 가져와 출력하기
ProcessHandle.current().pid();
Process: 프로레스 자체를 표현
ProcessHandle: 프로세스를 제어하는 기능들
ProcessHandle.Info: 프로세스 관련 다양한 정보 조회
10. Stack-Walking API 추가
스택을 훑는 기능
- 지금 시점의 스택 프레임을 제어하는 기능
(StackTrace, 함수 호출을 거슬러 최초 함수 호출까지 확인 가능)
11. 내부 문자열 처리 방식 개선
UTF-16 char 배열
- 글자 하나당 2byte 저장공간 필요
자바 9부터는 문자열 저장에 몇 바이트가 필요한지에 따라 관리하고 있음
- Compact string
12. JDK 버전 스키마 방식 변경
메이저.마이너.보안패치
- 메이저: 자바 버전 표기
13. 메모리 및 GC 관련 변경 사항
자바9부터 기본 GC: G1GC
이전: Parallel GC, 튜닝 시 CMS GC 를 사용하기도 함
Parallel GC
- 실제 물리적으로 각 객체의 세대 구분
G1GC
- 메모리를 바둑판처럼 나누어 관리
- 메모리 전체를 관리하는 방식보다 메모리 회수를 할 때 발생하는 프로그램 중단 시간이 평균보다 줄어듦
- CMS GC 는 deprecated 됨
- OutOfMemoryError (OOM) 옵션 추가 -> JVM 이 크래시 발생시켜 크래시 파일 만드는 옵션(CrashOnOutOfMemoryError)