JAVA 버전 별 변경 사항 - 자바 개발자가 알아야 할 필수 개념

상윤·2024년 9월 4일
0

BackEnd

목록 보기
9/11
post-thumbnail

⭐JDK 8

JDK 8 oracle 공식문서

  • Lambda
  • Stream
  • interface default Method
  • Optional
  • LocalDateTime

Lambda (람다 표현식)
익명 함수를 단순하고 간결하게 작성할 수 있도록 도와준다. 이는 가독성을 높이고, 함수형 인터페이스와 함께 사용할 때 유용하다.

// 기존 익명 클래스 방식
Runnable oldRunnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, world!");
    }
};

// 람다 표현식 사용
Runnable lambdaRunnable = () -> System.out.println("Hello, world!");

lambdaRunnable.run();

Stream API
컬렉션 데이터에 대한 복잡한 작업(필터링, 매핑, 축소 등)을 간결하게 처리할 수 있도록 도와준다. 반복(iteration)을 통해 작업을 처리하므로, 병렬 처리를 쉽게 할 수 있는 장점이 있다. (단점으론 병렬처리로 cpu를 많이 사용하게 된다.)

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

// 필터링과 매핑을 사용한 스트림 처리
List<String> filteredNames = names.stream()
    .filter(name -> name.startsWith("A"))
    .map(String::toUpperCase)
    .collect(Collectors.toList());

System.out.println(filteredNames); // 출력: [ALICE]

interface default Method
기존 interface는 추상 메소드만을 정의할 수 있었는데 default 선언을 통해 method를 미리 정의해둘 수 있다. 또한 정의된 default 메소드는 상속된 class에서 오버라이딩 될 수 있다.

다음과 같이 default 메소드로 선언되면 상속받은 class에서 따로 구현하지 않아도 exec 메소드를 사용할 수 있으며 필요시 오버라이드 하여 사용할 수 있다.

public interface Calculator {
	public int plus(int i, int j);
	public int multiple(int i, int j);
	default int exec(int i, int j){      //default로 선언함으로 메소드를 구현할 수 있다.
		return i + j;
	}
}

//Calculator인터페이스를 구현한 MyCalculator클래스
public class MyCalculator implements Calculator {

	@Override
	public int plus(int i, int j) {
		return i + j;
	}

	@Override
	public int multiple(int i, int j) {
		return i * j;
	}
}

public class MyCalculatorExam {
	public static void main(String[] args){
		Calculator cal = new MyCalculator();
		int value = cal.exec(5, 10);
		System.out.println(value);
	}
}

Optional
Optional은 자바 8에서 도입된 클래스 중 하나로, null 값을 안전하게 처리하기 위해 사용한다. Optional을 사용하면 null을 직접 다루지 않아도 되며, 값의 존재 여부에 따라 안전하게 작업을 수행할 수 있다.

import java.util.Optional;

public class OptionalExample {
    public static void main(String[] args) {
        Optional<String> optionalValue = Optional.of("Hello, Optional!");

        // 값이 존재하면 처리
        optionalValue.ifPresent(System.out::println); // 출력: Hello, Optional!

        // 값이 존재하지 않으면 대체 값 반환
        String defaultValue = optionalValue.orElse("Default Value");
        System.out.println(defaultValue); // 출력: Hello, Optional!

        // 값이 존재하면 가져오고, 없으면 예외 발생
        String value = optionalValue.orElseThrow(() -> new IllegalArgumentException("No value present"));
        System.out.println(value); // 출력: Hello, Optional!
    }
}

LocalDateTime
날짜 및 시간 API인 java.time 패키지는 LocalDateTime, LocalDate, LocalTime과 같은 클래스를 통해 더 직관적이고 사용하기 쉬운 날짜 및 시간 처리를 제공한다.

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class LocalDateTimeExample {
    public static void main(String[] args) {
        // 현재 날짜와 시간 가져오기
        LocalDateTime now = LocalDateTime.now();
        System.out.println("Current DateTime: " + now);

        // 특정 날짜와 시간 생성
        LocalDateTime specificDateTime = LocalDateTime.of(2024, 9, 2, 10, 30);
        System.out.println("Specific DateTime: " + specificDateTime);

        // 날짜와 시간 포맷팅
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String formattedDateTime = now.format(formatter);
        System.out.println("Formatted DateTime: " + formattedDateTime);

        // 날짜와 시간의 계산
        LocalDateTime tomorrow = now.plusDays(1);
        System.out.println("Tomorrow's DateTime: " + tomorrow);
    }
}

