
Java 8
- Stream, Lambda, Optional
- Stream
- for, if 사고에서 전환 → 데이터를 흐름으로 판단한다
- Lambda
- 익명클래스 → 람다
- Optional
- NULL 체크를 위한 Optional 객체
Java 9~11
- 타입 추론, HttpClient의 표준화
- 10 버전
- var 타입 추론 기능 → 명시적 타입 중복 선언 제거
var list = List.of(1,2,3,4);
// var -> 자동으로 List<Integer> 추론- HttpClient의 표준화 (HttpURLConnection 연결 코드 → 외부 API 통신도 Stream 처럼)
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder(URI.create("http://url~")).GET().Build();
HttpResponse<String> res = client.send(request, HttpResponse.BodyHandlers.ofString());Java 12~16
- Switch 표현 개선 , “”” 텍스트 블록, Record 클래스 (17에서 안정화)
- Switch 표현 개선 및 결과를 만드는 표현식으로 변경 → 1급 객체스럽게 변경됨 (statement에서 expression으로 바뀐 것)
String result = switch (value) {
case A, B -> "Pass";
case C, D -> "Re-Learn";
default -> "Fail";
};String query = """
SELECT *
FROM food f
where f.price >= 10000
""";public record User(String id, String pwd) {}Java 17
- Sealed Class, 타입 매칭 검사의 패턴 매칭
- Sealed Class로 상속, 구현 클래스를 선언 및 지정하여 해당 클래스만 상속, 구현이 되도록 제한 하는 기능
- sealed → 상속 제한의 부모 클래스
- permits → 어떤 클래스만 상속 받을지 명시
- final → 더 이상 상속 불가하도록 선언
- non-selad → 다시 자유로운 상속 선언 (sealed class 조상이 있어야 사용가능한 키워드)
public sealed class Parent permits Child, Child2 { // sealed class 로 상속 클래스 선언, Child만 상속되도록 permits 키워드 사용
public String familyName = "김";
public void introduce() {
System.out.println("김씨 가문");
}
}
public non-sealed class Child extends Parent {
// 상속 받은 Child 클래스 -> 아무나 자유롭게 상속 가능
}
public final class Child2 extends Parent {
// 상속 받은 Child2 클래스 -> 더는 상속 불가 선언
}- 타입 매칭 검사의 패턴 매칭
//기존
if (object instanceof String) {
String str = (String) object;
}
//17
if (object instanceof String str) {
System.out.println(str.length());
}Java 21
- 동시성을 위한 가상 쓰레드, Sequenced Collections
- Virtual Thread의 도입
- 기존 쓰레드는 OS의 쓰레드와 1:1 매핑으로 메모리 차지가 매우 크고, 1만개 이상 적용이 힘들었음
- JVM이 스케줄링하는 더 가볍고, 대량 실행이 가능하며, I/O와 대기에 관해 설계 경량화를 위해 개발됨
// 기존
Runnable1 runnable1 = new Runnable1();
Thread t1 = new Thread(runnable1);
long start = System.currentTimeMillis();
t1.start();
try {
t1.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("합계 : " + runnable1.getSum());
long end = System.currentTimeMillis();
System.out.println("실행시간 : " + (end - start));
// 가상쓰레드로 전환시
Runnable1 runnable1 = new Runnable1();
long start = System.currentTimeMillis();
// 가상 쓰레드 생성 + 즉시 시작
Thread thread1 = Thread.ofVirtual().start(runnable1);
try {
thread1.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
long end = System.currentTimeMillis();
System.out.println(end - start);- Sequenced Collection
- 컬렉션에서 앞과 뒤의 개념을 공통으로 다루도록 통합한 것 → 순서를 1급 객체의 개념으로 다룸 → 순서의 보장
- SequencedCollection, SequencedSet, SequencedMap 인터페이스의 생성
- 기존의 List, LinkedHashSet, LinkedHashMap이 이 인터페이스들을 구현하도록 바뀜
- 그래서 Linked 되지 않은 클래스들은 addFirst, addLast, getFirst, getLast() 의 메서드를 사용하지 못했는데 이제 가능
public class Cart {
private final ArrayList<Product> cart;
public Cart() {
cart = new ArrayList<>();
}
public Cart(ArrayList<Product> cart) {
this.cart = cart;
}
public void addProduct(Product product) {
//cart.add(product); -> 기존엔 add(index 입력, Product) 로 밖에 Linked가 아닌 이상 못했음
cart.addLast(product); // 앞과 뒤의 개념을 공통으로 다루도록 되어 맨 뒤에 추가 가능
}
}
Java 25
- 동시성 보장을 위한 Scoped Value
- 각 쓰레드의 독립적인 저장 공간을 제공하는 ThreadLocal은 구조적 문제가 있었음
- ThreadPool로 여러 쓰레드를 재사용하는 환경에서는 값이 남아 다른 요청에 섞이는 문제와 메모리 누수가 있었음
- 어떤 코드의 범위에선 이 값이 유효한지 알수가 없었음
- 21에 추가된 가상 쓰레드의 개념과 섞일 수가 없음 (가상 쓰레드는 JVM이 스케줄링하므로 값이 더 꼬인다는 문제가 잔존)
public class Example {
private static final ThreadLocal<Integer> counter = ThreadLocal.withInitial(() -> 0);
public static void main(String[] args) {
Runnable task = () -> {
for (int i = 0; i < 3; i++) {
counter.set(counter.get() + 1); // 값을 지정하는 부분
System.out.println(Thread.currentThread().getName() + " : " + counter.get());
}
};
new Thread(task, "A").start();
new Thread(task, "B").start();
}
}
// 각각 A : 1..2..3 B: 1..2..3 출력
// 여기서는 각각 쓰레드가 자기만의 counter 복사분을 지님
// 수동으로 get(), set(), remove() 해주어야 관리됨
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
import java.lang.ScopedValue;
public class ScopedExample {
static final ScopedValue<Integer> COUNTER = ScopedValue.newInstance();
public static void main(String[] args) throws Exception {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { // Executors를 통한 비동기 처리 + 가상쓰레드 실행 + var를 통한 추론
IntStream.range(0, 2).forEach(i ->
executor.submit(() -> {
ScopedValue.where(COUNTER, 0).run(() -> {
runTask("Thread-" + i);
}); // 값을 지정하는 부분
})
);
}
}
static void runTask(String name) {
for (int i = 0; i < 3; i++) {
int next = COUNTER.get() + 1;
ScopedValue.where(COUNTER, next).run(() -> {
System.out.println(name + " : " + COUNTER.get());
});
}
}
}
// 가상 쓰레드 사용 + 스코프로 값의 유효 범위를 지정
// 스코프가 끝나면 자동으로 값이 해제됨Share article