✅ 정식 출시 (Finalized)
JEP 484 · Class-File API
.class 바이트코드 파일을 읽고, 쓰고, 변환하는 표준 API입니다.
기존에는 바이트코드를 다루려면 ASM, Javassist 같은 서드파티 라이브러리에 의존해야 했습니다. 이제 JDK 자체에서 공식 지원합니다.
// 클래스 파일 읽기 예시
ClassFile cf = ClassFile.of();
ClassModel classModel = cf.parse(Path.of("MyClass.class"));
classModel.methods().forEach(method -> {
System.out.println("Method: " + method.methodName().stringValue());
});
활용 사례:
- 프레임워크 개발 (Spring, Hibernate 등 내부 바이트코드 조작)
- 커버리지 툴, 프로파일러
- 코드 변환 도구
JEP 485 · Stream Gatherers
Stream 의 중간 연산을 직접 커스터마이징할 수 있는 gather() API입니다.
기존 스트림은 filter, map, limit 등 정해진 연산만 가능했지만, 이제 직접 만든 중간 연산을 삽입할 수 있습니다.
// 연속된 중복 제거 (커스텀 중간 연산)
Stream.of(1, 1, 2, 2, 3, 1, 1)
.gather(Gatherers.fold(() -> new ArrayList<>(), (list, e) -> {
if (list.isEmpty() || !list.get(list.size()-1).equals(e))
list.add(e);
return list;
}))
.forEach(System.out::println);
// 출력: 1, 2, 3, 1
// JDK 기본 제공 Gatherers
Stream.of(1,2,3,4,5)
.gather(Gatherers.windowFixed(2)) // [1,2], [3,4], [5]
.forEach(System.out::println);
기본 제공 Gatherers:
| 메서드 | 설명 |
|--------|------|
| windowFixed(n) | n개씩 묶어서 List로 |
| windowSliding(n) | 슬라이딩 윈도우 |
| fold(init, fn) | 누적 연산 |
| scan(init, fn) | 중간 누적값 포함 |
JEP 487 · Scoped Values
스레드 간 불변 데이터를 안전하게 공유하는 메커니즘입니다.
기존 ThreadLocal의 단점(변경 가능, 메모리 누수 위험)을 해결한 대안입니다.
// ScopedValue 선언
static final ScopedValue<User> CURRENT_USER = ScopedValue.newInstance();
void handleRequest(User user) {
// 특정 범위(scope) 안에서만 값이 유효
ScopedValue.where(CURRENT_USER, user).run(() -> {
processRequest();
});
}
void processRequest() {
User user = CURRENT_USER.get(); // 안전하게 접근
System.out.println("현재 사용자: " + user.name());
}
ThreadLocal과 비교:
| 항목 | ThreadLocal | ScopedValue |
|------|--------------|---------------|
| 변경 가능 여부 | ✅ 가능 | ❌ 불변 |
| 메모리 누수 위험 | ⚠️ 있음 | ✅ 없음 |
| 가상 스레드 지원 | ⚠️ 비효율적 | ✅ 최적화 |
| 유효 범위 | 스레드 전체 | 명시적 scope |
JEP 490 · ZGC: Generational Mode by Default
Z Garbage Collector(ZGC)의 세대별(Generational) 모드가 기본값으로 설정되었습니다.
"대부분의 객체는 생성 직후 곧 죽는다" — 세대별 가설(Generational Hypothesis)
# 이전 Java 23까지
-XX:+UseZGC -XX:+ZGenerational ← 명시적으로 켜야 했음
# Java 24부터
-XX:+UseZGC ← 자동으로 세대별 모드 사용
# 구 모드로 되돌리려면
-XX:+UseZGC -XX:-ZGenerational효과:
- 단기 객체는 Young Generation에서 빠르게 수거
- GC 오버헤드 감소
- 처리량(Throughput) 및 지연시간(Latency) 동시 개선
JEP 491 · Synchronized Virtual Threads
가상 스레드가 synchronized 블록 진입 시 플랫폼 스레드에 고정(pin)되던 문제를 해결했습니다.
// 기존(Java 23 이하): synchronized 블록에서 가상 스레드가 pin됨
// → 플랫폼 스레드를 점유해 다른 가상 스레드 실행 불가
// Java 24: pin 없이 동작
synchronized (lock) {
// I/O 작업 등 블로킹 발생해도
// 플랫폼 스레드를 해제하고 다른 가상 스레드가 실행 가능
Files.readAllBytes(path);
}
// 대규모 가상 스레드 활용 예
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 100_000; i++) {
executor.submit(() -> {
synchronized (sharedLock) {
doBlockingWork(); // 이제 안전하게 확장 가능
}
});
}
}
영향:
synchronized를ReentrantLock으로 마이그레이션하지 않아도 됨- 기존 레거시 코드도 가상 스레드의 이점을 바로 누릴 수 있음
🔄 Preview / Incubator
JEP 488 · Primitive Types in Patterns (2차 Preview)
instanceof와 switch에서 원시 타입(int, long, double 등)도 패턴 매칭 가능합니다.
// instanceof에서 원시 타입 패턴
Object obj = 42;
if (obj instanceof int i) { // 언박싱 자동 처리
System.out.println("정수: " + i);
}
// switch에서 원시 타입
switch (someDouble) {
case 0.0 -> System.out.println("영");
case double d when d > 0 -> System.out.println("양수: " + d);
case double d -> System.out.println("음수: " + d);
}
// 넓은 타입 변환도 지원
long bigNum = 100L;
if (bigNum instanceof int i) { // 범위 체크 후 변환
System.out.println("int 범위: " + i);
}
JEP 492 · Flexible Constructor Bodies (3차 Preview)
생성자에서 super() 또는 this() 호출 전에 코드 실행을 허용합니다.
// 기존: super() 전에 아무것도 못 씀
class OldStyle extends Parent {
OldStyle(int value) {
super(validate(value)); // 검증 로직을 static 메서드로 강제 분리
}
}
// Java 24: super() 전에 검증/준비 코드 가능
class NewStyle extends Parent {
NewStyle(int value) {
// super() 호출 전 코드 허용!
if (value < 0) throw new IllegalArgumentException("음수 불가");
var adjusted = value * 2;
super(adjusted); // 이후 super() 호출
}
}
JEP 494 · Module Import Declarations (2차 Preview)
모듈 단위로 한 번에 import 하는 기능입니다.
// 기존: 패키지마다 개별 import
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import java.io.IOException;
// ...수십 줄의 import
// Java 24: 모듈 단위 일괄 import
import module java.base; // java.base 모듈의 모든 패키지 import
public class Main {
public static void main(String[] args) {
List<String> list = List.of("a", "b", "c");
Map<String, Integer> map = Map.of("x", 1);
Stream<String> stream = list.stream();
}
}
학습용, 프로토타이핑, 스크립트 작성 시 특히 유용합니다.
JEP 495 · Simple Source Files & Instance Main Methods (4차 Preview)
초보자 친화적인 간결한 Java 프로그램 작성을 지원합니다.
// Java 24: 클래스 선언, static, String[] args 모두 생략 가능!
void main() {
System.out.println("Hello, Java 24!");
}
// 인스턴스 메서드로 main 작성도 가능
class Greeter {
void main() {
var name = "World";
System.out.println("Hello, " + name);
}
}
기존 방식과 비교:
// 기존 방식 (여전히 유효)
public class OldMain {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
Python, Kotlin처럼 교육 목적으로 진입 장벽을 낮추는 것이 목표입니다.
JEP 499 · Structured Concurrency (4차 Preview)
여러 비동기 작업을 하나의 논리적 단위로 관리하는 API입니다.
// 여러 작업을 구조적으로 관리
Response handle() throws InterruptedException, ExecutionException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
// 두 작업을 병렬 실행
Subtask<User> userTask = scope.fork(() -> fetchUser(userId));
Subtask<Order> orderTask = scope.fork(() -> fetchOrder(orderId));
scope.join() // 두 작업 모두 완료 대기
.throwIfFailed(); // 하나라도 실패 시 예외
// 두 결과 모두 성공적으로 사용 가능
return new Response(userTask.get(), orderTask.get());
}
// scope 종료 시 미완료 작업 자동 취소
}'Java' 카테고리의 다른 글
| Java 23 (0) | 2026.05.06 |
|---|---|
| Java에서 데이터 없음을 표현하는 방법 (0) | 2026.04.18 |
| Synchronized & ReentrantLock (0) | 2025.07.15 |
| ThreadLocal (0) | 2020.05.26 |
| Java 예외 처리 (0) | 2020.04.25 |