JDK 9

  • 모듈화(Jigsaw)
  • streamtakeWhile, dropWhile, iterate 등 추가
  • optionalifPresentOrElse 추가
  • interfaceprivate method 추가

jigsaw
기존 java의 jar 패키징을 통해 모듈화를 진행하고 있지만, Jar Hell이 복잡한 ClassLoader로 정의시 JVM 컴파일은 성공하지만 런타임시 ClassNotFonundException을 마주하게 된다. 또한 Jar 자체가 무겁다. 때문에 gsaw라는 새로운 모듈화를 통해 가볍고 복잡하지 않은 java 모듈 시스템을 구축한 것이다.
이를 통해 애플리케이션을 더 작은 모듈로 나누어 관리할 수 있게 하며, 더 나은 접근 제어와 패키지 구조를 제공하고, 애플리케이션의 구조를 명확하게 하고, 모듈 간의 의존성을 명시적으로 관리할 수 있다.

  • 모듈은 module-info.java 파일로 정의
  • 각 모듈은 자신이 제공하는 패키지와 다른 모듈에서 사용할 패키지를 명시적으로 선언
// module-info.java
module com.example.myapp {
    exports com.example.myapp.api;
    requires java.base;
}

Stream API의 새로운 메서드 추가: takeWhile, dropWhile, iterate

  • takeWhile: 조건이 참인 동안 스트림의 요소를 수집합니다.
  • dropWhile: 조건이 참인 동안의 요소를 건너뛰고, 이후의 요소만을 수집합니다.
  • iterate: 람다 표현식을 사용하여 스트림의 요소를 생성합니다.
import java.util.List;
import java.util.stream.Stream;

public class StreamExample {
    public static void main(String[] args) {
        List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // takeWhile 사용 예시
        List<Integer> taken = numbers.stream()
            .takeWhile(n -> n < 5)
            .toList();
        System.out.println(taken); // 출력: [1, 2, 3, 4]

        // dropWhile 사용 예시
        List<Integer> dropped = numbers.stream()
            .dropWhile(n -> n < 5)
            .toList();
        System.out.println(dropped); // 출력: [5, 6, 7, 8, 9, 10]

        // iterate 사용 예시
        Stream<Integer> iteratedStream = Stream.iterate(1, n -> n + 1)
            .limit(10);
        iteratedStream.forEach(System.out::println); // 1부터 10까지 출력
    }
}

Optional의 ifPresentOrElse 메서드

  • ifPresentOrElse: Optional에 값이 존재하면 지정된 작업을 실행하고, 값이 없으면 다른 작업을 실행
import java.util.Optional;

public class OptionalExample {
    public static void main(String[] args) {
        Optional<String> optionalValue = Optional.of("Hello, JDK 9!");

        optionalValue.ifPresentOrElse(
            value -> System.out.println("Value is present: " + value),
            () -> System.out.println("Value is absent")
        );

        Optional<String> emptyOptional = Optional.empty();

        emptyOptional.ifPresentOrElse(
            value -> System.out.println("Value is present: " + value),
            () -> System.out.println("Value is absent")
        ); // 출력: Value is absent
    }
}

Interface의 Private Method
interface에서 private method를 사용한다는 의미는 기존 interface에서 정의된 메서드는 상속받은 class에서 구현해야했다. 아니면 상속 받은 그대로 사용하던지 하지만 private method는 interface 내부에서만 사용하며 상속 받은 class는 해당 메서드를 따로 구현할 수 없는 것이다.

예를 들어 다음 코드를 봤을 때 method2()에서 method4()를 호출하여 사용하지만 실제 상속받은 CustomClass 에서 method4()를 오버라이딩할 수 없다.

  • private 메서드는 인터페이스 내에서만 사용될 수 있으며, 구현 클래스에서는 접근할 수 없습니다.
public interface CustomInterface {
     
    public abstract void method1();
     
    public default void method2() {
        method4();  //private method inside default method
        method5();  //static method inside other non-static method
        System.out.println("default method");
    }
     
    public static void method3() {
        method5(); //static method inside other static method
        System.out.println("static method");
    }
     
