자바 1.8 새롭게 추가된 기술들
Java 8의 주요 변경 사항
- Lambda expression(람다 표현식)
// 람다 표현식으로 단순하게 표현
Thread thread = new Thread(() -> System.out.println("Start to new thread!"));
2.Functional interface(함수형 인터페이스)
단 하나의 추상 메서드를 갖는 인터페이스를 함수형 인터페이스
@FunctionalInterface
public interface Car {
String drive(int driveLevel);
}
Car car = new Car() {
@Override
public String drive(int driveLevel) {
return driveLevel == 0 ? "" : "자동차가 " + driveLevel + " 의 속도로 이동합니다.";
}
};
System.out.println(car.drive(10));
3. Default method(디폴트 메서드)
- interface 에 바로 구현체 (default / static )
public interface Car {
// 전진
void drive();
// 후진
void reverse();
// 날기
default void fly() { System.out.println("Fly to the moon"); }
}
4. Stream(스트림)
- 중간연산 ( filter () / sotred() / map() )
- 종료연산 ( foreach() / count() / collect() )
List<String> booksWrittenByNietzsche =
books.stream()
.filter(book -> book.getAuthor().equals("Friedrich Nietzsche"))
.sorted(Comparator.comparing(Book::getName))
.map(Book::getIsbn)
.collect(Collectors.toList());
5.Optional(옵셔널)
public class BookService {
// Optional을 사용하여 null에서 안전한 코드 작성하기
public Optional<String> getAuthorName(Book book) {
return Optional.ofNullable(book)
.map(bookObject -> bookObject.getAuthor())
.map(authorObject -> authorObject.getName());
}
}
public class BookService {
// Optional을 사용하여 null에서 안전한 코드 작성하기
public Optional<String> getAuthorName(Book book) {
return Optional.ofNullable(book).ifPresent(System.out::plintln(book))
}
}
- optional method 종류
[객체를 Optional로 감싸는 메서드]
- of(T):Optional<T>
-> 파라미터로 받은 객체를 Optional로 감싸 반환한다. 만약 파라미터가 null이면 NPE가 발생한다. - ofNullable(T):Optional<T>
-> 기본적으로 of()와 동일하나 파라미터가 null이면 빈 Optional을 반환한다. - empty():Optional<T>
-> 빈 Optional을 반환한다. Optional 객체의 중간 연산 중에 값이 null이 되면 내부적으로 이 메서드를 호출한다.
[Optional의 중간 연산]
- filter(Predicate<? super T> predicate):Optional<T>
-> Stream API의 filter()와 동일하다. predicate의 조건에 맞는 값을 필터링한다. - map(Function<? super T, ? extends U> mapper):Optional<T>
-> Stream API의 map()과 동일하다. Optional로 감싸진 객체를 다른 객체로 변경하도록 데이터 변경을 한다. - flatMap(Function<? super T, Optional<U>> mapper):Optional<T>
-> Optional안의 Optional이 있는 이중 구조일 때, 단일 구조로 변경하여 map()의 기능을 수행할 수 있다.
[Optional의 종료 연산]
- get():T
-> Optional의 값을 반환한다. 만약 값이 빈 값이라면 NPE가 발생한다. - orElse(T):T
-> get()과 동일한 기능을 수행하지만, 값이 비어있다면 파라미터로 받은 값으로 반환한다. - orElseGet(Supplier<? extends T> other):T
-> get과 동일한 기능을 수행하지만, 값이 비어있다면 파라미터에서 제공하는 값을 반환한다. - orElseThrow(Supplier<? extends X> exceptionSupplier):T
-> get과 동일한 기능을 수행하지만, 값이 비어있다면 파리미터에서 생성한 exception을 발생시킨다.
[기타 메서드]
- isPresent():boolean
-> Optional의 값이 있다면 true, 비어있다면 false를 반환한다. 상태를 확인할 뿐 값에 어떤 영향도 미치지 않는다. - ifPresent(Consumer<? super T> consumer):void
-> Optional의 값이 있다면 파라미터를 실행하고, 비어있다면 false를 반환한다.
6. 새롭게 추가된 날짜 API
- 사람용 날짜 API :: LocalDate, LocalTime, LocalDateTime
LocalDateTime
.now()
.format(DateTimeFormatter.ofPattern("오늘은 y-mm-dd일(E)이고,현재시간은 a h:s분입니다")
.withLocale(Locale.KOREA))
7. CompletableFuture(컴플리터블 퓨처)
- Java의 비동기 프로그래밍은 자주 사용하지 않아 불편함조차 인지하지 못하는 경우가 많다. 대표적으로 생성한 스레드가 동작중인지, 아니면 정상적으로 종료되었는지 혹은 예외가 발생해 비정상 종료되었는지 메인 스레드에서는 알기 어렵다.
- 자바 비동기 프로그래밍 . ( 스레드 동작여부, 정상종료 확인 여부, 예외 발생 여부 등 체크 )
[Thread를 사용한 조회]
// 엔터파크에서 책 가격 조회
new Thread(() -> {
int bookPrice = Enterpark.getBookPrice();
System.out.println("Enterpark : " + bookPrice);
}).start();
// 일리단에서 책 가격 조회
new Thread(() -> {
int bookPrice = Illidan.getBookPrice();
System.out.println("Illidan : " + bookPrice);
}).start();
[Future를 사용한 조회]
ExecutorService executorService = Executors.newFixedThreadPool(5);
Future<Integer> enterparkFuture = executorService.submit(() -> {
int bookPrice = Enterpark.getBookPrice();
System.out.println("Enterpark : " + bookPrice);
return bookPrice;
});
Future<Integer> illidanFuture = executorService.submit(() -> {
int bookPrice = Illidan.getBookPrice();
System.out.println("Illidan : " + bookPrice);
return bookPrice;
});
// 스레드 결괏값 반환을 요청. 3초 안에 반응이 없으면 스레드를 블록시킨다.
Integer enterparkPrice = enterparkFuture.get(3, TimeUnit.SECONDS);
Integer illidanPrice = illidanFuture.get(3, TimeUnit.SECONDS);
// 가장 낮은 가격과 서점명 출력
printPriceAndBookstore(enterparkPrice, illidanPrice);
>>
ExecutorService executorService = Executors.newFixedThreadPool(5);
Future<Integer> enterparkFuture = executorService.submit(() -> {
int bookPrice = Enterpark.getBookPrice();
System.out.println("Enterpark : " + bookPrice);
if (true) throw new TimeoutException("fail"); // 의도적으로 예외 발생
return bookPrice;
});
// try - catch문으로 다른 스레드의 예외를 캐치할 수 있다.
Integer enterparkPrice = null;
try {
enterparkPrice = enterparkFuture.get(3, TimeUnit.SECONDS);
} catch (TimeoutException e) {
System.out.println(e.getMessage());
}
[CompletableFuture를 사용한 조회]
CompletableFuture<Integer> lowPriceSearchFuture = CompletableFuture.supplyAsync(() -> {
int bookPrice = Enterpark.getBookPrice();
System.out.println("Enterpark : " + bookPrice);
return bookPrice; })
.thenCombine(CompletableFuture.supplyAsync(() -> {
int bookPrice = Illidan.getBookPrice();
System.out.println("Illidan : " + bookPrice);
return bookPrice;
}),
(enterpartBookPrice, illidanBookPrice) -> printPriceAndBookstore(enterpartBookPrice, illidanBookPrice))
.exceptionally(throwable -> {
System.out.println(throwable.getMessage());
return -1; })
.thenApply(integer -> commonService.createLog(integer)); // 메서드 체이닝에 추가 (Database에 log를 기록하려면)
lowPriceSearchFuture.get(3, TimeUnit.SECONDS);
8. JVM의 변화
JVM에도 변화가 있었다. 기존의 PermGen 영역이 사라지고 대신 Metaspace 영역이 새롭게 생겨났다. 실무적인 관점에서 보면 Java 8부터는 메모리 옵션 중 -XX:PermSize나 -XX:MaxPermSize가 사라지고 -XX:MetaspaceSize, -XX:MaxMetaspaceSize 옵션이 새롭게 등장한 것이다. 따라서 Java 8을 사용하면서 PermGen의 메모리 옵션을 줘도 아무런 영향을 주지 못한다. 그럼 Metaspace로의 변경을 어떤 의미를 갖는지 간단히 확인해보자.
Heap 대신 Native로
PermGen영역은 클래스의 메타정보를 관리하는 메모리 공간이다. 클래스의 이름, 애노테이션 정보뿐만 아니라 static 필드 등 클래스를 구성하는 정보 따위 담는다. Java 8 이전에는 PermGen 영역이 Heap 메모리에 포함되어 있어 크기가 제한되었다. 그래서 클래스가 많은 애플리케이션을 구동하다 보면 PermGen 영역의 OOM이 발생하는 경우가 종종 발생한다.
Java 8부터는 이 공간이 삭제되고 동일한 기능을 하는 Metaspace 영역이 새롭게 생겼다.(이름부터 무엇을 저장하고 싶은지 명확해졌다) 이곳은 Native 영역에 속하며 OS가 크기를 자동으로 조정한다. 애플리케이션이 동작하면서 동적으로 클래스를 많이 생성한다 해도 Metaspace 영역이 가변적으로 늘어나 충분히 수용할 수 있게 된다. PermGen 영역을 사용할 때에 비해 OOM이 발생할 여지가 크게 줄어든 셈이다.
이런 변경점은 대게 몰라도 아무런 문제가 없다. 다만 큰 애플리케이션을 개발하거나 성능이 부족한 서버 하나에 여러 애플리케이션을 구동하는 경우라면 반드시 이런 이해가 필요할 것이다. 가장 좋은 것은 jstat과 같은 모니터링 도구로 적절한 설정값을 눈으로 확인하여 이를 기준값으로 설정하는 것이다.
실무 Tip
- Metaspace의 최댓값은 반드시 설정해주자. 가변적으로 늘어나기 때문에 극단적인 상황에는 서버의 모든 메모리를 Metaspace가 가져가 애플리케이션이 아닌 서버가 죽게 될 수도 있다.
- -XX:MaxMetaspaceSize=N으로 최댓값을 설정할 수 있고, 최댓값까지 늘어나는 과정이 불필요하다면 -XX:MetaspaceSize를 통해 기본값 = 최댓값으로 설정하면 된다.
출처
https://bbubbush.tistory.com/m/23
Java 8의 주요 변경 사항과 실무 적용 포인트
들어가며 Java는 끊임없이 진화하고 있다. 1996년 JDK 1.0으로 발표된 이후 25년이 지난 오늘날까지 대한민국에서 널리 사용되는 이유는 여기에 있다. 그중 'Modern java'라는 별칭이 붙은 Java 8의 변경사
bbubbush.tistory.com