Java Version별 특징을 알아보자

byeol·2023년 6월 20일

요즘 공부하면서 새롭게 도입된 기능과 문법에 대해서 제가 제대로 알지 못한다는 생각이 들어 전체적인 큰 그림을 그리고자 포스팅을 하게 되었습니다.

최근에 var과 Stream, record에 대한 것들을 접하면서
Java 버전 별로 어떤 문법들이 도입되었는지
어떻게 개발자를 편하게 했는지에 대해서 정리해야 겠다는 생각이 들었습니다.

그래서 오늘은 Java Version별 특징을 정리해보려고 합니다.

✅ Java 7

🎁 Type Inference (타입 추론)

//7버전 이전
List<String> list = new ArrayList<String>();
// 7버전 이후
List<String> list = new ArrayList<>();

🎁 Swtich문 문자열 허용

switch(fruit){
  case "사과" :
    ...
    break;
  case "수박" :
    ...
    brak;
  default :
    ...
    break;
}

🎁 try-with-resources

7버전 이전에는 무조건 finally 블록을 통해서 리소스 해제가 가능했지만
이후에는 try-catch를 사용해도 자동으로 리소스가 해체되도록 변경되었습니다.

🎁 try-catch문에서 멀티 캐치

예외 상황을 여러개 줄 수 있습니다.
즉 하나의 catch 안에 논리연산자 or에 해당하는 '|'를 사용하여 여러개의 예외를 연결할 수 있습니다.

tyr{

} catch(예외1 | 예외2 | 예외3 e) {

}

🧨 몇 가지 주의사항이 있습니다.

  • Multi Catch문에 사용된 예외들은 예외의 상속관계에서 부모와 자식 관계에 있으면 안됩니다. 굳이 2개로 표현할 필요없이 부모에 해당하는 예외만 언급해도 되기 때문입니다.
  • Multi Catch문에 사용된 예외들은 공통된 조상의 멤버만 사용할 수 있습니다. catch문 안에는 하나의 참조변수로 예외를 처리합니다. 따라서 이 참조변수가 가키리는 곳이 어디인지 알기 위해서 공통된 조상을 가지고 있어야 합니다.

🎁 ForkJoinPool

이전에는 분할 정복 방식으로 작업하기 위해서 관련 코드를 작성해야 했지만 7버전 이후에는 ForkJoinPool 클래스가 제공되었습니다.

✅ Java 8

🎁 Lambda Expression

단일 추상 메서드를 포함하는 인터페이스를 구현할 수 있는 람다 표현식이 도입되었습니다. 이후 함수형 프로그래밍, 스트림 API 그리고 컬렉션 프레임워크의 개선에 영향을 주었습니다.

public interface Operate {
    // 추상 메서드가 하나이다
    int operate(int a, int b);

    // default 메서드는 추상 메서드에 포함되지 않는다
    default void print() {
        System.out.println("출력");
    }
}
Operate operate = (a, b) -> {
    print();
    return a + b;
};

🎁 Method Reference

특정 메서드만을 호출하는 람다의 축약형입니다.
새로운 기능이 아니라 하나의 메서드를 참조하는 람다를 편리하게 표현할 수 있는 문법입니다.

람다메서드 레퍼런스
(Soccer s) -> s.getGoal()Soccer::getGoal
()-> Thread.currentThread().dumpStack()Thread.currentThread()::dumpStack
(str,i) -> str.substring(i)String::substring
(String s) -> System.out.println(s)System.out::println

🎁 interface에 default와 static 키워드 사용

interface에도 구현체 작성이 가능하게 되었습니다.
위 예시에서도 확인 가능합니다.

🎁 Null 처리 Optional 클래스

Java에서는 null을 다루는게 까다롭습니다. NPE에 대한 예외처리를 신경써야 했습니다. 그래서 8버전 이후부터 null 체크를 깔끔하게 해줄 수 있는 Optional 클래스가 도입되었습니다.

//Optional 구조체 생성
Optional<String> optionalStr = Optional.empty();
Optional<String> optionalStr = Optional.of("str");
Optional<String> optionalStr = Optional.ofNullable("str");

// 이전
String value = null;
String result = "";
try{
   result = value.toUpperCase();
} catch (NullPointException e) {
  throw new Exception();
}

// 이후
String value = null;
Optional<String> result = Optional.ofNullable(value);

🎁 Collections와 Streams

List를 반복문 대신 깔끔하고 편한게 해주는 Stream API를 사용할 수 있게 되었습니다.