    private void method4(){
        System.out.println("private method");
    } 
     
    private static void method5(){
        System.out.println("private static method");
    } 
}

public class CustomClass implements CustomInterface {
 
    @Override
    public void method1() {
        System.out.println("abstract method");
    }
     
    public static void main(String[] args){
        CustomInterface instance = new CustomClass();
        instance.method1();
        instance.method2();
        CustomInterface.method3();
    }
}

JDK 10

  • var 추가

  • Garbage Collector(GC) 병렬 처리 도입으로 인한 성능 향상

  • JVM heap 영역을 시스템 메모리가 아닌 다른 종류의 메모리에도 할당 가능

    var 키워드의 도입

  • var는 로컬 변수에만 사용할 수 있으며, 클래스 필드, 메소드 매개변수 또는 리턴 타입으로는 사용할 수 없다.

  • 변수의 초기화 시점에 타입이 명확해야 하며, 추론된 타입은 변하지 않는다.

public class VarExample {
    public static void main(String[] args) {
        var message = "Hello, JDK 10!";
        var number = 10;
        var list = List.of("Apple", "Banana", "Cherry");

        System.out.println(message);  // 출력: Hello, JDK 10!
        System.out.println(number);   // 출력: 10
        System.out.println(list);     // 출력: [Apple, Banana, Cherry]

        // 컴파일러가 message 변수를 String으로 추론
        System.out.println(message.getClass().getSimpleName()); // 출력: String
    }
}

Garbage Collector(GC) 병렬 처리 도입으로 인한 성능 향상

  • Parallel GC는 GC 작업을 여러 스레드로 병렬 처리하여, GC로 인한 애플리케이션의 중단 시간을 줄입니다.
    이는 특히 대규모 애플리케이션에서 메모리 관리 성능을 크게 향상시킵니다.

JVM heap 영역을 시스템 메모리가 아닌 다른 종류의 메모리에도 할당 가능

JVM의 힙 메모리를 특수 메모리 영역에 할당할 수 있게 되면서, 대규모 데이터 처리나 고성능 컴퓨팅에서 더 나은 성능을 발휘할 수 있습니다. 이를 위해 JVM 옵션을 사용해 메모리 할당을 구성할 수 있습니다.

JDK 11

  • LTS(Long Term Support)
  • Oracle JDK와 OpenJDK 통합
  • Oracle JDK가 구독형 유료 모델로 전환
  • Lambda에 var 사용 가능
  • Java 10부터 Java 소스 파일 을 먼저 컴파일 하지 않고도 실행할 수 있다. 이전 java는 소스를 컴파일하여 class로 뽑은 후 class를 실행했는데 java를 통해 바로 실행할 수 있게 되었다.

JDK 12

  • switch문 확장
  • 유니코드 11 지원

확장된 switch문
case문에 여러 경우를 넣을 수 있다.

// JDK 12 이전
String time;
switch (weekday) {
	case MONDAY:
	case FRIDAY:
		time = "10:00-18:00";
		break;
	case TUESDAY:
	case THURSDAY:
		time = "10:00-14:00";
		break;
	default:
		time = "휴일";
}

// JDK 12 이후
String time = switch (weekday) {
	case MONDAY, FRIDAY -> "10:00-18:00";
	case TUESDAY, THURSDAY -> "10:00-14:00";
	default -> "휴일";
};

JDK 13

  • 유니코드 12.1 지원
  • switch 개선
  • Multi line String

개선된 switch
변수를 선언하여 해당 변수에 switch문으로 값을 리턴 할 수 있다.

boolean result = switch (status) {
    case SUBSCRIBER -> true;
    case FREE_TRIAL -> false;
    default -> throw new IllegalArgumentException(*"something is murky!"*);
};

개선된 multi line string

//Java 13 이전
String htmlBeforeJava13 = "<html>\n" +
              "    <body>\n" +
              "        <p>Hello, world</p>\n" +
              "    </body>\n" +
              "</html>\n";
              
 //Java 13 이후
String htmlWithJava13 = """
              <html>
                  <body>
                      <p>Hello, world</p>
                  </body>
              </html>
              """;             

JDK 14

  • switch 표준화
  • instanceof 개선
  • recode 선언 기능 추가
  • NullPointerException 개선

