본문 바로가기

자바

자바 버전 정리

Java에는 다양한 버전이 존재

그 중 가장 많이 쓰이는 버전은 Java 8, Java 11, Java 17

위 3가지 버전이 많이 사용되는 이유는 이 버전들이 LTS버전이기 때문

 

 

LTS

- Long Term Support

- 유지보수를 지원하는 버전

- 자바는 6개월마다 새로운 버전을 출시

- 3년마다 LTS버전 출시

- 현재 Java 8, Java 11, Java 17 버전이 LTS

 

 

자바 버전별 특징

1. Java 8

- Oracle이 Java 인수 후, 출시한 첫 번째 LTS 버전

- 32bit를 지원하는 마지막 공식 Java 버전

- Oracle JDK(Oracle사에서 지원하는 버전으로 유료), Open JDK(오픈소스 기반의 무료) 로 나뉨

- 다양한 기능과 특징 출시, 밑에서 다룰 예정

 

2. Java 11

- Oracle JDK와 Open JDK 통합

- Oracle JDK가 구독형 유료 모델로 전환

- Third Party JDK로의 이전 필요 (Zulu JDK, AdoptOpenJDK)

- 람다 지역 변수 사용법 변경 (var 키워드 사용)

List<String> list = Arrays.asList("a", "b", "c");
List<String> notBlanks = list.stream()
                             .map((var x) -> x.toUpperCase())
                             .collect(Collectors.toList());
System.out.println(notBlanks);

- 자바 표준 Http Client API 등장, 이전에는 아파치에서 만든 라이브러리 사용(자바에서 공식지원X) -> 성능도 개선

- String, File에 메서드 추가

ex) String class : isBlank, lines, strip, stripLeading , etc

ex) File class : readString, writeString

- javac를 통해 컴파일 하지 않고도, 바로 java 파일 실행 가능

// java 11 이전
$ javac Test.java
$ java Test
Hello World!
 
// java 11 이후
$ java HelloWorld.java
Hello World!

 

3. Java 17

- 가장 최신 LTS 버전

- 봉인 클래스 (Sealed Class) 정식 추가

- 패턴 매칭

public String test(Object obj) {

    return switch(obj) {
      case Integer i -> *"An integer"*;
      case String s -> *"A string"*;
      case Cat c -> *"A Cat"*;
      default -> *"I don't know what it is"*;
    };
}

- Incubator (Foreign Function & Memory API) -> Java Native Interface(JNI) 대체

- 애플 M1 및 이후 프로세서 탑재 제품군에 대한 정식 지원

- 난수 생성 API 추가

 

 

Java 8을 많이 사용하는 이유

1. Oracle 지원기간이 길다

- Java 8 : 2030년 12월

- Java 11 : 2026년 9월

- Java 17 : 2029년 9월

- 옛날에 나온 Java 8이 그 이후에 나온 11과 17보다 지원기간이 김

 

2. 다양한 기능과 특징 존재

1) Lambda

- 프로그래밍 언어에서 사용되는 개념으로 익명 함수(Anonymous function)을 지칭

- 2개 이상의 입력이 있는 함수는 최종적으로 1개의 입력만 받는 람다 대수로 단순화 가능

- 람다 표현식을 통해 함수형으로 프로그래밍 가능

 

Lambda 장점

- 코드의 간결성 : 람다를 사용하면 불필요한 반복문의 삭제가 가능하며 복잡한 식을 단순히 표현 가능

- 지연연산 수행 : 람다는 지연연산을 수행 함으로써 불필요한 연산을 최소화 가능

- 병렬처리 가능 : 멀티 쓰레드를 활용하여 병렬처리를 사용 가능

 

Lambda 단점

- 람다식 호출이 까다로움

- 람다 Stream 사용 시, 단순 for문 혹은 while문 사용 시 성능이 떨어짐

- for문은 단순 인덱스 기반으로 도는 반복문으로 메모리 접근이기 때문에 Stream에 비해 오버헤드가 없음

- Stream의 경우,  JVM이 처리해줘야 하는 것들이 있어서 실행 시 느릴 수 밖에 없음

 

Lambda 예시 코드

import java.util.ArrayList;

public class LambdaTest {

	public static void main(String[] args) {		
		ArrayList<Integer> numbers = new ArrayList<Integer>();
		numbers.add(1);
		numbers.add(2);
		numbers.add(3);
		numbers.add(4);		
		numbers.forEach((n) -> {System.out.println(n);});
	}
}

 

 

