Search
Duplicate

Java Development Kit (JDK) 버전별 변화( 8 ~ 17까지)

목차

개요

Java Development Kit (JDK)이 8버전부터 17버전까지 릴리즈되면서 추가 혹은 변경된 기능들에 대해서 살펴보자.
해당 포스팅은 Notion AI를 기반으로 만들어졌으며, Oracle Javadocument로 교차검증을 진행했다.

JDK 8

람다식: 함수형 프로그래밍을 위한 람다식 추가
List<String> list = Arrays.asList("apple", "banana", "orange"); list.forEach(str -> System.out.println(str));
Java
복사
Stream API: 컬렉션 처리를 위한 Stream API 추가
List<String> list = Arrays.asList("apple", "banana", "orange"); long count = list.stream().filter(str -> str.length() > 5).count(); System.out.println(count);
Java
복사
메서드 참조: 람다식을 더 간결하게 표현하는 메서드 참조 추가
List<String> list = Arrays.asList("apple", "banana", "orange"); list.forEach(System.out::println);
Java
복사
Optional 클래스: null 체크를 간편하게 도와주는 Optional 클래스 추가
String str = null; Optional<String> optional = Optional.ofNullable(str); System.out.println(optional.isPresent());
Java
복사
JDK 8은 2014년 3월에 출시.
함수형 프로그래밍을 위한 람다식과 Stream API가 추가되었다. 람다식은 함수형 프로그래밍을 위한 기능으로, 코드를 더욱 간결하고 가독성 있게 작성할 수 있게 해준다. 또한 Stream API는 컬렉션 처리를 위한 API로, 병렬 처리가 가능하며, 많은 양의 데이터를 처리할 때 유용하다. 또한 메서드 참조와 Optional 클래스도 추가되었으며, 코드를 더욱 간결하게 작성할 수 있게 해준다.

JDK 9