switch 표준화

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    default      -> {
      String s = day.toString();
      int result = s.length();
      yield result;
    }
};

//- default 코드 마지막라인에서 yield는 return을 의미함

instanceof 개선

이전에는 obj를 캐스팅 하는 것이 필요했지만 instanceof 연산자 뒤에 선언까지 가능해졌다.

// JDK 14 이전
if (obj instanceof String) {
    String s = (String) obj;
}


java
// JDK 14 이후
if (obj instanceof String s) {
    System.out.println(s.contains(*"hello"*));
}

record 선언

JDK 14 이전에는 다음과 같은 클래스 선언시 equals, hashcode, toString 이 잠재적으로 포함되어 있었다.

// Java 14 이전
final class Point {
    public final int x;
    public final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

//JDK 14 이후 record를 이용하여 간단하게 선언할 수 있게 되었다.
// Java 14 이후
record Point(int x, int y) { }

NullPointerException 개선
자바 개발자의 고질적인 예외인 NullPointerException이 개선되어, 어떤 변수가 Null인지를 설명해준다.
다음의 예외 메시지로 이전 버전과의 차이점을 알 수 있다.

author.age = 35;
---
Exception in thread "main" java.lang.NullPointerException:
     Cannot assign field "age" because "author" is null

JDK 15

  • 스케일링 가능한 낮은 지연의 가비지 컬렉터 추가(ZGC)
  • Sealed Classes
  • Nashorn JavaScript Engine 제거
    • java에서 javaScript를 실행할 수 있었던 Engine

Sealed Classes
한글로 직역하면 봉인된 클래스로 Java에서 자주 사용하던 상속을 제한할 수 있다.

public sealed interface SafetyBelt permits Car, Truck {
    String belt();
}

public final class Car implements SafetyBelt {

    @Override
    public String belt() {
        // TODO Auto-generated method stub
        return null;
    }
    
}

public final class Truck implements SafetyBelt {

    @Override
    public String belt() {
        // TODO Auto-generated method stub
        return null;
    }
    
}

위 코드와같이 SafetyBelt는 Car, Truck 에서 사용할 수 있도록 허용했고, 실제 상속받아서 belt 메소드를 구현한다. 참고로 상속받은 class는 추가 확장을 방지하기 위해 final로 선언한다.

public class Temp extends Car {  // ERROR: Car의 상속이 막혀있으므로 추가 확장시 오류 발생
}

public interface Vehicle implements SafetyBelt {  // 컴파일 ERROR 발생
}

이 sealed class 기능은 인터페이스 외에도 abstract class 에서도 사용할 수 있다.

JDK 16

  • Unix-Domain Socket Channels 사용 가능 (Mac 및 Windows(10+)에도 지원)

⭐JDK 17

  • Java 11 이후 새로운 LTS
  • Spring Boot 3.x.x 버전은 해당 버전부터 지원 가능
  • Pattern Matching for switch
  • Sealed Classes (Finalized)
    • Java 15에서 제공되었던 Sealed Classes 기능 완료
  • Foreign Function & Memory API
    • Java Native Interface(JNI)를 대체
  • Deprecating the Security Manager
    • Java 1.0 이후로 보안 관리자가 있었는데 현재 사용되지 않으므로 제거될 예정
    Pattern Matching for switch
// Pattern Matcing for Switch 적용된 코드 
public String test(Object obj) {
    return switch(obj) {
      case Integer i -> "An integer";
      case String s -> "A string";
      case Cat c -> "A Cat";                 // Cat 이라는 클래스를 선언했을 경우
      default -> "I don't know what it is";
    };
}


// JDK 17 이전 코드 
public String test2(Object obj) {
		if (obj instanceof Integer) {
        return "An integer";
    } else if (obj instanceof String) {
        return "A string";
    } else if (obj instanceof Cat) {         // Cat 이라는 클래스를 선언했을 경우
        return "A Cat";
    } else {
        return "I don't know what it is";
    }
}

⭐JDK 21

  • 2023년 9월 릴리즈
  • Spring Boot 3.2 부터 지원 가능
  • UTF-8이 기본값으로 사용
  • Pattern Matching
  • Virtual Threads

Oracle Java Docs

JDK Release Notes

JDK Release Notes

Oracle Java SE Support

JDK Java SE Support

0개의 댓글