2) Stream

- 다양한 데이터를 표준화된 방법으로 다루기 위한 라이브러리

- 반복문을 병렬 처리 가능

 

Stream API 구성

- stream() : 스트림 생성

- filter : 중간 연산(스트림 변환), 연속으로 수행 가능

- count : 최종 연산(스트림 사용), 마지막에 단 한번만 사용 가능

 

Stream 특징

- 데이터를 변경하지 않음

- 1회용

- 지연 연산 수행 가능

- 병렬 실행 가능

 

Stream 사용 전

import java.util.ArrayList;
import java.util.List;

public class StreamTest {

	public static void main(String[] args) {
		
		// Before
		List<String> names = new ArrayList<String>();
		names.add("KIM1");
		names.add("CHOI22");
		names.add("BAEK333");		
		
		int count = 0;
		for(String str : names) {
			if(str.length() < 5) count++;
		}			
		
		System.out.println(count + " strings with length less than 5");				
	}
}

Stream 사용 후

import java.util.ArrayList;
import java.util.List;

public class StreamTest {

	public static void main(String[] args) {
    
		// After
		List<String> names2 = new ArrayList<String>();
		names2.add("KIM1");
		names2.add("CHOI22");
		names2.add("BAEK333");
				
		// Using Stream and Lambda Expression
		long cnt = names2.stream().filter(
				str -> str.length() < 5).count();
		
		System.out.println(cnt + " strings with length less than 5");
	}

}
List<String> booksWrittenByChoi = 
            books.stream()
                .filter(book -> book.getAuthor().equals("choi"))
                .sorted(Comparator.comparing(Book::getName))
                .map(Book::getIsbn)
                .collect(Collectors.toList());

 

 

3) 함수형 인터페이스

- 1개의 추상 메소드를 갖는 인터페이스

- Java 8부터 기본 구현체를 포함한 디폴트 메서드를 포함할 수 있음

- 여러 개의 디폴트 메소드가 있더라도 추상 메서드가 오직 하나면 함수형 인터페이스

- @FunctionalInterface 어노테이션 사용, 해당 인터페이스가 함수형 인터페이스 조건에 맞는지 검사

- @FuncitonalInterface 어노테이션이 없어도 함수형 인터페이스로 동작하고 사용하는데 문제는 없지만, 인터페이스 검증과 유지보수를 위해 붙이는 것이 좋음

- 자바의 람다 표현식은 함수형 인터페이스로만 사용 가능

 

기본적으로 자바에서 제공해주는 함수형 인터페이스 (아래 함수형 인터페이스로 웬만한 람다식 만들 수 있기 때문에 함수형 인터페이스 직접 만드는 경우 별로 없다고 함)

 

 

참고 링크 : https://bcp0109.tistory.com/313

 

4) default Method

- Interface에 default 접근자로 메소드 선언이 가능해짐

- 또한 이를 구현하는 클래스는 default 메소드를 오버라이딩 가능 

 

- Before

package testCode;

public interface TestInterface {
	int test1();
	int test2();
}

- After

package testCode;

public interface TestInterface {
	int test1();
	int test2();
	
	default int test3() {
		return 0;
	}
	
	static int test4() {
		return 1;
	}
}

- 참고 링크 : http://kbs0327.github.io/blog/technology/java8-default-interface/

 

4) Optional

NPE(NullPointException)

- 개발할 때 가장 많이 발생하는 예외 중 하나

- null 여부를 검사해야 하는데, null 검사를 해야하는 변수가 많은 경우, 코드가 복잡해짐

- null 대신 초기값을 사용하길 권장

package testCode;

import java.util.ArrayList;
import java.util.List;

public class OptionalTest {
	
	public static void main(String[] args) {		
		List<String> names = new ArrayList<>();
		
		// NPE를 방지하기 위해 null 검사
		if(names != null) {
			for(String name: names) {
				System.out.println(name);
			}
		}
	}
}

Optional

- Optional<T>는 NPE 방지에 도움을 줌

- Optional<T>는 null이 올 수 있는 값을 감싸는 Wrapper 클래스

- null을 참조하더라도 NPE가 발생하지 않도록 도와줌

- 참고 링크 : https://mangkyu.tistory.com/70

 

5) JVM의 변화

Java 7 의 JVM