public List<LocationResponse> autoCompleteLocation(String fullAddress, Pageable pageable){
        return locationRepository.findAllLocation(fullAddress,pageable).stream()
                .map(LocationResponse::buildLocationResponse)
                .collect(Collectors.toList());

🎁 PermGen Area 제거

이 부분이 흥미로웠습니다.

Method 영역에 클래스에 대한 메타 정보( 클래스의 이름, 생성정보, 필드정보, 메서드 정보 등)와 정적 멤버 변수 그리고 interned String이 저장됩니다.

이 Method 영역은 Permenent Generation이 관리하는 한 영역이며 Permenent Generation 영역의 크기가 제한적이라는 것에 문제가 발생했습니다.

  • 클래스 로딩이 많아지면 메모리가 부족해져 아래와 같은 에러가 발생합니다.
    java.lang.OutOfMemoryError: PermGen space
  • 사이즈를 유지시키 위해서 GC의 연산을 수행해야 하고 이는 비용이 발생합니다.
  • 직접 Permenent Generation의 크기를 늘려줄 수 있지만 자동으로는 불가능하고 크기를 예측하기 어렵습니다.

그래서 자바 8 버전 이후부터 PermGen(Permenent Generation) Area가 제거되었습니다.

이 영역이 제거되고 MetaSpace 영역이 등장하게 되었고 이는 heap이 아닌 Native Memory 영역으로 옮겨가 이제는 JRE가 아닌 OS가 관리하는 메모리 대상이 되었습니다.

하지만 기존에 PermGen이 했던 모든 역할이 MataSpace로 옮겨간 것은 아니고
클래스 메타 정보만 MetaSpace에서 관리하고
정적 멤버 변수와 interned String은 heap에서 관리되도록 쪼개졌습니다.

✅ Java 9

🎁 Jigsaw 등장

사실 이건 무슨 말인지 모르겠습니다.
공부하다가 추후에 알게 된다면 추가하겠습니다.

🎁 새로운 HTTP Client 패키지 등장

HttpURLConnection을 대체하기 위해서 java.net.http 패키지가 추가되었습니다.

java.net.http는 HTTP에 대한 상위 클라이언트 인터페이스와 WebSocket에 대한 하위 클라이언트 인터페이스를 제공합니다.
정의된 주요 유형은 아래와 같습니다.

  • HttpClient
  • HttpRequest
  • HttpResponse
  • WebSocket

🎁 List, Set, Map을 쉽게 구성할 수 있는 기능 추가

List<String> list = List.of("one", "two", "three");
Set<String> set = Set.of("one", "two", "three");
Map<String, String> map = Map.of("foo", "one", "bar", "two");

🎁 스트림에 몇 가지 메서드 추가

takeWhile, dropWhile, iterate 메서드 형태가 추가되었습니다.

Stream<String> stream = Stream.iterate("", s -> s + "s")
  .takeWhile(s -> s.length() < 10);

🎁 Optional To Stream

Optional을 포함하는 스트림을 쉽게 처리할 수 있도록 Optional에 stream()이 추가되었습니다.

Optional<Menu> selectedMenu = input.enterMenu();
        selectedMenu.ifPresentOrElse(
                menu -> run(menu),
                () -> output.viewMenuInputError());

🎁 interface에 private 메서드 사용

interface NewInterface {
    
    private static String staticPrivate() {
        return "static private";
    }
}

✅ Java 10

🎁 로컬 변수 타입 추론 기능인 var

아래 상황에서만 사용될 수 있습니다.

  • initializer와 함께 선언되는 로컬 변수
  • 향상된 for문 내 index
  • 전통적인 for문 안에서의 로컬 변수

즉 추론이 가능한 상황에서 사용이 가능합니다.

 var customerId = UUID.randomUUID();
 var numList = List.of(1,2,3,400);
 for(var number : numList) {
    System.out.println(number);
 }

🎁 Garbage Collector Interface, Thread-Local Handshakes, Root Certificates

이 3가지 키워드는 제가 아직 이해하지 못해서 추후에 추가하도록 하겠습니다.

✅ Java 11

🎁 String에 새로운 메소드 추가

매소드기능
strip()문자열 앞, 뒤 공백 제거
stripLeading()문자열 앞의 공백 제거
stripTrailing()문자열 뒤의 공백 제거
isBlank()문자열이 비어있거나 공백만 포함되어 있을 경우 true를 반환
lines()문자열을 라인 단위로 쪼개는 스트림을 반환
repeat(n)지정된 수 만큼 문자열을 반복하여 붙여 반환
String str = "A \n B \n C \n D";

Stream<String> lines = str.lines();

lines.forEach(System.out::println);

🎁 Local-Variable Syntax for Lambda Parameters

list.stream()
    .map((var s) -> s.toLowerCase())
    .collect(Collectors.toList());

근데 제가 느꼈을 때는 굳이? 라는 생각이 들었습니다.
없는 편이 더 간단하고 어차피 추론이 가능한 상태라고 생각했기 때문입니다.

JEP 323의 목표를 보면
명시적으로 선언되던 구문을 var로 선언할 수 있게 되었는데
원래 람다식에서 암시적으로 선언되었던 형태에 var를 적용함으로써 표현식을 통일할 수 있도록 하였다고 합니다.

람다표현식에 var를 선언하는 경우 람다표현식에도 어노테이션을 사용하는 경우 조금 더 코드가 간단해집니다.

list.stream()
    .map((@NotNull var s) -> s.toLowerCase())
    .collect(Collectors.toList());

reference

https://headf1rst.github.io/TIL/jvm-static
https://meetup.nhncloud.com/posts/171

profile
꾸준하게 Ready, Set, Go!

0개의 댓글