[ 자바 8의 지원일자 ]
- 무료 지원 일자 : 20214-03 ~ 2022-03
- 유료지원 일자 : 2030-12
- 무료 대안( Amazon Corretto, AdoptOpenJDK 등에서 2026년까지 지원 계획 )
[ 호환 스프링 부트 ]
- Spring Boot 호환 : 1.5x ~ 2.7x
- 안정 적인 버전 : 자바 8과는 2.7.x(18) 버전이 안정적
[ JVM & GC ]
- Parallel 스트림에 포크/조인 풀 사용
[ 특징 및 주요 변경 사항 ]
1. Lambda expression(람다 표현식)
- Lambda expression(람다 표현식) : 아래와 같이 람다식을 쓸 수 있게 변경되었습니다. 람다 식은 함수형 프로그래밍 언어로 함수 자체를 값처럼 전달할 수 있도록 해줍니다.
- 문법
형식 | 예시 | 비고 |
(파라미터)→{본문} | (int x, int y) → x + y | 타입 추론 가능시 타입 생략 가능 |
파라미터 1개 + 타입추론 + 본문 1줄 리턴생략 | x → x*x | 괄호 생략 가능 |
본문 다중 라인 | (a,b) → { int r = a*b; return r; } | 중괄호 필수 |
메서드 참조 | String::toUpperCase, Integer::sum | 람다식 간결화 |
- 코드
List<Integer> list = Arrays.asList(1,2,3,4,5);
Collections.sort(list, (a,b)->b - a);
for(int num : list)
System.out.print(num + " ");// 출력결과 : 5 4 3 2 1
2. Functional interface(함수형 인터페이스)
- 추상 메서드가 단 하나만 존재하는 인터페이스로 @FunctionalInterface 어노테이션을 사용해 쓸 수 있다.
3. Default method(디폴트 메서드)
- 디폴트 메서드는 간단하게 말하면, 기존 코드를 건드리지 않고 인터페이스에 새 동작을 넣고 싶을 때 사용할 수 있습니다. 기존에는 인터페이스 자체에 구현을 넣을 수 없어 API 확대가 어려웠습니다. 이를 해결하기 위해 default 키워드로 인터페이스에 몸체를 바로 넣을 수 있습니다.
public interface A {
void abstractMethod(); // 여전히 추상 메서드
default void log(String msg) { // ← 디폴트 메서드
System.out.println("[A] " + msg);
}
}
// 다중 상속시, 명시적으로 super 키워드로 해결
interface L {
default void log(){ System.out.println("L"); }
}
interface R {
default void log(){ System.out.println("R"); }
}
class Impl implements L, R {
@Override public void log(){ // 직접 해결
L.super.log(); // or R.super.log()
System.out.println("Impl");
}
}
4. Stream(스트림)
- 스트림은 데이터 원본(컬렉션, 배열, I/O 등)을 선언적 파이프라인으로 처리할 수 있게 해주는 API입니다. 반복문에서 사용하는 임시 변수를 제거하여 가독성이 향상되고 병렬 처리가 가능합니다.
- 구성요소
- 생성 : stream(), IntStream.range(), Files.lines()
- 중간 연산 : filter, map, sorted, peek( 무한 체인, lazy )
- 최종 연산 : collect, reduce, count, forEach → 파이프라인 실행 트리거
- 일반적인 반복 문과 차이점은 아래와 같습니다.
항목 | 반복문 | 스트림 |
선언적 코드 | X | O |
Lazy 처리 | X | O |
병렬 전환 | 수동 스레딩 작성 | parallel() 한 줄로 가능 |
성능(소량) | 빠름 | 약간 느릴 수 있음 |
- 코드 예시
List<String> words = List.of("java", "lambda", "stream", "blog");
long result = words.stream() // ① Source
.filter(w -> w.length() > 3) // ② Intermediate
.map(String::toUpperCase)
.distinct()
.count(); // ③ Terminal
5. Optional(옵셔널)
- Optioanl은 NPE 발생 가능성을 컴파일 타임에 노출하여, 오류를 빨리, 명시적으로 처리하게 합니다. 그러나 오남용하면 오히려 복잡성과 오버헤드가 늘어납니다.
- Optional<T>는 값이 있을 수도, 없을 수도 있는 컨테이너로, 타입 수준에서 없음으로 표현합니다.
- 코드 예시
// Spring Data findById
User user = userRepo.findById(id)
.orElseThrow(() -> new EntityNotFoundException("사용자 없음"));
// Optional + Stream (Java 8 방식)
List<String> mails = users.stream() // List<User>
.map(User::getPrimaryEmail) // Optional<String>
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
6. 새롭게 추가된 날짜 API
- 자바 8 java.time API는 가독성, 불변, 스레드 안전을 모두 만족시키게 업데이트되었습니다.
범주 | 클래스 | 특징 |
날짜 전용 | LocalDate | YYYY-MM-DD(타임존 X) |
시간 전용 | LocalTime | hh:mm:ss.nnnnnnnn(타임존 X) |
날짜 + 시간 | LocalDateTime | 가장 자주 쓰임(타임존 X) |
시점 | Instant | UTC 기준 1970-01-01부터 ns 단위 offset |
오프셋 포함 | OffsetDateTime, OffsetTime | +09:00 같은 오프셋 포함 |
타임존 포함 | ZonedDateTime | LocalDateTime + ZoneId 완전체 |
기타 | Year, YearMonth, MonthDay, Clock, ZoneId |
- 코드 예시
// 생성 & 조작
LocalDate today = LocalDate.now(); // 2025‑04‑30
LocalDate payday = today.withDayOfMonth(25); // 이번 달 25일
LocalDate nextMonthFirst = today.with(TemporalAdjusters.firstDayOfNextMonth());
LocalDateTime meeting = LocalDateTime.of(2025, 5, 2, 14, 0);
LocalDateTime plus90 = meeting.plusMinutes(90); // 15:30
// 포매팅 / 파싱 (DateTimeFormatter)
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm");
String s = fmt.format(meeting); // "2025.05.02 14:00"
LocalDateTime parsed = LocalDateTime.parse("2025‑05‑02 14:00", fmt);
7. CompletableFuture(컴플리터블 퓨처)
- java 5에서 추가된 Future의 단점을 모두 개선한 CompletableFuture가 등장했다.
- Non-blocking : 결과가 준비될 때까지 스레드를 묶어두지 않음
- 함수형 체이닝 : 람다와 결합해 Promise 스타일 파이프라인을 구성
- 조합 : 여러 비동기 작업을 AND / OR 방식으로 조립 가능
- 완료 알림 : 성공, 실패, 취소 이벤트를 한곳에서 다룹니다.
- 비동기 + 이벤트 기반 API를 표준 라이브러리에서 바로 제공합니다.
- thenApply, thenCompose, allOf, exceptionally 등 풍부한 조합자(Combinator)로 콜백 지옥을 방지합니다.
- CPU 바운드, I/O 바운드 작업 분리가 용이합니다.
CompletableFuture
.supplyAsync(() -> {
sleep(300); // 시간이 오래 걸리는 작업
return "hello";
})
.thenApply(String::toUpperCase) // 후속 변환 (논블로킹)
.thenAccept(System.out::println); // HELLO
supplyAsync : 별도 스레드에서 Supplier 실행.
thenApply : 완료된 결과를 받아 변환.
thenAccept : 최종 소비(출력).
// 두 계산 병렬 → 합산
CompletableFuture<Integer> a = CompletableFuture.supplyAsync(() -> heavy(20));
CompletableFuture<Integer> b = CompletableFuture.supplyAsync(() -> heavy(22));
a.thenCombine(b, Integer::sum)
.thenAccept(sum -> log("합계=" + sum)); // 42
8. JVM의 변화
- Java 8이 나오면서 JVM의 메모리 영역 중에 Permanent Generation 이 완전히 제거되고, Metaspace 메모리 영역이 새로 도입되었습니다.
- 메모리 관리 개선: 고정 크기의 PermGen으로 인한 제한사항을 해결하고, 동적으로 확장 가능한 메모리 모델을 제공합니다.
- 가비지 컬렉션 부담 감소: Metaspace는 가비지 컬렉션 관점에서 더 효율적으로 설계되었습니다. G1(Garbage-First) 가비지 컬렉터의 개선으로 대규모 힙 관리가 더 효율적으로 이루어졌습니다.
- 클래스 언로딩 개선: 클래스 언로딩 메커니즘이 개선되어 동적 클래스 로딩/언로딩이 많은 애플리케이션의 성능이 향상되었습니다.
- 64비트 아키텍처 최적화: 현대적인 64비트 시스템에서 네이티브 메모리를 더 효율적으로 활용할 수 있게 되었습니다.
'자바' 카테고리의 다른 글
[Java] 컬렉션(Collections) 동기화 방법 (1) | 2025.06.11 |
---|---|
[자바] Java 21 버전 특징과 주요 변경 사항 (0) | 2025.05.09 |
[자바] Java 17 버전 특징과 주요 변경 사항 (1) | 2025.05.08 |
[자바] Java 11 버전 특징과 주요 변경 사항 (1) | 2025.05.07 |