기존의 Permanent 영역에 다음과 같은 정보들 저장

- Class의 메타데이터(바이트코드 포함)

- Method의 메타데이터

- static 객체, static 상수

- 상수화된 String Object

- Class와 관련된 배열 객체 메타데이터

- JVM 내부적인 객체들과 JIT의 최적화 정보

 

OOM 문제 발생

- 이런 많은 것들이 PermG(Permenent Generation)안에 있다보니, String Constant Pool, Class 메타 데이터가 쌓여 OOM 발생

- PermG는 시작할 때부터 크게 잡지 않는 이상 리사이징이 되지 않아 문제 발생

 

Java 8 의 JVM

- Java 8에서부턴 PermG 영역을 삭제하고 Metaspace 영역을 추가해 Native 메모리의 영역으로 이동

 

구체적 변경점

- Class의 메타데이터(바이트코드 포함) -> MetaSpace로 이동

- Method의 메타데이터 -> MetaSpace로 이동

- static 객체, static 변수(class variable) -> Heap으로 이동

- 상수화된 String Object -> Heap으로 이동

- Class와 관련된 배열 객체 메타데이터 -> Metaspace로 이동

- JVM 내부적인 객체들과 JIT의 최적화 정보 -> Metaspace로 이동

- 위와 같이 변경하면, 기존 PermG에 있던 Static Object가 Heap영역으로 옮겨져서 GC의 대상이 될 수 있음

- Native(Metaspace)로 많은 부분을 옮기면서, Native 영역은 JVM에 의해서 크기가 강제되지 않고, 프로세스가 이용할 수 있는 메모리를 최대로 활용 가능

 

"Metaspace 영역은 Heap이 아닌 Native 메모리 영역안에 있다. Heap 영역은 JVM이 관리하고 Native 영역은 OS레벨에서 관리해 자동으로 크기를 조절하고, Metaspace가 Native 메모리를 사용함으로써 개발자는 메모리에서의 영역확보의 상한을 크게 인식할 필요가 없게 되었다. 이것이 Java 8에서 Metaspace가 도입된 이유"

 

6)  Base64 인코딩과 디코딩을 위한 표준 API

-  java.util.Base64를 이용해 인코딩 디코딩 가능

import java.io.UnsupportedEncodingException;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
 
public class Test {
  public static void main(String[] args) throws UnsupportedEncodingException {
    Encoder encoder = Base64.getEncoder();
    String encoded = encoder.encodeToString("Hello Word".getBytes("UTF-8"));
    System.out.println(encoded);
 
    Decoder decoder = Base64.getDecoder();
    byte[] decoded = decoder.decode(encoded);
    System.out.println(new String(decoded, "UTF-8"));
  }
}


7) 새로운 날짜, 시간 API (Date & Time API)

- java8 이전에도 날짜와 시간 관련된 많은 클래스 제공 (Date(java 1.0), Calendar(java 1.1),, etc)

- 하지만 불변객체가 아닌 점, 상수 필드의 남용, 헷갈리는 월 지정, Java.util.Date의 하위 클래스 문제(equals 대칭성) 등의 문제가 존재

- java.time 패키지 등장

- LocalDate, LocalTime, LocalDateTime 등의 클래스 등장

- LocalDate + LocalTime => LocalDateTime

- LocalDateTime + timezone => ZonedDateTime

- java 8 이상을 사용한다면 time 패키지 사용 권장

- 한국에서만 서비스한다면 LocalDateTime으로 처리 권장

- 글로벌 서비스까지 고려한다면 timezone이 추가된 ZonedDateTime 고려 권장

 

 

"시대의 흐름에 맞게 병렬 프로세싱을 활용하고자 했고, 그로 인해 기존 버전에서는 구현하기 힘들었던 부분을 함수형 프로그래밍과 비동기 논블로킹 방식을 도입하여 해결"

 

3. Java 11은 기능 추가된 것들이 많지만

- Java 8이 외부 개발 툴과의 연동성에서 가장 안정적

- Java 11부터 요금 정책이 바뀌어서 Oracle JDK 말고 OpenJDK를 사용해야 함

- Java 11과 크게 차이가 나지 않음

- 따라서 Java 8 릴리즈 지원 안하면 그때 업그레이드 해도 됨

'자바' 카테고리의 다른 글

자바 면접 질문  (0) 2023.06.19
배열  (0) 2022.07.27
JAVA 기본  (0) 2022.07.26