ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Java 8 ~ 17 주요 변경점 정리하기
    JAVA공부/JAVA 2023. 9. 17. 02:22

    현업에서 Java 17버전을 사용하게 되었는데,

    주요 변경점들에 대해서 너무 모르고 있는 것이 많아 정리하려고 한다.

     

    Java 8 → Java 11

    1. 모듈 시스템

    자바 9부터 모듈 시스템이 도입되어 라이브러리와 애플리케이션을 모듈로 분리하는 기능을 제공한다.

     

    2. 로우-레벨 프로파일러 (JMH)

    자바 11에는 JMH를 지원하는 도구가 포함되어 있어 ,  로우-레벨에서 성능 테스트를 수행하기가 더 쉬워졌다.

     

    ※ JMH: Java언어로 작성된 마이크로 벤치마킹 도구이다. Java코드의 작은 부분을 테스트하고 성능 특성을

    분석하는데 쓰인다. Java 성능 튜닝과 최적화에 매우 유용한 도구로 인정받고 있다.

     

    3. HTTP/2 지원

    자바 9부터 HTTP/2 프로토콜을 지원한다. 이를 통해 네트워크 통신의 성능을 향상시킬 수 있다.

     

    4.HTTP 클라이언트 API 

    자바 11에는 새로운 HTTP 클라이언트 API가 도입되었다. HTTP/1.1, HTTP/2를 지원하며

    HttpURLConnection 보다 효율적이고 강력한 기능을 제공한다.

    비교 코드로 보면다음과 같이 볼 수 있다.

     

    package com.codingdreamtree.performance.call;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URI;
    import java.net.URL;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    import java.util.Objects;
    import java.util.concurrent.CompletableFuture;
    
    @Slf4j
    public class HttpSample {
    
    
        /* Java 11 이전 버전 */
        public void urlConnection() throws IOException {
            String url = "http://localhost:10000/test";
            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
            connection.setRequestMethod("GET");
            
            int responseCode = connection.getResponseCode();
            
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            StringBuilder response = new StringBuilder();
            
            while(Objects.nonNull(line = reader.readLine())) {
                response.append(line);
            }
            
            reader.close();
    
            log.info("response Code: {} Result:{}", responseCode, response);
        }
    
        /* Java 11버전 */
        public void httpClient() throws IOException, InterruptedException {
            String url = "http://localhost:10000/test";
            HttpClient httpClient = HttpClient.newHttpClient();
            HttpRequest httpRequest = HttpRequest.newBuilder()
                    .GET()
                    .uri(URI.create(url))
                    .build();
    
            HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
            /* 비동기 호출시에 아래와 같이 호출한다. */
            //CompletableFuture<HttpResponse<String>> httpResponseCompletableFuture = httpClient.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofString());
    
            log.info("response Code: {} Result:{}", response.statusCode(), response.body());
        }
    }

     

     

    5. var 지원

    자바 10부터 지역 변수 선언에 var 키워드를 사용할 수 있게 되었다. 이를 통해 타입 추론을 활용하여

    코드를 간결하게 작성할 수 있다.

     

    예시 코드는 다음과 같다.

    /* 배열 */
    var numbers = new int[]{1, 2, 3};
    
    /* 함수 결과값 */
    var currentDate = LocalDate.now();
    
    /* 반복문 */
    var names = List.of("kim", "tae", "hyun");
    
    for (var name : names) {
    	System.out.println("name = " + name);
    }
    
    /* 스트림 연산의 결과값 */
    var array = LongStream.range(0L, 10L)
            .filter(num -> num % 3 == 0)
            .toArray();
            
    /* 제너릭 사용 */
    var map = new HashMap<String, Integer>();
    
    /* try문 */
    
    try (var reader  = new BufferedReader(new InputStreamReader(new FileInputStream("data.txt")))) {
        var line = reader.readLine();
        while (Objects.nonNull(line)) {
            System.out.println("line =" + line);
        }
    
    } catch (Exception e) {
    
    }
    
    /* 람다 표현식 사용 */
    Function<Integer, String> intToString = (var num) -> num.toString();
    
    
    //그외 지역변수로 다양하게 활용 할 수 있다.
    /* 메서드 매개변수나, 반환타입으로는 사용 불가능하다. */

    친절한 Intellij IDE

    무슨 타입인지 확인해보려면 해당 변수에 마우스 오버를 하면 IDE가 확인해준다.

     

    6. GC 개선

    자바 9부터 다양한 가비지 컬렉터의 개선사항이 도입되었다. G1 가비지 컬렉터,  ZGC 가비지 컬렉터가 추가로 개선되었다.

     

    7. 모듈화된 JDK

    자바 11부터 JDK가 모듈화되어 각 모듈로 분리되었다. 이를 통해 필요한 모듈만 선택하여 배포할 수 있다.

    개발자 또한 작성한 코드를 패키지 단위로 내보낼 수 있고, 다른 모듈에서 내보낸 패키지를 사용할 수 있도록 지정할 수 있다.

     

    8. Flight Recorder 제공

    자바 11부터는 Java Flight Recorder이 JDK의 일부로 무료로 제공된다.

     

    JFR(Java Flight Recorder)기능을 활성화 하려면 java 실행 옵션에

    -XX:StartFlightRecording=disk=true,dumponexit=true,filename=myjfr.jfr

    Intellij Jfr 옵션 설정
    Intellij 에서 JFR 활성화 후 Spring Boot&nbsp; 실행시
    jfr 파일 위치 프로젝트 위치에 생성되는것을 확인했다.

    JFR에 시작시간과 지속시간등을 설정하지 않으면 애플리케이션 종료시점까지 지속적으로 기록된다.

    만약 종료를 진행하려면 jcmd, jconsole을 이용하면된다.

    jcmd <PID> JFR.stop

    명령어를 

    (애플리케이션을 다시 실행하면 초기화되니 주의하거나 매 실행시 파일이름을 다르게 저장할 수 있도록 유도하자.)

     

    이렇게 생성된 파일은 JMC(Java Mission Control)을 이용해 분석하거나 VisualVM을 통해서 분석 할 수 있다.

    JMC가 조금 더 분석하기에 보기좋은 UI로 구성되어있는 것 같다. (상세함)

     

     

    ※ Java Flight Recorder 

    Oracle JDK, OpenJDK의 통합 프로파일링, 이벤트 수집 도구이다. 애플리케이션 성능 분석및 디버깅에 사용되는 강력한 도구이다.

    JFR은 애플리케이션 실행중 이벤트 기반의 데이터를 수집하며

    거의 무시할 수 있는 오버헤드로 프로파일링 및 이벤트 수집을 수행한다. 실제 서비스 환경에서도 사용하기 적합하다.

    상세한 성능 정보를 수집(CPU, 메모리, 스레드 활동, I/O작업, 가비지 컬렉션등)을 할 수 있다.

     

     

    ※ Java Mssion Control

    성능 분석, 모니터링 및 프로파일링 도구이며 Java애플리케이션의 동작을 실시간으로 모니터링하고 성능 문제를 진단한다.

    JFR데이터를 시각화(그래프, 차트, 테이블)하고 분석하는 도구이다.

     

     

     

     

     

     


     

     

    Java 11 → Java 17

     

    1. Java switch 표현식 개선

    int result = 2;
    String resultString;
    
    /* 기존 표현식 */
    switch (result) {
        case 1: 
            resultString = "1Day";
            break;
        case 2:
            resultString = "2Day";
            break;
        default:
            resultString = "Unknown";
    }
    
    
    /* 변경된 표현식 */
    String test = switch (result) {
        case 1 -> "1Day";
        case 2 -> "2Day";
        default -> "Unknown";
    };

     

     

     

     

    2. String Method 추가

     

    ■ String.repeat(int count)

    지정된 횟수만큼 해당 문자열을 반복하여 새로운 문자열 반환

     

    ■ String.isBlank()

    문자열이 비어있거나 공백문자로만 이루어져있는지 확인하는 메서드

     

    ■ String.indent(int n)

    지정된 횟수만큼 해당 문자열 앞에 스페이스 들여쓰기로 들어가게 하여 새로운 문자열을 반환해주는 메서드

     

    ■ String.transform(Function<String, String> f)

    문자열을 주어진 함수를 사용하여 변환한 결과를 반환

     

    ■ String.strip()

    문자열의 시작, 끝부분의 공백문자를 제거한 새로운 문자열을 반환

     

    Vs String.trim()

    둘다 같은 기능을 하지만, strip() 메서드는 공백문자의 정의가 Character.isWhitespce에 따라

    더 일반적인 유니코드 공백문자(스페이스, 탭 , 개행, 줄바꿈 등)를 제거한다.

    다국어 환경에서 더 일반적으로 사용할 수 있다.

    이에 따라 11버전 이후부터는 String.strip()메서드를 사용하는 것을 더 권장한다.

     

        @Test
        void repeatTest() {
            String repeat = "repeate";
            log.info("string.repeate {}", repeat.repeat(10));
            // 결과 :  string.repeate repeaterepeaterepeaterepeaterepeaterepeaterepeaterepeaterepeaterepeate
        }
    
        @Test
        void isBlankTest() {
    //        String text = null;
    //        log.info("String is Blank: {}", text.isBlank())
    //        NPE 발생!
    
            String text2 = "";
            log.info("text2 is Blank: {}", text2.isBlank());
            // 결과 : text2 is Blank: true
        }
    
        @Test
        void indentTest() {
            String text = "text";
            String indentText = text.indent(5);
            log.info("text.indent() {}", indentText);
            // 결과 : text.indent()      text (공백 5칸이 들어간다.)
        }
    
        @Test
        void transformTest() {
            String text = "text";
            String transformedText = text.transform(string -> string.substring(0, 1));
            log.info("text.transform() {}", transformedText);
            // 결과 : text.transform() t
        }
    
        @Test
        void stripTest() {
            String text = " text ";
            String stripText = text.strip();
            log.info("text.strip() {}", stripText);
            // 결과 : text.strip() text
            String trimText = text.trim();
            log.info("text.trim() {}", trimText);
            // 결과 : text.trim() text
        }

     

     

     

    3. Sockerchnnel 연결 개선

    ■ SockerChannel의 연결 개선을 위한 API 가 도입

     

     

     

     

    4.Text Blocks

    멀티라인 문자열을 리터럴을 사용하는 Text Blocks이 도입되었다.

        @Test
        void textBlockTest() {
            String textBlock1 = """
                    여기에 문자열을 써보도록 하겠습니다.
                    개행 문자는 어떻게 처리가 될까요?
                    """;
    
             log.info("textBlock1 = {}", textBlock1);
             // 결과: textBlock1 = 여기에 문자열을 써보도록 하겠습니다.
             // 개행 문자는 어떻게 처리가 될까요?
        }

     

     

     

    5. Sealed Classes

    클래스와 인터페이스의 접근 제어를 더욱 엄격하게 제어할 수 있도록 해주는 개념

    클래스 상속과 인터페이스 구현을 제한할 수 있도록하여 아무 클래스에서 상속, 인터페이스 구현을  하여

    코드 안전성과 유지보수성을 망치는것을 방지하여 준다.

     

     

     

    sealed public class Animal permits Dog, Cat, Rabbit{
        public void run() {
            System.out.println("Running --------");
        }
    }
    
    
    final public class Cat extends Animal{
    
    }
    
    public final class Dog extends Animal{
    
    }
    
    final public class Rabbit extends Animal{
        
    }

    만약 permits에 Rabbit를 허용해놓고 실제 Rabbit클래스가 존재하지 않으면  cannot find symbol 에러와 함께 컴파일 에러가 난다.

    상속받는 클래스는 fianl로 선언되어야하며 부모 클래스가 자식 클래스가 어떠한 것이 있는지 알아야한다.

     

    만약 허용되지 않은 클래스가 상속받으려 하는 경우

    class is not allowed to extend sealed class: com.codingdreamtree.performance.call.seal.Animal 

    에러와 함께 컴파일 에러가 발생한다.

     

     

     

     

    6. Record

     

    16버전부터 도입된 새로운 클래스 형식이다. Record는 데이터를 보관하고 조회하기 위한 목적으로 사용되며

    불변(Immutable)하고 간결한 코드를 작성하는데 도움을 준다.

    Record는 정의할때 Getter메서드, equals(), hashCode(), toString() 메서드를 자동으로 생성해준다.

     

    Record클래스는 주로 데이터 전송 객체(DTO), 데이터베이스 레코드, 이벤트 객체 등과 같이 데이터를 단순하게 표현해야하는

    경우 사용하는 것이 좋다.

    /* 레코드 선언 */
    public record RecordExample(
            String name, 
            int age, 
            LocalDateTime startDt
    ) {
    }
    
    /* 레코드 사용 */
    
    
    @Test
    void recordTest() {
        RecordExample recordExample = new RecordExample("taehyun", 10, LocalDateTime.of(2023, 1, 1, 23, 0, 0));
    
        log.info("Record example name: {}, age: {}, startDt:{}", recordExample.name(), recordExample.age(), recordExample.startDt());
        // 결과: Record example name: taehyun, age: 10, startDt:2023-01-01T23:00
    
        log.info("Record example toString: {}", recordExample);
        // 결과: Record example toString: RecordExample[name=taehyun, age=10, startDt=2023-01-01T23:00]
    }

    위의 예제 코드를 보면 알겠지만, 레코드 클래스에는 상태 필드를 선언하지 않는다.

     

    만약 상태값을 바꾸려고 메서드를 만들게 되면

    final로 선언되어있어 값을 할당할 수 없다고 컴파일 오류가 난다.

     

     

     

    7. Pattern Matching

     

    데이터 패턴을 쉽게 매칭하고 추출할 수 있는 기능이다. 코드의 가독성을 향상시키고 불필요한 형변환을 줄일 수 있다.

    1. instanceof 연산자를 대체할 수있다.

    예제)

        @Test
        void instanceOfPatternTest() {
            Object animal = getCatObject();
    
            if (animal instanceof String ani) {
                System.out.println("animal is String result =" + ani);
            } else if(animal instanceof Animal ani){
                System.out.println("animal is Animal result = " + ani);
            } else {
                System.out.println("animal is Not String And Not Animal");
            }
    
        }
    
        private Object getCatObject() {
            return new Cat();
        }

     

     

    2. swtich 표현식에서 활용

     

    예제)

        @Test
        void casePatternTest() {
            Object animal = getCatObject();
            switch(animal) {
                case String str -> System.out.println("animal is String result =" + str);
                case Animal ani -> System.out.println("animal is Animal result = " + ani);
                default -> System.out.println("animal is Not String And Not Animal");
    
            }
        }

     

     

    이외에도 몇몇 API 가 Deprecated되엇거나 Remove 되었거나 개선사항이 있었지만

    개발적인 측면에서 알아보아야하는 것들을 알아보았다.

Designed by Tistory.