Java 8, 11, 17에서의 변화
LTS란
LTS는 Long Term Support의 줄임말로, 출시 후 8년 동안 보안 업데이트 및 버그 수정을 지원하는 버전이다.
일반 버전과 달리 안정성에 중점을 둔 버전으로, 기능 업데이트는 최소한으로 이루어지거나, 아예 없는 반면 보안 업데이트의 지원이 일반 버전보다 훨씬 길다. 그렇기 때문에 서버 등 최신 기능보다 안정성이 중요한 곳에서 많이 쓰이며, 기업이나 기관 등에서 기능 업데이트로 인해 이용 중인 다른 소프트웨어가 영향을 받는 것을 피하기 위해 이용하기도 한다.
자바 8, 11, 17은 모두 LTS(Long Term Support) 버전이다.
JAVA 8
Lamda Expression 도입
Lambda Expression은 익명 함수를 생성하기 위한 표현식. 익명 클래스의 한 개의 메소드를 식으로 표현한 것이다.
//(ex1)
//기존의 익명 내부 함수
Runnable r1 = new Runnable(){
@Override
public void run(){
System.out.println("Hello world!!");
}
};
r1.run();
//람다를 이용한 구현
Runnable r2 = () -> System.out.println("Hello world!!");
r2.run();
(매개변수 목록) -> {함수 몸체}의 형태로 작성되며, 기존의 불필요한 코드가 줄어들고 작성 된 코드의 가독성이 높아진다는 장점이 있다.
Method Reference 도입
class::methodName 구문을 사용하여 클래스 또는 객체에서 메서드를 참조할 수 있다.
// 람다식
str -> str.toString()
// 메서드 참조
String::toString
// 람다식
str -> str.length()
// 메서드 참조
String::length
// 람다식
(int x, int y) -> x.compareTo(y)
// 메서드 참조
Integer::compareTo
Stream API 도입
자바에서는 여러 개의 데이터를 저장하기 위해서 배열이나 컬렉션을 사용한다. 그리고 이렇게 저장된 데이터에 접근하기 위해서는 반복문이나 반복자(iterator)를 사용하여 매번 코드를 작성해야 했다.
하지만 이렇게 작성된 코드는 길이가 길고 가독성도 떨어지며, 코드의 재사용이 거의 불가능했다. 또한, 정형화된 처리 패턴을 가지지 못했기 때문에 데이터마다 다른 방법으로 접근해야만 했다.
이러한 문제점을 극복하기 위해서 도입된 방법이 스트림 API다. 스트림 API는 데이터를 추상화해서 다루므로, 다양한 형태로 저장된 데이터를 위한 공통된 방법을 제공한다.
스트림 API는 다음과 같이 세 가지 단계에 걸쳐서 동작한다.
- 스트림 생성
- 스트림 중개 연산 (filter)
- 스트림 최종 연산 (map)
// 정수형 배열에서 스트림 생성
Integer[] arr1 = new Integer[] {1, 5, 11, 13, 20, 52};
Stream stream1 = Arrays.stream(arr1);
stream1.map(i -> i * 2);
stream1.filter(i -> i % 2 == 0); // 재사용이 불가능하기 때문에 에러 발생!
// 정수형 배열에서 스트림 생성
Integer[] arr2 = new Integer[] {1, 5, 11, 13, 20, 52};
Stream stream2;
stream2 = Arrays.stream(arr2)
.filter(i -> i % 2 != 0) // {1, 5, 11, 13}
.map(i -> i * 2); // {2, 10, 22, 26}
Interface default Method 도입
Interface 내부에서 default 메서드를 선언할 수 있다.
Optional Class 도입
Optional Class를 이용하여 NullPointException이 발생하는 것을 방지해줄 수 있게 되었다.
java.time 패키지 도입
JDK 1.1부터 제공된 Calendar 클래스는 다음과 같은 문제점을 가지고 있다.
- Calendar 인스턴스는 immutable object가 아니라서 값이 수정될 수 있다.
- 윤초(leap second)와 같은 특별한 상황을 고려하지 않는다.
- Calendar 클래스에서는 월(month)을 나타낼 때 1~12가 아닌 0~11로 표현해야 한다.
따라서 많은 개발자들은 Calendar 클래스뿐만 아니라 더 나은 성능의 Joda-Time이라는 라이브러리를 함께 사용해왔는데, ava SE 8 버전에서는 이러한 Joda-Time 라이브러리를 발전시킨 새로운 날짜/시간 API인 java.time 패키지를 제공한다.
LocalDate today = LocalDate.now();
System.out.println("올해는" + today.getYear() + "년입니다.");
LocalDate otherDay = today.withYear(1982);
System.out.println("올해는" + otherDay.getYear() + "년입니다.");
JAVA 11
String 클래스에 새로운 메소드 추가
- strip(): 문자열 앞, 뒤의 공백 제거.
- stripLeading(): 문자열 앞의 공백 제거.
- stripTrailing(): 문자열 뒤의 공백 제거.
- isBlank(): 문자열이 비어있거나, 공백만 포함되어 있을 경우 true를 반환한다. String.trim().isEmpty() 와 결과가 동일함.
- repeat(n): n개만큼 문자열을 반복하여 붙여서 반환함
java.nio.file.Files 클래스에 새로운 메소드 추가
- Path writeString(Path, String, Charset, OpenOption): 파일에 문자열을 작성하고 Path로 반환한다. 파일 오픈 옵션에 따라 작동 방식을 달리하며, charset을 지정하지 않으면 UTF-8이 사용된다.
- String readString(Path, Charset): 파일 전체 내용을 읽어서 String으로 반환하고, 파일 내용을 모두 읽거나 예외가 발생하면 알아서 close를 한다. charset을 지정하지 않으면 UTF-8이 사용된다.
- boolean isSameFile(Path, Path): 두 Path가 같은 파일을 가리키며, true, 아니면 false를 반환한다.
컬렉션 인터페이스에 새로운 메소드 추가
컬렉션의 toArray() 메소드를 오버 로딩하는 메소드가 추가되었고, 원하는 타입의 배열을 선택하여 반환할 수 있게 되었다.
List sampleList = Arrays.asList("Java", "Kotlin");
String[] sampleArray = sampleList.toArray(String[]::new);
assertThat(sampleArray).containsExactly("Java", "Kotlin");
람다에서 로컬 변수 Var 사용
람다식에서 Var을 사용할 수 있게 되었다.
List<String> sampleList = Arrays.asList("Java", "Kotlin");
String resultString = sampleList.stream()
.map((@Nonnull var x) -> x.toUpperCase())
.collect(Collectors.joining(", "));
assertThat(resultString).isEqualTo("JAVA, KOTLIN");
자바 파일 실행
javac를 통해 컴파일 하지 않고도, 바로 java 파일을 실행할 수 있게 되었다.
$ java HelloWorld.java
Hello Java 11!
차세대 Garbage Collector
Java 11의 Default GC는 G1 GC이다.
새로운 HTTPClient API
새로운 HTTP API는 전반적인 성능을 향상시키고 HTTP / 1.1 및 HTTP / 2를 모두 지원한다.
JAVA 17
텍스트 블록
기존에 JSON 문자열을 직접 생성할 때 이스케이프 처리로 가독성이 떨어지던 문제를 텍스트 블록을 통해 개선할 수 있게 되었다.
텍스트 블록은 3개의 큰 따옴표로 정의되며, 아래와 같이 작성할 수 있다.
private static void oldStyle() {
String text = "{\n" +
" \"name\": \"John Doe\",\n" +
" \"age\": 45,\n" +
" \"address\": \"Doe Street, 23, Java Town\"\n" +
"}";
System.out.println(text);
}
private static void jsonBlock() {
String text = """
{
"name": "John Doe",
"age": 45,
"address": "Doe Street, 23, Java Town"
}
""";
System.out.println(text);
}
switch 표현식
기존의 break 문은 누락, 가독성 문제가 있었는데 이를 아래와 같이 변경했다.
private static void withReturnValue(Fruit fruit) {
String text = switch (fruit) {
case APPLE, PEAR -> "Common fruit";
case ORANGE, AVOCADO -> "Exotic fruit";
default -> "Undefined fruit";
};
System.out.println(text);
}
instanceOf 변수 생성
instanceof 연산자는 사용하면 객체가 특정 클래스에 속하는지 아닌지를 확인할 수 있는 연산자다.
Java 17부터는 instanceof 체크 시에 변수를 생성할 수 있어 새 변수를 생성하고 객체를 캐스팅하는데 추가 줄이 필요하지 않다.
private static void patternMatching() {
Object o = new GrapeClass(Color.BLUE, 2);
if (o instanceof GrapeClass grape) { // 변수 생성
System.out.println("This grape has " + grape.getNbrOfPits() + " pits.");
}
}
Stream.toList()
private static void streamToList() {
Stream<String> stringStream = Stream.of("a", "b", "c");
List<String> stringList = stringStream.toList();
for(String s : stringList) {
System.out.println(s);
}
}
자바 17에서는 toList 이전 동작을 대체하는 메서드가 추가 되었다. 그래서 이전처럼 Stream에서 List로 변환할 때 기존의 긴 Collectors.toList() 를 호출하지 않아도 된다.
출처