안녕하세요! 오늘은 Java 언어가 어떻게 발전해왔는지, 각 버전별로 추가된 주요 기능들을 정리해보려고 합니다. Java는 1995년 처음 등장한 이후로 지속적인 발전을 거듭하며 현대적인 프로그래밍 언어로 진화해왔습니다. 특히 Java 8 이후부터는 함수형 프로그래밍 패러다임을 적극적으로 수용하고, 개발자 생산성을 높이는 다양한 기능들이 추가되었습니다.
Java 8 (2014년 3월)
Java 8은 언어의 큰 전환점이 된 버전으로, 함수형 프로그래밍 기능을 본격적으로 도입했습니다.
주요 기능:
- 람다 표현식(Lambda Expressions)
// 이전 Runnable r1 = new Runnable() { @Override public void run() { System.out.println("Hello World!"); } }; // Java 8 이후 Runnable r2 = () -> System.out.println("Hello World!");
- 스트림 API(Stream API)
List<String> names = Arrays.asList("John", "Jane", "Jack", "Joe"); // 이름 중 J로 시작하는 것만 필터링하고 대문자로 변환 List<String> filteredNames = names.stream() .filter(name -> name.startsWith("J")) .map(String::toUpperCase) .collect(Collectors.toList());
- 메서드 레퍼런스(Method References)
// 일반 람다식 Consumer<String> printer1 = s -> System.out.println(s); // 메서드 레퍼런스 Consumer<String> printer2 = System.out::println;
- 디폴트 메서드(Default Methods) 인터페이스에서 구현부가 있는 메서드를 정의할 수 있게 되었습니다.
public interface Vehicle { default void print() { System.out.println("I am a vehicle!"); } }
- Optional 클래스 null을 보다 안전하게 처리할 수 있는 컨테이너 객체
Optional<String> optional = Optional.of("hello"); optional.ifPresent(s -> System.out.println(s));
- 새로운 날짜와 시간 API(java.time 패키지)
LocalDate date = LocalDate.now(); LocalTime time = LocalTime.now(); LocalDateTime dateTime = LocalDateTime.now();
- Nashorn JavaScript 엔진
Java 9 (2017년 9월)
주요 기능:
- 모듈 시스템(Jigsaw Project)
// module-info.java module com.myapp { requires java.logging; exports com.myapp.api; }
- 불변 컬렉션 팩토리 메서드
List<String> list = List.of("a", "b", "c"); Set<String> set = Set.of("a", "b", "c"); Map<String, Integer> map = Map.of("a", 1, "b", 2);
- 인터페이스에 private 메서드 지원
public interface MyInterface { private void privateMethod() { // 구현 코드 } default void defaultMethod() { privateMethod(); // 추가 코드 } }
- try-with-resources 개선
// Java 9 이전 try (BufferedReader br1 = new BufferedReader(new FileReader("file1.txt")); BufferedReader br2 = new BufferedReader(new FileReader("file2.txt"))) { // 코드 } // Java 9 이후 - 외부에서 선언된 자원 사용 가능 BufferedReader br1 = new BufferedReader(new FileReader("file1.txt")); BufferedReader br2 = new BufferedReader(new FileReader("file2.txt")); try (br1; br2) { // 코드 }
- 다이아몬드 연산자 익명 클래스 지원
// Java 9 이전 MyHandler<Integer> intHandler = new MyHandler<Integer>() { // 구현 코드 }; // Java 9 이후 MyHandler<Integer> intHandler = new MyHandler<>() { // 구현 코드 };
- Stream API 개선
// takeWhile, dropWhile 메소드 추가 Stream.of(1, 2, 3, 4, 5, 6, 7, 8) .takeWhile(n -> n < 5) .forEach(System.out::println); // 1, 2, 3, 4 출력
- HTTP/2 클라이언트(인큐베이터)
Java 10 (2018년 3월)
주요 기능:
- 로컬 변수 타입 추론 (var 키워드)
// 이전 String message = "Hello, Java 10"; // Java 10 이후 var message = "Hello, Java 10"; // 컴파일러가 타입을 추론
- 가비지 컬렉터 개선 (G1GC)
- 병렬 처리 가비지 컬렉션
- 컨테이너 인식 기능
- 애플리케이션 클래스-데이터 공유
Java 11 (2018년 9월) – LTS 버전
주요 기능:
- 람다 파라미터에 var 사용 가능
(var x, var y) -> x + y
- HTTP 클라이언트 API 표준화
HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://example.com")) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
- String 클래스 새 메서드
"".isBlank(); // true " ".isBlank(); // true "abc".isBlank(); // false "abc\n".lines().count(); // 1 "abc\ndef".lines().count(); // 2 "abc".repeat(3); // "abcabcabc" " abc ".strip(); // "abc"
- Files 클래스 새 메서드
Path path = Files.writeString( Files.createTempFile("test", ".txt"), "This is a test"); String content = Files.readString(path);
- 자바 실행 단순화
# 소스 파일을 직접 실행 java HelloWorld.java
- Epsilon: 가비지를 수집하지 않는 GC
- ZGC: 확장 가능한 낮은 지연 가비지 컬렉터
Java 12 (2019년 3월)
주요 기능:
- 스위치 표현식(프리뷰)
// 이전 switch (day) { case MONDAY: case FRIDAY: case SUNDAY: System.out.println(6); break; case TUESDAY: System.out.println(7); break; // ... } // Java 12 이후 (프리뷰) switch (day) { case MONDAY, FRIDAY, SUNDAY -> System.out.println(6); case TUESDAY -> System.out.println(7); // ... }
- 문자열 들여쓰기(indent) 메서드
String text = "Hello\nWorld"; text = text.indent(4); // " Hello\n World\n"
- NumberFormat 통화 형식 지정
NumberFormat fmt = NumberFormat.getCompactNumberInstance( Locale.US, NumberFormat.Style.SHORT); String result = fmt.format(1000); // "1K"
- Shenandoah GC(실험적)
Java 13 (2019년 9월)
주요 기능:
- 텍스트 블록(프리뷰)
String html = """ <html> <body> <p>Hello, World!</p> </body> </html> """;
- 스위치 표현식 업데이트(프리뷰) yield 키워드 추가
int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY -> { System.out.println(6); yield 6; } case TUESDAY -> { System.out.println(7); yield 7; } // ... };
- ZGC: Uncommit 메모리
Java 14 (2020년 3월)
주요 기능:
- 레코드(프리뷰) 불변 데이터 클래스를 쉽게 정의
record Person(String name, int age) { }
- 패턴 매칭 for instanceof(프리뷰)
if (obj instanceof String s) { // s를 바로 사용 가능 }
- 텍스트 블록(두 번째 프리뷰)
- 스위치 표현식 표준화
int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY -> 6; case TUESDAY -> 7; default -> throw new IllegalStateException("Invalid day: " + day); };
- 유용한 NullPointerExceptions 어떤 변수가 null인지 더 명확하게 표시
- 가비지 컬렉터 개선(ZGC on macOS and Windows)
Java 15 (2020년 9월)
주요 기능:
- 텍스트 블록 표준화
- 레코드(두 번째 프리뷰)
- 봉인 클래스(Sealed Classes, 프리뷰)
public sealed class Shape permits Circle, Rectangle, Triangle { // ... }
- 패턴 매칭 for instanceof(두 번째 프리뷰)
- 히든 클래스
- 외부 메모리 액세스 API(인큐베이터)
- ZGC: 생산 준비 완료
Java 16 (2021년 3월)
주요 기능:
- 레코드 표준화
- 패턴 매칭 for instanceof 표준화
- 봉인 클래스(두 번째 프리뷰)
- Vector API(인큐베이터) SIMD(Single Instruction, Multiple Data) 연산 지원
- Unix-Domain 소켓 채널
- 외부 메모리 액세스 API(두 번째 인큐베이터)
- C++ 14 언어 기능에 JDK 내부 접근
Java 17 (2021년 9월) – LTS 버전
주요 기능:
- 봉인 클래스 표준화
public sealed class Shape permits Circle, Rectangle, Triangle { // ... } public final class Circle extends Shape { // ... }
- 패턴 매칭 for 스위치(프리뷰)
String formatted = switch (obj) { case Integer i -> String.format("int %d", i); case Long l -> String.format("long %d", l); case Double d -> String.format("double %f", d); case String s -> String.format("String %s", s); default -> obj.toString(); };
- 외부 메모리 액세스 API(인큐베이터)
- Vector API(두 번째 인큐베이터)
- 컨텍스트별 역직렬화 필터
- 강력한 캡슐화 for JDK 인터널
Java 18 (2022년 3월)
주요 기능:
- UTF-8 by Default JDK는 이제 기본적으로 UTF-8을 사용합니다.
- Simple Web Server
jwebserver -p 8000
- 코드 스니펫 in Java API 문서
- 벡터 API(세 번째 인큐베이터)
- 패턴 매칭 for 스위치(두 번째 프리뷰)
- 외부 함수 및 메모리 API(인큐베이터)
Java 19 (2022년 9월)
주요 기능:
- 가상 스레드(프리뷰)
// 가상 스레드 생성 Thread.startVirtualThread(() -> { System.out.println("Hello from Virtual Thread"); });
- 구조적 동시성(인큐베이터)
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { Future<String> user = scope.fork(() -> findUser(userId)); Future<Integer> order = scope.fork(() -> fetchOrder(orderId)); scope.join(); // 모든 작업이 완료될 때까지 대기 scope.throwIfFailed(); // 실패한 작업이 있으면 예외 발생 return new Response(user.resultNow(), order.resultNow()); }
- 레코드 패턴(프리뷰)
if (obj instanceof Point(int x, int y)) { System.out.println(x + ", " + y); }
- 패턴 매칭 for 스위치(세 번째 프리뷰)
- 외부 함수 및 메모리 API(두 번째 프리뷰)
- 벡터 API(네 번째 인큐베이터)
Java 20 (2023년 3월)
주요 기능:
- 스코프 값(인큐베이터)
ScopedValue<String> USERNAME = ScopedValue.newInstance(); ScopedValue.where(USERNAME, "user123").run(() -> { // USERNAME.get()은 "user123"을 반환 System.out.println("Username: " + USERNAME.get()); });
- 가상 스레드(두 번째 프리뷰)
- 구조적 동시성(두 번째 인큐베이터)
- 레코드 패턴(두 번째 프리뷰)
- 패턴 매칭 for 스위치(네 번째 프리뷰)
Java 21 (2023년 9월) – LTS 버전
주요 기능:
- 가상 스레드 표준화
// 가상 스레드 생성 Thread thread = Thread.ofVirtual().start(() -> { System.out.println("Hello from Virtual Thread"); }); // ExecutorService 사용 try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { executor.submit(() -> { // 작업 내용 }); }
- 패턴 매칭 for 스위치 표준화
String formatted = switch (obj) { case Integer i -> String.format("int %d", i); case Long l -> String.format("long %d", l); case Double d -> String.format("double %f", d); case String s -> String.format("String %s", s); case Point(int x, int y) -> String.format("Point(%d, %d)", x, y); case null -> "null"; default -> obj.toString(); };
- 레코드 패턴 표준화
// 중첩 패턴 if (obj instanceof Point(int x, int y)) { // x와 y를 직접 사용 } // 레코드 패턴 in 스위치 record Point(int x, int y) {} record Rect(Point upperLeft, Point lowerRight) {} static void printShape(Object o) { switch (o) { case Point(var x, var y) -> System.out.printf("Point(%d, %d)%n", x, y); case Rect(Point(var x1, var y1), Point(var x2, var y2)) -> System.out.printf("Rect(%d, %d)-(%d, %d)%n", x1, y1, x2, y2); default -> System.out.println("Unknown"); } }
- 문자열 템플릿(프리뷰)
String name = "Joan"; int age = 25; String message = STR."My name is \{name} and I am \{age} years old.";
- 구조적 동시성 API(프리뷰)
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { Future<String> user = scope.fork(() -> findUser(userId)); Future<Integer> order = scope.fork(() -> fetchOrder(orderId)); scope.join(); scope.throwIfFailed(); // 결과 처리 processResults(user.resultNow(), order.resultNow()); }
- 외부 함수 및 메모리 API(프리뷰)
- 스코프 값(프리뷰)
- 시퀀셜 컬렉션
SequencedCollection<String> sc = new ArrayList<>(List.of("a", "b", "c")); String first = sc.getFirst(); // "a" String last = sc.getLast(); // "c"
- 키-값 스토어 API(인큐베이터)
Java 22 (2024년 3월)
주요 기능:
- 외부 함수 및 메모리 API(두 번째 프리뷰) 네이티브 코드와 메모리에 안전하게 액세스하기 위한 API
- 문자열 템플릿(두 번째 프리뷰)
String name = "Joan"; int age = 25; // 기본 문자열 템플릿 String message = STR."My name is \{name} and I am \{age} years old."; // 커스텀 템플릿 프로세서 String json = JSON."{ "name": "\{name}", "age": \{age} }";
- 구조적 동시성 API(세 번째 프리뷰)
Response handle() throws ExecutionException, InterruptedException { try (var scope = new StructuredTaskScope.ShutdownOnFailure()) { Future<String> user = scope.fork(() -> findUser()); Future<Integer> order = scope.fork(() -> fetchOrder()); scope.join(); scope.throwIfFailed(); return new Response(user.resultNow(), order.resultNow()); } }
- 스코프 값(두 번째 프리뷰) 스레드 로컬과 유사하지만 가상 스레드에 최적화된 값 공유 메커니즘
- 통합된 링크 단계
jlink
와jpackage
의 기능을 통합한 새로운 링크 단계 - 벡터 API(인큐베이터) SIMD(Single Instruction Multiple Data) 연산을 지원하는 API
- 외부 배열(프리뷰)
try (Arena arena = Arena.ofConfined()) { MemorySegment segment = arena.allocateArray(ValueLayout.JAVA_INT, 100); // 배열 처리 }
Java 23 (2024년 9월) – 최신 버전
주요 기능:
- 문자열 템플릿 표준화
String name = "Joan"; int age = 25; // 기본 문자열 템플릿 String message = STR."My name is \{name} and I am \{age} years old."; // 표현식이 있는 복잡한 템플릿 int a = 10, b = 20; String calculation = STR."\{a} + \{b} = \{a + b}";
- 스코프 값 표준화
// 스코프 값 정의 final ScopedValue<String> USER_NAME = ScopedValue.newInstance(); void process() { // 스코프 값 설정 및 사용 ScopedValue.where(USER_NAME, "admin").run(() -> { // 이 블록과 호출되는 모든 메서드에서 USER_NAME.get()으로 값 접근 가능 String user = USER_NAME.get(); // "admin" doSomething(); }); } void doSomething() { // 동일한 스코프 내에서 호출되면 값에 접근 가능 String user = USER_NAME.get(); }
- 외부 함수 및 메모리 API 표준화
// C 함수 호출 try (Arena arena = Arena.ofConfined()) { // C 헤더에서 다운로딩 SymbolLookup stdlib = SymbolLookup.libraryLookup("libc", arena); MethodHandle strlen = Linker.nativeLinker().downcallHandle( stdlib.find("strlen").orElseThrow(), FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS) ); // 문자열 생성 및 네이티브 메모리에 복사 String javaString = "Hello World"; MemorySegment cString = arena.allocateUtf8String(javaString); // 네이티브 함수 호출 long length = (long) strlen.invoke(cString); System.out.println("Length: " + length); }
- 구조적 동시성 API(네 번째 프리뷰)
// 첫 번째 성공 또는 모든 실패 전략 try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) { scope.fork(() -> findMirror1()); scope.fork(() -> findMirror2()); scope.fork(() -> findMirror3()); // 첫 번째 성공 결과를 기다립니다 scope.join(); // 첫 번째 성공한 결과 사용 String result = scope.result(); return result; }
- 명명된 클래스 패턴(인큐베이터)
// 패턴 변수에 이름 지정 if (obj instanceof Point(int x, int y where x > 0 && y > 0)) { // x와 y는 양수 }
- 확장된 패턴 매칭 for 스위치(인큐베이터)
int result = switch (obj) { case String s when s.length() > 5 -> s.length(); case String s -> 0; case Integer i -> i; default -> -1; };
- 벡터 API 표준화
// 128비트 벡터 연산 FloatVector a = FloatVector.fromArray(FloatVector.SPECIES_128, new float[]{1, 2, 3, 4}); FloatVector b = FloatVector.fromArray(FloatVector.SPECIES_128, new float[]{5, 6, 7, 8}); // 벡터 연산 수행 FloatVector c = a.add(b); // [6, 8, 10, 12] FloatVector d = a.mul(b); // [5, 12, 21, 32] FloatVector e = a.neg(); // [-1, -2, -3, -4] FloatVector f = d.add(c).mul(e); // [-11, -20, -31, -44] // 결과 추출 float[] result = new float[4]; f.intoArray(result, 0);
결론
Java는 25년 이상의 역사 동안 지속적으로 발전해왔으며, 특히 Java 8 이후로는 모던 프로그래밍 언어로서의 면모를 갖추기 위해 많은 변화가 있었습니다.
최근의 발전 방향을 보면:
- 생산성 향상 – 레코드, var, 문자열 템플릿 등을 통해 코드 작성이 간결해지고 있습니다.
- 함수형 패러다임 수용 – 람다, 스트림, 패턴 매칭 등 함수형 프로그래밍 요소들이 지속적으로 추가되고 있습니다.
- 동시성 개선 – 가상 스레드, 구조적 동시성 API를 통해 확장성 있는 애플리케이션 개발을 지원합니다.
- 네이티브 상호 운용성 – 외부 함수 및 메모리 API를 통해 네이티브 코드와의 통합이 더 쉬워지고 있습니다.
- 성능 최적화 – 벡터 API, 개선된 가비지 컬렉터 등을 통해 성능이 계속 개선되고 있습니다.
Java의 버전별 주요 기능을 이해하면 프로젝트에 맞는 적절한 버전을 선택하는 데 도움이 됩니다. LTS 버전(현재 Java 8, 11, 17, 21)은 장기간 지원되므로 안정성이 중요한 기업 환경에서 주로 사용됩니다.
앞으로의 Java는 더욱 현대적이고 효율적인 언어로 발전하면서도, 기존 코드와의 호환성을 유지하는 방향으로 나아갈 것으로 보입니다. 특히 가상 스레드, 패턴 매칭, 외부 함수 및 메모리 API 등은 Java의 미래를 바꿀 중요한 기능들이 될 것입니다.