- JDK (Java Development Kit)
- 자바 개발 키트는 개발자들이 JVM과 JRE에 의해 실행되고 구동될 수 있는 자바 프로그램을 생성할 수 있게 해준다.
- 자바 애플리케이션을 구축하기 위한 핵심 플랫폼 구성요소다. 이 중심에는 자바 컴파일러(Compiler)가 있다.
- JVM (Java Virtual Machine)
- 자바 가상 머신은 프로그램을 실행하는 자바 플랫폼 구성요소다.
- JRE (Java Runtime Environment)
- 자바 런타임 환경은 JVM을 생성하는 디스크 상의 부분이다.
우선 자바의 버전별 특징을 서술하기에 앞서 LTS 라는 개념에 대해 짚고 넘어가야 할것 같다
LTS 란 Long Term Support 의 약자이며 출시 후 8년 이라는 긴 기간동안 보안 업데이트와 버그 수정을 지원할 것임을 선언한 버전이다. (보다 안정적으로 java를 사용하고 싶다면 LTS 버전을 사용하는 것이 좋다)
자바 버전 히스토리에 대한 전체 내용은 오라클의 자바 로드맵이나 위키피디아의 자바 버전 히스토리를 참고하면 된다.
이 글을 작성하는 현재 제공되는 jdk버전중 가장 최신 버전은 Java SE 19이며, LTS 버전중 가장 최신 버전은 Java SE 17 이다.
수많은 새로운 버전들이 출시되고 있지만 java LTS 버전 중 현재 실무에서 가장 많이 사용하는 버전은 java 8이다.
java 8에서 등장한 lambda와 Stream API 등 편리한 기능 뿐만 아니라 오라클이 자바를 인수하고 나서 출시한 첫번째 LTS 버전 이라는 점, 또 32비트를 지원하는 마지막 공식 java 버전이라는 점 등이 이 이유가 될것 같다.
Java8과 경쟁상대로 가장 많이 언급되었던 Java version은 11이였다.
그 이유는 우선 LTS였기 때문이였는데. Oracle 사에서 Java8을 사용한 레거시 프로젝트들(현재도 서비스하고 있기 때문에 새로운 기술에 계속해서 대응해줘야함!)이 너무 많음을 고려하여 Java8이 Java11보다 더 긴 지원기간을 갖게되었다.
이런 이유 때문에 지금 Java11을 메인으로 프로젝트를 개발하게 된다면 26년 9월 이후에는 기술적 지원이 종료되기 때문에 Java8을 대체하는 Java version으로 11보단 17을 선호하게 되었다.
Java 8의 특징
- Lambda,
- stream
- interface default method
- Optional
- new Date and Time API(LocalDateTime, …)
Java는 객체지향 언어이기 때문에 기본적으로 함수형 프로그래밍이 불가능하다.
하지만 JDK8부터 Stream API와 람다식, 함수형 인터페이스 등을 지원하면서 Java를 이용해 함수형으로 프로그래밍할 수 있는 API 들을 제공해주고 있다.
배열 정렬의 병렬 처리
병렬 배열 정렬(Parallel Array Sorting)
배열을 정리할 때는 Arrays.sort() 메소드를 사용했는데, 이 메소드는 정렬이 순차적으로 실행된다는 단점이 있었다. Java 8부터는 Arrays.parallelSort() 를 통해 병렬적으로 배열을 정렬할 수 있다.
Optional
Java 8의 Optional 클래스는 null-safe한 코드를 작성할 수 있도록 도와주는 클래스입니다.
Optional 클래스는 null을 직접 다루는 것 대신, 값을 가질 수도 있고 없을 수도 있는 컨테이너 객체입니다.
만약 값이 존재한다면 isPresent() 메소드는 true를 반환하고, get() 메소드는 값을 반환합니다. 이를 통해 NullPointerException을 방지할 수 있습니다. [참조]
Lambda
Java 8 이전 익명 클래스의 사용을 람다를 이용하여 더욱 간결하고 직관적으로 구현 가능 불필요한 코드를 줄이고, 가독성을 높임
(매개변수목록) -> {함수몸체}
Runnable runnable = new Runnable(){
@Override
public void run(){
System.out.println(*"Hello world !"*);
}
};
Runnable runnable = () -> System.out.println(*"Hello world two!"*);
람다식(Lambda Expression) 의 특징
- 람다식 내에서 사용되는 지역변수는 final이 붙지 않아도 상수로 간주된다.
- 람다식으로 선언된 변수명은 다른 변수명과 중복될 수 없다.
람다식(Lambda Expression) 의 장점
- 코드를 간결하게 만들 수 있음
- 식에 개발자의 의도가 명확히 드러나 가독성이 높아짐
- 함수를 만드는 과정없이 한번에 처리할 수 있어 생산성이 높아짐
- 병렬프로그래밍이 용이
람다식(Lambda Expression) 의 단점
- 람다를 사용하면서 만든 무명함수는 재사용이 불가능
- 디버깅이 어려움
- 람다를 남발하면 비슷한 함수가 중복 생성되어 코드가 지저분해질 수 있음
- 재귀로 만들경우에 부적합함
Stream
컬렉션을 위한 대용량 데이터 처리 ( 스트림 )
자바에서 배열이나 컬렉션을 사용할 때 여기에 저장된 데이터에 접근하기 위해서는 반복문이나 반복자(Iterator)를 사용하여 데이터에 접근해야했는데, 그렇게 되면 코드가 너무 길어지고 가독성도 떨어지고, 코드의 재사용이 거의 불가능한 상태의 코드가 탄생한다.
이러한 문제점을 극복하기 위해 나온게 스트림 API다. 그러다보니 보다 간결해지고, 데이터 소스에 대한 공통된 접근 방식을 제공하기 때문에 자주 사용된다.
자바 8은 스트림 API를 통해 컬렉션을 처리하면서 발생하는 모호함과 반복적인 코드 문제와 멀티코어 활용 어려움이라는 두 가지 문제를 모두 해결 하였다.
List<String> list = Arrays.asList(*"franz"*, *"ferdinand"*, *"fiel"*, *"vom"*, *"pferd"*);
list.stream() // 생성하기
.filter(name -> name.startsWith(*"f"*)) // 가공하기
.map(String::toUpperCase) // 가공하기
.sorted()// 가공하기
.forEach(System.out::println);
Stream API의 특징
- 원본의 데이터를 변경하지 않는다.
- 일회용이다.
- 내부 반복으로 작업을 처리한다. (코드 간결의 이유)
- 스트림 API는 다양한 데이터 소스 (컬렉션, 배열, 가변 매개변수, 지정된 범위의 연속된 정수, 특정 타입의 난수들, 람다 표현식, 파일, 빈 스트림)에서 생성할 수 있다.
Stream API 사용시 주의사항
- Stream API를 정확히 알고 사용하지 못하면 처리 속도의 지연을 야기할 수 있다
- Stream API는 수직적인 구조로 진행이 되기 때문에 실행 순서를 고려하는 것이 상당히 중요하다. 잘못된 실행 속도는 연산의 횟수를 불필요하게 증가시키기 때문이다.
- 대규모 트래픽을 감당할 때는 오히려 traditional for-loop 스타일이 좋다
for문 보다 Stream API가 느린 이유
1. for문은 단순 인덱스 기반이다.
for문은 단순 인덱스 기반으로 도는 반복문으로 메모리 접근이기 때문에 Stream에 비해 빠르고 오버헤드도 없다.
stream의 경우는 JVM이 이것저것 처리해줘야하는 것들이 많아 실행 시 느릴 수 밖에 없다.
2. for문은 컴파일러가 최적화를 시킨다.
stream은 java 8부터 지원한 것이고 for문은 그보다 훨씬 오래전부터 계속 사용해왔다.
그만큼 사용되는 컴파일러는 오래 사용된 for문에 대한 처리가 되어 있어 for문의 경우 미리 최적화를 시킬 수 있지만,
stream의 경우 for문과 같은 정교한 최적화를 수행할 수 없다.
for문 보다 Stream API가 느리지만 사용하는 이유
참고 : Java Stream API는 왜 for-loop보다 느릴까?
1. 가독성이 좋아진다.
이것도 개발자 역량에 따라 다르지만 stream을 사용하면 확실히 stream api에 포함되어 있는 여러 함수 들을 이용해 코드가 간결해진다.
물론 이것도 stream에 익숙하지 않은 사람은 그 의미를 일일이 해독하느라 코드를 읽는 시간이 더 걸릴 수 있지만,
stream을 한 두 번 보다보면 가독성이 좋다는 것이 느껴진다.
물론 이것도 무조건적인 장점은 아니다. 어떤 기능을 개발하느냐에 따라 for문이 가독성이 좋을 수 있고(특히 for문 안에서 중첩 반복 또는 조건문을 사용하는 경우) stream이 오히려 가독성이 안 좋을 수 있다.
2. 코드로 작성해야하는 로직을 stream에서 제공해주는 함수로 간단하게 해결 가능하다.
이것은 특히 filter, reduce 등과 같이 단순 forEach가 아닌 함수들을 사용할 경우 유용하다.
데이터가 많지 않을 경우 stream을 사용해도 (for문보다 성능이 떨어지지만) 속도가 느리지 않다.
그러니 코드로 일일이 기능을 구현해야하는 것들을 stream api에서 제공한다면 당연히 stream을 사용하든 것이 좋다.
단, 단순 forEach일 경우 stream이 아닌 for문을 사용하자! (또는 컬렉션의 forEach 함수를!)
for문 보다 Stream API가 느리지않은 경우
Collection이 되는 스트림 소스의 크기가 충분히 크거나, 컴퓨팅 연산이 CPU-intensive할 정도로 비용이 매우 비싼경우 Stream이 성능이 더좋다고 한다.
Java 9의 특징
- 모듈시스템 등장(jigsaw)
Java 10의 특징
- var 키워드
- 병렬 처리 가비지 컬렉션 도입으로 인한 성능 향상
- JVM 힙 영역을 시스템 메모리가 아닌 다른 종류의 메모리에도 할당 가능
Java 11의 특징
- LTS
- Oracle JDK와 OpenJDK 통합
- Oracle JDK가 구독형 유료 모델로 전환
- Lambda에 var 사용 가능
- Java 10부터 Java 소스 파일 을 먼저 컴파일 하지 않고도 실행할 수 있다. 스크립팅을 향한 한 걸음
- 이전 java는 소스를 컴파일하여 class로 뽑은 후 class를 실행했는데 java를 통해 바로 실행할 수 있게 되었다
람다 매개변수에 대한 지역 변수 유형 추론(var)
람다 표현식에 var 사용 가능
(var firstName, var lastName) -> firstName + lastName
String 클래스에 새로운 메소드 추가
- strip(): 문자열 앞, 뒤의 공백 제거.
- stripLeading(): 문자열 앞의 공백 제거.
- stripTrailing(): 문자열 뒤의 공백 제거.
- isBlank(): 문자열이 비어있거나, 공백만 포함되어 있을 경우 true를 반환한다.
- String.trim().isEmpty() 와 결과가 동일함.
- repeat(n): n개만큼 문자열을 반복하여 붙여서 반환함.
컬렉션 인터페이스에 새로운 메소드 추가
컬렉션의 toArray() 메소드를 오버 로딩하는 메소드가 추가되었고, 원하는 타입의 배열을 선택하여 반환할 수 있게 되었다.
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를 반환한다.
Java 12의 특징
- 유니코드 11 지원
- 새로운 스위치 표현식의 preview 뿐
Java 13의 특징
스위치 표현식(preview)
이제 스위치 표현식이 값을 반환 가능하며 fall-through/break 문제 없이 표현식에 람다 스타일 구문을 사용 가능
switch(status) {
case SUBSCRIBER:
*// code block*break;
case FREE_TRIAL:
*// code block*break;
default:
*// code block*}
boolean result = switch (status) {
case SUBSCRIBER -> true;
case FREE_TRIAL -> false;
default -> throw new IllegalArgumentException(*"something is murky!"*);
};
Java 14의 특징
- 스위치 표현시 표준화
- instanceof 패턴 매칭 (preview)
- record (data object) 선언 기능 추가 (preview)
스위치 표현(Standard)
버전 12 및 13에서 preview 였던 스위치 표현식 이 이제 표준화 되었다.
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
default -> {
String s = day.toString();
int result = s.length();
yield result;
}
};
유용한 NullPointerExceptions
마지막으로 NullPointerExceptions는 정확히 어떤 변수가 null 인지 설명한다 .
author.age = 35;
---
Exception in thread *"main"* java.lang.NullPointerException:
Cannot assign field *"age"* because *"author"* is null
recode 선언
// 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;
}
}
Java 14 이전에는 다음과 같이 선언 시 equals/hashcode, toString 잠재적으로 포함되었다.
// Java 14 이후
record Point(int x, int y) { }
recode를 통해서 간단하게 선언할 수 있게 되었다.
instanceof 개선
//Java 14 이전
if (obj instanceof String) {
String s = (String) obj;
}
이전에는 obj를 캐스팅하는 것이 필요했지만
if (obj instanceof String s) {
System.out.println(s.contains(*"hello"*));
}
다음과 같이 개선되었다.
Java 15의 특징
- Text-Blocks / Multiline Strings
- Records & Pattern Matching(2차 preview, 상단 Java 14 참조)
- 스케일링 가능한 낮은 지연의 가비지 컬렉터 추가(ZGC)
- 레코드 (2차 preview, 상단 Java 14 참조)
- Sealed Classes - Preview
- Nashorn JavaScript Engine 제거
Text-Blocks / Multiline Strings
Java 13의 실험 기능으로 도입된 여러 줄 문자열은 이제 프로덕션 준비 완료
String text = *"""
Lorem ipsum dolor sit amet, consectetur adipiscing \
elit, sed do eiusmod tempor incididunt ut labore \
et dolore magna aliqua.\
"""*;
Sealed Classes - Preview
한글로 직역하면 봉인된 클래스로 Java에서 자주 사용하던 상속을 제한할 수 있다.
- 상속 가능한 클래스를 지정할 수 있는 봉인 클래스가 제공된다.
- 상속 가능한 대상은 상위 클래스 또는 인터페이스 패키지 내에 속해 있어야 한다.
public abstract sealed class Shape
permits Circle, Rectangle, Square {...}
즉, 클래스가 public인 동안 하위 클래스로 허용되는 유일한 Shape 클래스들은 Circle, Rectangle 및 Square 이다.
public sealed interface SafetyBelt permits Car, Truck {
String belt();
}
다음과 같이 SfatetyBelt 안전벨트를 선언해놓자.
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;
}
}
다음과 같이 안전벨트는 Car와 Truck에서 사용할 수 있도록 허용했고 실제 상속받아서 belt mehtod를 구현한다.
상속받은 class는 추가 확장을 방지하기 위해 final로 선언한다.
다음과 같이 다른 class에서 해당 class를 상속하지 못하도록 막아버린다.
그럼 여기서 만약 오토바이 Vehicle class에 안전벨트를 구현하려고 하면 어떻게 될까?
당연하게도 허용되지 않은 class는 해당 인터페이스를 상속받을 수 없다.
인터페이스 이외에 abstract class에서도 사용할 수 있다.
public abstract sealed class Vehicle permits Car, Truck {
String belt();
}
Java 16의 특징
- Pattern Matching for instanceof
- Unix-Domain Socket Channels
- Foreign Linker API - Preview
- Records & Pattern Matching
Java 17의 특징
- Java 11 이후 새로운 LTS
- 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 이후로 보안 관리자가 있었는데 현재 사용되지 않으므로 제거될 예정 - Spring Boot 버전3 은 Java 17버전 이상만 지원
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"*;
};
}
이제 객체를 전달하여 기능을 전환하고 특정 유형을 확인할 수 있다.
연관된 글 :
[Java] Java의 동작 원리 - Garbage Collection
참고:
Java8과 비교하여 Java 11에서는 GC가 어떻게 변했을까?
Java17을 왜 고려해야 할까? (Java version(8~17) 별 특징 정리)
for문 보다 Stream API가 느리지만 사용하는 이유
[Java] Stream API에 대한 이해 - (1/5)
Java Stream API는 왜 for-loop보다 느릴까?
[Java] 람다식(Lambda Expression)과 함수형 인터페이스(Functional Interface) - (2/5)
'개발 > Java' 카테고리의 다른 글
[JAVA] 정규표현식 (0) | 2023.02.21 |
---|---|
batch (0) | 2023.02.19 |
[Java] BeanUtils (0) | 2019.04.08 |
Java - Method (메소드) 개념 (0) | 2018.12.27 |
[Java] 생성자(constructor) 개념 (0) | 2018.12.27 |