모듈 시스템: 모듈 단위로 라이브러리를 관리하는 모듈 시스템 추가
JDK 9에서 추가된 모듈 시스템은 모듈 단위로 라이브러리를 관리할 수 있게 해주며\
다음과 같이 모듈을 정의할 수 있다.
module com.example.mymodule { requires org.apache.commons.lang3; exports com.example.mypackage; }
Java
복사
com.example.mymodule이라는 모듈을 정의했는데, 이 모듈은 org.apache.commons.lang3 모듈을 사용하고, com.example.mypackage 패키지를 외부에 노출한다.
인터페이스 개선: private 메서드, private 정적 메서드, 인터페이스에서 private 필드 등의 기능 추가
기능을 구현하는 코드가 더욱 간결해지고 가독성이 개선
private 메서드를 사용하여 기능을 구현하는 예시 코드
public interface MyInterface { default void myMethod() { // public 메서드에서 private 메서드 호출 doSomething(); } private void doSomething() { // private 메서드 구현 } }
Java
복사
JDK 9는 2017년 9월에 출시.
이번 버전에서는 모듈 시스템과 인터페이스 개선 기능이 추가되었다.
모듈 시스템은 모듈 단위로 라이브러리를 관리할 수 있게 해주며, 라이브러리 간의 의존성을 더욱 명확하게 관리할 수 있게 해준다. 또한 인터페이스에서
private 메서드
private 정적 메서드,
private 필드
등의 기능이 추가되어, 코드를 더욱 간결하고 가독성 있게 작성할 수 있게 해준다.

JDK 10

var 키워드: 변수의 타입을 추론하는 var 키워드 추가
var str = "Hello, World!"; var list = Arrays.asList("apple", "banana", "orange");
Java
복사
로컬 변수 타입 인터페이스: 로컬 변수에 인터페이스를 사용할 수 있는 기능 추가
interface MyInterface { void myMethod(); } public class MyClass { void myMethod() { MyInterface myInterface = new MyInterface() { @Override public void myMethod() { // 구현 } }; } }
Java
복사
JDK 10은 2018년 3월에 출시.
이번 버전에서는 변수의 타입을 추론하는 var 키워드와 로컬 변수에 인터페이스를 사용할 수 있는 기능이 추가되었다.
var 키워드는 코드를 더욱 간결하고 가독성 있게 작성할 수 있게 해주며, 로컬 변수에 인터페이스를 사용할 수 있는 기능은 코드 재사용성을 높일 수 있게 해준다.

JDK 11

HTTP 클라이언트: HTTP/2를 지원하는 HTTP 클라이언트 추가 - HTTP/2를 지원하는 HTTP 클라이언트가 추가되었다. 이를 사용하면 HTTP/2를 지원하는 서버와 더욱 빠르고 안전하게 통신할 수 있게 되었다.
HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("<https://www.example.com/>")) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body());
Java
복사
HttpClient를 사용하여 HttpRequest를 생성하고 HttpResponse를 받아오는 예제 코드
로컬 변수 문법 개선: var 키워드로 로컬 변수를 선언할 때 초기화 구문이 없어도 됨
타입을 명시하지 않아도 적절한 타입으로 추론할 수 있게 됌. 이전에는 변수를 선언할 때 타입을 반드시 명시해야 했지만, var 키워드를 사용하면 타입 추론이 가능해져서 코드를 간결하게 작성할 수 있게 되었다.
var를 사용하는 예제 코드
var str = "Hello, World!"; var list = Arrays.asList("apple", "banana", "orange");
Java
복사
str 변수는 String 타입으로 추론되며, list 변수는 List<String> 타입으로 추론 가능.
⇒ 하지만 var 키워드를 남용하면 코드의 가독성을 떨어뜨릴 수 있으므로, 적절하게 사용해야한다. 또한 var 키워드를 사용하면 변수의 타입을 명시적으로 지정하지 않으므로, 변수의 용도나 목적에 대한 이해가 필요합니다.
JDK 11은 2018년 9월에 출시.
이번 버전에서는 HTTP/2를 지원하는 HTTP 클라이언트와 로컬 변수 문법 개선 기능이 추가됌.
HTTP 클라이언트는 HTTP/2를 지원하기 때문에 더욱 빠르고 안전한 웹 개발이 가능해졌으며, 로컬 변수를 선언할 때 초기화 구문이 없어도 되도록 개선되었기 때문에, 코드를 더욱 간결하고 가독성 있게 작성할 수 있게 해준다.

JDK 12

Switch 표현식 개선: Switch 표현식에 새로운 기능 추가
Preview 이기 때문에 —enable-preview 옵션을 줘야 사용 가능하다.
새로운 기능은 Switch 표현식에서 Case 라벨을 화살표 표현식으로 바꿀 수 있다.
switch문을 통해 반환값을 제공할 수 있다.
Multi Label Case를 제공한다(Ex: case MONDAY, FIRDAY, SUNDAY)
화살표 표현식을 사용한 예제 코드
int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY -> 6; case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; default -> 0; };
Java
복사
힙 메모리 최적화: G1 GC 최적화
G1 GC는 다른 GC 방식과는 다르게 힙 공간을 여러 영역으로 분할해 G1 GC의 성능 개선
JDK 12에서는 이러한 G1 GC의 성능이 더욱 개선되었습니다.
JDK 12는 2019년 3월에 출시.
이번 버전에서는 Switch 표현식에 새로운 기능이 추가되었으며, G1 GC 최적화가 이뤄졌다.
Switch 표현식은 조건문을 간결하게 작성할 수 있도록 해주며, 새로운 기능이 추가된 것은 개발자들의 생산성을 높일 수 있게 된다. 또한 G1 GC 최적화는 힙 메모리 사용을 최적화하여 프로그램의 전체적인 성능 향상을 기대할 수 있게 된다.

JDK 13

텍스트 블록: 텍스트 블록을 사용해 코드 문서화 용이성 개선
String html = """ <html> <body> <h1>Hello, world!</h1> </body> </html> """;
Java
복사
switch 표현식 개선
Preview 이기 때문에 —enable-preview 옵션을 줘야 사용 가능하다.
yield 키워드 추가: yield 키워드는 switch 표현식에서 반환값을 지정할 수 있게 해준다. 이전에는 switch 표현식에서 반환값을 지정할 수 없었지만, yield 키워드를 사용하면 반환값을 지정할 수 있게 된다.
String result = switch (day) { case MONDAY, FRIDAY, SUNDAY -> { yield "It's a good day to start coding."; } case TUESDAY -> { yield "It's Tuesday."; } case THURSDAY, SATURDAY -> { yield "It's almost the weekend."; } case WEDNESDAY -> { yield "It's hump day."; } };
Java
복사
JDK 13은 2019년 9월에 출시.
이번 버전에서는 텍스트 블록을 사용해 코드 문서화 용이성을 개선하는 기능이 추가되었고, switch 문법이 좀 더 개선되었다. yield를 사용해서 break문을 사용하지 않고 변수에 값을 넣을 수 있게 되었다.

JDK 14

Switch 표현식 이 preview → 표준화 되었다.
instanceof 패턴 매칭: instanceof 키워드를 사용한 패턴 매칭 추가(Preview)
객체 캐스팅을 효과적으로 해 줘서 별도의 캐스팅 작업 코드를 제거할 수 있게 되었다.
public void print(Object obj) { if (obj instanceof String s) { System.out.println(s.toUpperCase()); } else if (obj instanceof Integer i) { System.out.println(i.intValue()); } else { System.out.println(obj.toString()); } }
Java
복사
record 클래스 추가(preview)
레코드 클래스는 데이터 전달 목적의 클래스를 간결하게 작성할 수 있도록 해준다. 또한 불변적이며 값 타입(value type)으로 동작하여 객체의 상태가 변경되는 것을 방지할 수 있다.
레코드 클래스는 다음과 같이 선언할 수 있다.
public record Person(String name, int age) { // 생성자, 메서드 등을 작성할 수 있다. }
Java
복사
nameage 필드를 가지며, 불변적인 값 타입으로 동작한다.
생성자와 게터(getter) 메서드를 자동으로 생성해준다.
Person person = new Person("Alice", 30); System.out.println(person.name()); // "Alice" 출력 System.out.println(person.age()); // 30 출력
Java
복사
레코드 클래스는 JavaBeans와 유사하지만, JavaBeans보다 간결하고 가독성이 좋다는 장점이 있다. 또한 불변적인 값 타입으로 동작하기 때문에 객체지향 프로그래밍의 원칙에 부합한다.
JDK 14는 2020년 3월에 출시.
이번 버전에서는 JDK12, JDK13에서 preview로 제공되었던 switch의 새로운 기능들이 표준화되었으며 instanceof 키워드를 사용한 패턴 매칭이 추가되었다.
또한, 데이터 전달 목적의 클래스인 Record 클래스가 추가되었다.

JDK 15

Sealed 클래스: 클래스 사용 규칙을 명확히 지정할 수 있는 Sealed 클래스 추가(prewview)
Sealed 클래스는 클래스 사용 규칙을 명확히 지정할 수 있도록 해준다.
특정 클래스를 상속하거나 인터페이스를 구현하는 클래스의 종류를 제한할 수 있다. 이를 통해 객체지향 프로그래밍의 다형성을 더욱 강화할 수 있다.
예제 코드
public abstract sealed class Shape permits Circle, Rectangle, Square { // ... } final class Circle extends Shape { private final double radius; public Circle(double radius) { this.radius = radius; } // ... }
Java
복사
⇒ Shape 클래스가 public인 동안 하위 클래스로 허용되는 구현체는 Circle, Rectangle, Square이다.
Text Blocks 기능의 standard 전환
Text Blocks는 JDK 13에서 추가된 기능이 JDK 15에서는 standard로 전환되었다.
ZGC 업데이트: ZGC 성능 개선
ZGC는 JDK 11에서 추가된 Garbage Collector로, product feature가 되었다.
기존의 G1 GC를 변경하는 것은 아니다.
XX:+UnlockExperimentalVMOptions 옵션을 사용하지 않아도 사용 가능하게 되었다.
ZGC 성능 개선 사항
매우 큰 힙 사이즈(최대 16TB)를 가지는 애플리케이션에서도 매우 짧은 GC 일시 중단 시간을 보장한다.
GC 일시 중단 시간 최소화를 위해 힙 영역을 나누지 않는다.
대신 힙 영역을 Concurrent Root Region과 Heap Region으로 구분해 처리한다. 이를 통해 GC 일시 중단 시간을 최소화 한다.
Concurrent Root Region: GC 수행 중 데이터를 수정할 수 있는 영역
Heap Region: GC중 데이터 수정이 불가능한 영역
이제 작은 힙 크기에서도 사용할 수 있게 되었다.(범용성 )
Records 클래스 업데이트: Records 클래스에서 다른 클래스 상속 가능
Records 클래스에서 다른 클래스를 상속할 수 있게 되면서, 기존의 클래스와 Records 클래스를 조합하여 사용할 수 있게 되었다.
예시 코드
class Person { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } // ... } record Employee(String company, String department) extends Person { public Employee(String name, int age, String company, String department) { super(name, age); this.company = company; this.department = department; } // ... }
Java
복사
⇒ Person 클래스와 Employee 클래스를 조합하여 사용하는 예시 코드. Employee 클래스는 Person 클래스를 상속하고 있으며, name과 age 필드는 Person 클래스에서 상속받아 사용하고, company와 department 필드는 Employee 클래스에서 추가로 정의하고 있다.
Hidden 클래스: Hidden 클래스 추가(preview)
다른 class의 bytecode에서 직접 사용할 수 없는 class
Hidden 클래스는 일반적으로 애플리케이션 클래스 패스(classpath)에 로드되지 않으며, 리플렉션을 통해 접근 가능.
Hidden 클래스는 자바 모듈 시스템에서 사용된다. 모듈 시스템은 모듈 경계를 정의하여 모듈 간의 의존성을 명확하게 관리할 수 있게 해준다. Hidden 클래스는 모듈 시스템에서 모듈 간의 의존성을 명확하게 관리하기 위한 목적으로 사용된다.
Hidden 클래스는 다음과 같이 선언할 수 있다.
public class Hidden { private Hidden() { throw new AssertionError(); } public static void doSomething() { // ... } // ... }
Java
복사
⇒ Hidden 클래스는 생성자가 private으로 선언되어 있어 외부에서 객체를 생성할 수 없다.
Hidden 클래스는 다음과 같은 특징을 가지고 있다.
public 클래스이며, public 생성자를 가지지만, 리플렉션을 통해 생성자를 호출할 수 없다.
Hidden 클래스에 선언된 메서드는 public이며, 리플렉션을 통해 접근 가능하다.
Hidden 클래스는 자바 모듈 시스템에서 사용되며, 일반적인 자바 클래스와는 다르게 애플리케이션 클래스 패스(classpath)에서 로드되지 않는다.
Foreign-Memory Access API
Java에서 직접 메모리를 조작할 수 있게 해준다. 이를 통해 C++와 같은 저수준 언어에서 가능한 메모리 조작을 Java에서도 가능하게 됩니다.
jdk.incubator.foreign 모듈에서 API를 제공하며 세 개의 추상 클래스를 제공한다.
MemorySegment, MemoryAddress, MemoryLayout
Native Memory를 할당하고, 해당 메모리에 문자열을 저장하고 출력하는 예제 코드
import jdk.incubator.foreign.*; public class Main { public static void main(String[] args) { try (MemorySegment segment = MemorySegment.allocateNative(1024)) { CLinker linker = CLinker.getInstance(); FunctionDescriptor printfDescriptor = FunctionDescriptor.of(CLinker.C_LONG_LONG, CLinker.C_POINTER); MethodHandle printfHandle = linker.downcallHandle( linker.lookup("printf").get(), printfDescriptor ); MemoryAccess.setByteAtOffset(segment, 0, (byte) 'H'); MemoryAccess.setByteAtOffset(segment, 1, (byte) 'e'); MemoryAccess.setByteAtOffset(segment, 2, (byte) 'l'); MemoryAccess.setByteAtOffset(segment, 3, (byte) 'l'); MemoryAccess.setByteAtOffset(segment, 4, (byte) 'o'); MemoryAccess.setByteAtOffset(segment, 5, (byte) ','); MemoryAccess.setByteAtOffset(segment, 6, (byte) ' '); MemoryAccess.setByteAtOffset(segment, 7, (byte) 'W'); MemoryAccess.setByteAtOffset(segment, 8, (byte) 'o'); MemoryAccess.setByteAtOffset(segment, 9, (byte) 'r'); MemoryAccess.setByteAtOffset(segment, 10, (byte) 'l'); MemoryAccess.setByteAtOffset(segment, 11, (byte) 'd'); MemoryAccess.setByteAtOffset(segment, 12, (byte) '!'); printfHandle.invokeExact("Memory contents: %s%n", segment.asString()); } } }
Java
복사
⇒ MemorySegment 클래스를 사용하여 네이티브 메모리를 할당하고,
⇒ MemoryAccess 클래스를 사용하여 메모리에 데이터를 쓴다. 또한, CLinker 클래스를 사용하여 printf 함수를 호출하고, 메모리에 저장된 데이터를 출력한다.
Garbage Collector 업데이트: Garbage Collector 개선
JDK 15에서는 G1 GC, ZGC, Shenandoah GC 등의 Garbage Collector가 각각 개선되었다..
ZGC는 더 많은 메모리를 지원하고,
Shenandoah GC는 일시 중지 시간이 더 짧아졌다.
문자열 노출 API 개선: Records 및 Enum 클래스의 toString() 메서드 개선
Records 클래스와 Enum 클래스에서 toString() 메서드를 호출할 경우, 각 필드와 값을 포함하는 문자열이 반환됩니다.
public record Person(String name, int age) { } Person person = new Person("John", 30); System.out.println(person.toString());
Java
복사
JDK 15는 2020년 9월에 출시.
이번 버전에서는 데이터 전용 클래스인 레코드 클래스와 Records 및 Enum 클래스의 toString() 메서드 개선이 추가 됌.

JDK 16

Record 클래스 업데이트
Sealed 클래스와의 조합이 가능해짐
예시 코드
public sealed interface Shape permits Circle, Rectangle { record Circle(int radius) implements Shape { } record Rectangle(int width, int height) implements Shape { } }
Java
복사
Record 클래스에 equals()와 hashCode() 구현 자동 생성
예시 코드
public record Person(String name, int age) { } Person person1 = new Person("John", 30); Person person2 = new Person("John", 30); System.out.println(person1.equals(person2)); // true
Plain Text
복사
⇒ Record 클래스에서 equals()와 hashCode()가 자동 생성된다.
Record 클래스에서 상속 가능한 클래스 선언 가능
Record 클래스에서 일반 클래스를 상속할 수 있게 되었다.
예시 코드
class Person { String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } // ... } record Employee(String company, String department) extends Person { public Employee(String name, int age, String company, String department) { super(name, age); this.company = company; this.department = department; } // ... }
Java
복사
Vector API (Incubator)
SIMD (Single Instruction, Multiple Data) 연산을 수행할 수 있는 새로운 API
선형 대수, 통계, 머신 러닝 등과 같은 분야에서 성능을 크게 향상시킬 수 있다.
vector 클래스를 사용하여 벡터 데이터를 만들고, vectorOp 클래스를 사용하여 벡터 연산을 수행한다.
예시 코드
import jdk.incubator.vector.*; public class Main { public static void main(String[] args) { VectorSpecies<Float> species = FloatVector.SPECIES_256; FloatVector a = FloatVector.broadcast(species, 1.0f); FloatVector b = FloatVector.broadcast(species, 2.0f); FloatVector c = FloatVector.broadcast(species, 3.0f); FloatVector d = a.mul(b).add(c); System.out.println(d); } }
Java
복사
FloatVector 클래스를 사용하여 벡터 데이터를 만들고, muladd 메서드를 사용하여 벡터 연산을 수행한다.
Foreign Linker API 업데이트
Java와 C/C++ 간의 상호 운용성을 위한 API
이 API를 사용하여 Java 애플리케이션에서 C/C++ 라이브러리를 사용할 수 있다.
예시 코드
import jdk.incubator.foreign.*; public class Main { public static void main(String[] args) { try (LibraryLookup lookup = LibraryLookup.ofDefault()) { FunctionDescriptor printfDescriptor = FunctionDescriptor.of(CLinker.C_LONG_LONG, CLinker.C_POINTER); SymbolLookup symbols = lookup.lookup("printf"); MethodHandle printfHandle = CLinker.getInstance().downcallHandle(symbols, printfDescriptor); printfHandle.invokeExact("Hello, World!\\n"); } } }
Java
복사
⇒ CLinker 클래스를 사용하여 printf 함수를 호출한다.
JEP 390: Warnings for Value-Based Classes
값 기반 클래스에서 오버라이딩 된 Object 메서드를 감지하는 경고 추가

JDK 17

Sealed 클래스 업데이트
JDK 17에서 Preview가 아닌 Standard가 되었다.
sealed 클래스의 경우 클래스의 상속 계층 구조를 미리 선언할 수 있도록 한다.
이를 통해 클래스의 확장성과 보안성을 높일 수 있다.
예시 코드
public sealed class Shape permits Circle, Rectangle, Triangle { // ... } final class Circle extends Shape { // ... } final class Rectangle extends Shape { // ... } non-sealed class Triangle extends Shape { // ... }
Java
복사
permits 키워드를 사용하여 각 하위 클래스를 정의한다.
Pattern Matching for switch 업데이트 (Preview)
switch 문에서 pattern matching을 사용할 수 있도록 한다.
예시 코드
public static void main(String[] args) { Object obj = "Hello, world!"; if (obj instanceof String s && s.length() > 10) { System.out.println(s.toUpperCase()); } else { System.out.println(obj); } }
Java
복사
objString 타입이고 길이가 10보다 큰 경우 대문자로 변환하여 출력한다.
Vector API 업데이트
Vector API에서는 새로운 연산이 추가되었다.
예시 코드
import jdk.incubator.vector.FloatVector; import jdk.incubator.vector.VectorOperators; public class Main { public static void main(String[] args) { var species = FloatVector.SPECIES_PREFERRED; var a = FloatVector.broadcast(species, 1.0f); var b = FloatVector.broadcast(species, 2.0f); var c = FloatVector.broadcast(species, 3.0f); var d = a.mul(b).add(c); var e = d.lanewise(VectorOperators.LOGARITHM_BASE_E); System.out.println(e); } }
Java
복사
VectorOperators.LOGARITHM_BASE_E를 사용하여 로그 값을 계산한다.
Foreign Function and Memory API 업데이트 (Incubator)
Foreign Function and Memory API에서는 새로운 기능이 추가되었다.
예시 코드
import jdk.incubator.foreign.MemoryAddress; import jdk.incubator.foreign.MemoryHandles; import jdk.incubator.foreign.MemoryLayouts; public class Main { public static void main(String[] args) { var layout = MemoryLayouts.ofSequence(10, MemoryLayouts.C_CHAR); var address = MemoryHandles.allocateArray(layout).address(); var data = "Hello, world!".getBytes(); for (int i = 0; i < data.length; i++) { MemoryAddress.setByteAtOffset(address, i, data[i]); } System.out.println(MemoryAddress.ofSequence(address, 0, data.length).getUtf8String()); } }
Java
복사
MemoryLayouts 클래스를 사용하여 메모리 레이아웃을 정의하고, MemoryHandles 클래스를 사용하여 메모리를 할당한다. 그리고 MemoryAddress 클래스를 사용하여 메모리에 데이터를 쓰고, 읽는다.
JEP 406: Pattern Matching for instanceof (Second Preview)
instanceof 연산자에서 pattern matching을 사용할 수 있도록 한다.
예시 코드
if (obj instanceof String s && s.length() > 10) { System.out.println(s.toUpperCase()); } else { System.out.println(obj); }
Java
복사
objString 타입이고 길이가 10보다 큰 경우 대문자로 변환하여 출력한다.