2025. 6. 17. 06:03ㆍJava
면접도 앞두고 있어 Java 8 버전 이후 새로운 기능이 어떤 것이 있었는지 정리해보겠다.
8 | Lambda Expression 및Method Reference 도입 | Language | anonymous inner class 표현 간략화 (함수형 표현)인자로 method reference를 전달 (함수형 표현) |
8 | Collections & Streams | Language | Collections에서 Streams API를 사용하여, 이전의 반복문이 아닌 함수형 구현 |
8 | Interface Default Method 도입 | Language | interface 내부에서 default 메서드를 선언할 수 있다. |
8 | Optional Class 도입 | Language | Optional Class를 이용하여 NullPointerException이 발생하는 것을 방지해 준다. |
9 | Module System & Jigsaw Project | Platform | Java 플랫폼 간소화 및 모듈화를 통해, 성능 및 사이즈 향상, 유연한 이미지 구성이 가능하도록 만들어 준다. |
9 | jlink | Tool | 모듈들을 조합하여 사용자 정의 런타임 이미지를 구성하는 도구 |
9 | Jshell - 대화형 커맨드 라인 툴(REPL) 도입 | Tool | Java 프로그래밍 학습과 Java 코드 프로토타이핑을 위한 대화식 도구 |
9 | Collection Factory Method 도입 | Core Libraries | 편리하게 콜렉션과 맵의 인스턴스를 생성 할 수 있는 새로운 정적 팩토리 메서드를 제공 |
9 | Streams 기능 추가 | Core Libraries | Stream 기능에 takeWhile,dropWhile,iterate 등의 추가 메서드 제공 |
9 | Interface Private Method 도입 | Language | interface 메서드들을 default, static이 아닌, private로 지정하여, interface에 대한 보안을 향상시킨다 |
9 | Linux/AArch64 지원 | Platform | 리눅스 ARM64 지원 |
9-11 | 새로운 HTTPClient API | Core Libraries | 기존에 제공된 낮은 수준의 API를 HTTP/2, WebSocket 기반의 최신 기능들로 업그레이드 |
10- | Java 기반의 JIT 컴파일러 (Graal VM) | Performance | 새로운 JIT 컴파일러 탑재 |
10 | var - 지역변수 type 추론 Keyword 도입 | Language | 변수의 타입을 컴파일러가 결정해 주는 var 변수 타입을 선언할 수 있다 |
10 | 어플리케이션 CDS(Class Data Sharing) 공유 | Performance | 어플리케이션 시작 속도 및 설치 공간을 개선하기 위해 기존 클래스 데이터 공유("CDS") 기능을 확장하여 애플리케이션 클래스를 공유 아카이브에 배치 |
10,8u191 | Docker 컨테이너 지원 | Platform | Docker 환경에 맞게 수정 |
11 | String & Files 에 새로운 Method 추가 | Language | String에 strip, isBlack, lines 등, Files에 readString 등 Method 추가 |
11 | Lambda 인수로 var 사용 가능 | Language | 람다 인수 선언시 var 키워드 사용 가능 |
11 | 소스 파일 실행 (javac 컴파일 불필요) | Platform | Java 소스 파일을 컴파일 하지 않고 스크립트로 실행 |
11-15 | ZGC - 차세대 Garbage Collector | Performance | 새로운 Garbage Collector 도입 |
12-14 | 개선된 switch문 표현방식 지원 | Language | switch문에서 "->" 사용으로, 코딩 간소화 |
12 | Default CDS 저장장소 | Performance | 어플리케이션 간에 공유되는 CDS (Class Data Sharing)에 대한 파일이 classes.jsa로 기본 저장 |
12 | Unicode 11 지원 | internalization | |
12-15 | Shenandoah: 멈춤 적은 가비지 컬렉터 | Performance | 멈춤 시간 적은 가비지 컬렉터 도입 |
12 | G1에서 사용하지 않는 메모리 빠른 반환 | Performance | G1 가비지 컬렉터에서 사용하지 않는 힙 메모리를 빠르게 반환한다 |
13-15 | Text Block 기능 도입 | Language | String에서 여러 줄의 문자열을 한번에 입력할 수 있다 |
13 | Unicode 12.1 지원 | internalization | |
13 | yield 예약어 사용 | Language | switch문에서 yield (switch의 return) 사용 |
14 | instanceof 패턴매칭 - 이후 캐스팅 불필요 | Language | instanceof 객체에 대한 캐스팅이 필요 없어졌습니다. |
14 | NullPointerException 기능 추가 | Platform | NullPointerExceptions이 발생할 때, Exception 메세지에 어떤 변수가 null 인지 알려줍니다. |
14-16 | 패키징 툴인 jpackage 도구 제공 | tool | Java 응용 프로그램을 해당 OS(Windows, Linux, Mac)에 맞게, 실행에 필요한 모든 라이브러리와 함께 패키징하는 jpackage 도구제공 |
14-16 | Record Class 도입 | Language | data를 다루는 record 클래스를 이용해서 간단하게 새로운 데이터 클래스 생성 |
15- | Sealed Class 도입 | Language | sealed 키워드를 이용하여 클래스의 상속을 제한할 수 있다. |
16 | Unix 도메인 소켓 채널 | core libraries | 소켓 채널 API에 유닉스 플랫폼과 Windows에서 흔히 볼 수 있는 유닉스 도메인 소켓의 모든 기능에 대한 지원을 추가 |
16 | Windows/AArch64 지원 | Platform | Windows ARM64 지원 |
16 | Alpine Linux 지원 | Platform | 작은 용량의 Alpine Linux 지원 |
16- | 외부 링커 API | Language | JNI(Java Native Interface)의 교체로, 네이티브 코드에 접근할 수 있는 API 제공 |
Optional (Java 8)
널 포인터 예외는 가장 전통적인 오류 가운데 하나다. 익숙한 문제지만 방지하기가 쉽지 않기도 하다. 그러나 자바 8에서 처음 소개되어 자바 10에서 더 개선된 Optional 클래스를 사용하면 더 이상 골칫거리가 아니다.
기본적으로 Optional 클래스는 변수를 래핑한 다음 래퍼의 메서드를 사용해서 널을 더 간편히 다룰 수 있게 해준다.
예시 1에는 흔한 널 포인터 오류의 예가 나와 있다. 클래스 레퍼런스인 foo가 널이고, 여기서 메서드인 foo.getName()이 액세스된다.
public class MyClass {
public static void main(String args[]) {
InnerClass foo = null;
System.out.println("foo = " + foo.getName());
}
}
class InnerClass {
String name = "";
public String getName(){
return this.name;
}
}
import java.util.Optional;
public class MyClass {
public static void main(String args[]) {
InnerClass foo = null; //new InnerClass("Test");
Optional fooWrapper = Optional.ofNullable(foo);
fooWrapper.ifPresent(x -> System.out.println("foo = " + x.getName()));
//System.out.println("foo = " + fooWrapper.orElseThrow());
}
}
class InnerClass {
String name = "";
public InnerClass(String name){
this.name = name;
}
public String getName(){
return this.name;
}
}
Optional을 사용할 때 orElse() 메서드를 사용하여 메서드 호출을 통해 기본값을 제공한다면 그 대신 orElseGet()을 사용해서 함수 참조를 제공하는 방법을 고려할 수 있다. 이렇게 하면 값이 널이 아닐 때 호출을 실행하지 않음으로써 성능상의 이득을 얻을 수 있다. 즉, orElse()는 값을 생성하여 orElseGet보다 비용이 크므로 최대한 사용을 피해야한다.
public void findUserEmailOrElse() {
String userEmail = "Empty";
String result = Optional.ofNullable(userEmail)
.orElse(getUserEmail());
System.out.println(result);
}
public void findUserEmailOrElseGet() {
String userEmail = "Empty";
String result = Optional.ofNullable(userEmail)
.orElseGet(this::getUserEmail);
System.out.println(result);
}
private String getUserEmail() {
System.out.println("getUserEmail() Called");
return "mangkyu@tistory.com";
}
// orElse경우
getUserEmail() Called
Empty
// orElseGet경우
Empty
orElse에 의한 발생가능한 장애 예시
orElse와 orElseGet은 명확하고 중요한 차이가 있는데, 이를 정확하게 인식하지 않으면 장애가 발생할 수 있다. userEmail을 unique한 값으로 갖는 시스템에서 아래와 같이 작성했다고 가정하자.
orElse로 작성했을 경우
public void findByUserEmail(String userEmail) {
// orElse에 의해 userEmail이 이미 존재해도 유저 생성 함수가 호출되어 에러 발생
return userRepository.findByUserEmail(userEmail)
.orElse(createUserWithEmail(userEmail));
}
private String createUserWithEmail(String userEmail) {
User newUser = new User(userEmail);
return userRepository.save(newUser);
}
위의 예제는 Optional의 단말 연산으로 orElse를 사용하고 있기 때문에, 조회 결과와 무관하게 createUserWithEmail 함수가 반드시 실행된다. 하지만 Database에서는 userEmail이 Unique로 설정되어 있기 때문에 오류가 발생할 것이다. 그렇기 때문에 위와 같은 경우에는 다음과 같이 해당 코드를orElseGet으로 수정해야 한다. 이렇게 코드를 수정하였다면 파라미터로 createUserWithEmail 함수 자체가 넘어가므로, 조회 결과가 없을 경우에만 사용자를 생성하는 로직이 호출 될 것이다.
public void findByUserEmail(String userEmail) {
// orElseGet에 의해 파라미터로 함수를 넘겨주므로 Null이 아니면 유저 생성 함수가 호출되지 않음
return userRepository.findByUserEmail(userEmail)
.orElseGet(this::createUserWithEmail(userEmail));
}
private String createUserWithEmail(String userEmail) {
User newUser = new User(userEmail);
return userRepository.save(newUser);
}
Record 클래스 (Java 16)
자바 앱 빌드에서 흔히 필요한 부분은 불변성 DTO(데이터 전송 객체)다. DTO는 데이터베이스, 파일 시스템 및 기타 데이터 저장소에서 데이터를 모델링하는 데 사용된다. 전통적으로 DTO는 getter가 액세스하는 일 없이 멤버가 생성자를 통해 설정되는 클래스를 만드는 방법으로 생성된다. 자바 14에서 처음 도입되어 자바 15에서 개선된 새로운 record 키워드가 바로 이 목적을 위한 간편한 방법을 제공한다.
record가 도입되기 전
public class MyClass{
public static void main(String[] args){
Pet myPet = new Pet("Sheba", 10);
System.out.println(String.format("My pet %s is aged %s", myPet.getName(), myPet.getAge()));
}
}
public class Pet{
String name;
Integer age;
public Pet(String name, Integer age){
this.name = name;
this.age = age;
}
public String getName(){
return this.name;
}
public Integer getAge(){
return this.age;
}
}
record 도입 후
public class MyClass{
public static void main(String[] args){
Pet myPet = new Pet("Sheba", 10);
System.out.println(String.format("My pet %s is aged %s", myPet.getName(), myPet.getAge()));
}
}
public record Pet(
String name,
Integr age
) {
}
데이터 객체를 사용하는 클라이언트 코드가 변경되지 않은 것을 볼 수 있다. 일반적으로 정의되는 객체와 똑같이 작동한다. record 키워드는 매우 똑똑해서, 간단한 정의를 통해 어떤 필드가 존재하는지를 추론한다.
또한 record 형식은 equals(), hashCode(),toString()의 기본 구현을 정의하며, 개발자가 이러한 기본 구현을 재정의하도록 허용한다. 맞춤형 생성자를 제공할 수도 있다.
참고로 record는 서브클래스가 불가능하다.
새로운 String 클래스 (isBlank, lines - Java10, indent - Java12)
자바 10과 자바 12에서 유용한 String 메소드가 여러개 추가됐다.
문자열 조작 메소드 외에 텍스트 파일 엑세스를 최소화하기 위해 2개의 새로운 메소드가 도입됐다.
자바 10의 새로운 String메소드
- isBlank() : 문자열이 비어있거나 공백만 포함된 경우 true를 반환
- (문자열 길이가 0인 경우에만 true를 반환하는 isEmpty()와는 다름)
System.out.println("".isEmpty() + "," + "".isBlank()); // true, true
System.out.println(" ".isEmpty() + "," + " ".isBlank()); // false, true
- lines() : 문자열을 각각 라인 하나를 포함한 여러 문자열 스트림으로 분할.
- 라인은 /r 또는 /n 또는 /r/n으로 정의
import java.io.IOException;
import java.util.stream.Stream;
public class MyClass {
public static void main(String args[]) throws IOException{
String str = "test \\ntest2 \\n\\rtest3 \\r";
Stream lines = str.lines();
lines.forEach(System.out::println);
}
}
/*
outputs:
test
test2
test3
*/
- strip(), stripLeading(), stripTrailing() : 각각 처음과 끝, 처음, 그리고 끝의 공백을 제거한다.
- repeat(int times) : 원래의 문자열을 받아서 지정된 횟수만큼 반복하는 문자열을 반환
- readString() : 파일 경로에서 문자열을 직접 읽어들일 수 있도록 한다
Path path = Path.of("myFile.txt");
String text = Files.readString(path);
System.out.println(text);
- writeString(Path path) : 지정된 경로의 파일에 직접 문자열을 쓴다.
자바 12의 새로운 String 메서드
- indent(int level) : 지정한 만큼 문자열을 들여쓰기한다. 음수 값은 선행 공백에만 영향을 미친다.
- transform(Function f) : 지정된 람다를 문자열에 적용한다.
Switch 식 (Java12 - break필요성을 없애는 화살표 구분, Java13 - yield키워드)
자바 12에는 switch를 문 내에서 인라인으로 사용할 수 있게 해주는 switch 식이 도입됐다. 즉, switch 식은 값을 반환한다. 또한 자바 12는 명시적인 break의 필요성을 없애는 화살표 구문도 제공한다. 자바 13은 한 걸음 더 나아가 switch 케이스가 반환하는 값을 명시적으로 나타내는 yield 키워드를 도입했다. 자바 14에서는 새로운 switch 식이 완전한 기능으로 도입됐다.
몇 가지 예를 보자. 먼저 아래는 전통적인(자바 8) 형식으로 된 switch 문의 (상당히 부자연스러운) 예가 나와 있다. 이 코드는 변수(message)를 사용해서 숫자의 이름(알려진 경우)을 출력한다.
class Main {
public static void main(String args[]) {
int size = 3;
String message = "";
switch (size){
case 1 :
message = "one";
break;
case 3 :
message = "three";
break;
default :
message = "unknown";
break;
}
System.out.println("The number is " + message);
}
}
break필요성을 없애는 화살표 구문 제공
class NewSwitch {
public static void main(String args[]) {
int size = 3;
System.out.println("The number is " +
switch (size) {
case 1 -> "one";
case 3 -> "three";
default -> "unknown";
}
);
}
}
switch 식은 System.out.println 호출 안에 들어가 있다. 이것만으로 가독성 측면에서 큰 진전이며 불필요한 message 변수도 없어진다. 또한 화살표 구문은 break 문을 제거해 코드의 양을 줄여준다.
함수의 return과 같은 yield키워드
public void test(Day day) {
int cnt = switch (day) {
case MONDAY -> 0;
case TUESDAY -> yield 1; // error! yield는 block 안에서만 유효다
case WEDNESDAY -> { yield 2;} // ok
default -> 0;
}
}
텍스트 블록 (Java13)
자바 13은 텍스트 블록을 통해 자바에서 복잡한 텍스트 문자열을 다룰 때의 오랜 불편함을 해소했다. 자바 14는 텍스트 블록 지원을 더 개선했다.
JSON, XML, SQL 등의 여러 중첩된 이스케이프 계층을 다루다 보면 정신을 차리기가 어렵다. 사양에는 다음과 같이 설명돼 있다.
자바에서 HTML, XML, SQL 또는 JSON 스니펫을 문자열 리터럴 안에 내장하는 경우 이 스니펫이 포함된 코드가 컴파일되려면 일반적으로 먼저 이스케이프 및 연결과 관련해 상당한 편집이 필요하다. 스니펫은 많은 경우 읽기가 어렵고 유지보수도 까다롭다.
아래는 새로운 텍스트 블록 구문을 사용해 JSON 스니펫을 만드는 것을 볼 수 있다.
class TextBlock {
public static void main(String args[]) {
String json = """
{
"animal" : "Quokka",
"link" : "<https://en.wikipedia.org/wiki/Quokka>"
}
""";
System.out.println(json);
}
}
Sealed 클래스 (Java15)
자바 15(JEP 260)에는 sealed 클래스 개념이 도입됐다. 새로운 sealed 키워드는 인터페이스 서브클래스가 가능한 클래스를 정의할 수 있게 해준다. 한 가지 예를 보는 것이 천 마디 설명보다 낫다.
public abstract sealed class Pet
permits Cat, Dog, Quokka {...}
여기서 인터페이스 디자이너는 sealed 키워드를 사용해서 Pet 클래스를 확장하도록 허용된 클래스를 지정한다.
아.. 면접질문이 뭐가 나올까.. 정리를 하면 할수록 계속 정리할게 생긴다.. 제발 .. 붙고 싶다 ..
'Java' 카테고리의 다른 글
Synchronized 키워드가 어디에 붙는지에 따라 의미가 약간씩 변화한다고 ? (0) | 2025.06.19 |
---|---|
JVM (0) | 2025.02.27 |
java동작 과정 (0) | 2024.01.16 |