자바(Java) 버전별 특징
안녕하세요. 이번에는 자바(Java)의 버전에 따라 추가되거나 수정된 주요 기능들을 살펴보려고 합니다.
우선 자바는 JDK 1.0 ~ 17까지 다양한 버전이 있는데, 이 글에서는 JDK 7 ~ 11의 기능들에 대해서만 알아보려고 합니다.
버전별 주요 기능을 말씀드리기 전에 JDK, JRE, JVM에 대해 간략히 말씀드리겠습니다.
JVM
자바는 C/C++과는 다르게 컴파일된 프로그램을 운영체제가 바로 실행시킬 수 없습니다. 그 이유는 자바 프로그램이 완전한 기계어가 아닌, 중간 단계의 바이트 코드이기 때문입니다.
예를 들면, C언어는 운영체제 별로 컴파일러가 존재합니다. 윈도우, 맥, 리눅스 컴파일러가 각각 존재하는 것이죠. 이 컴파일러는 코드를 운영체제가 인식할 수 있는 기계어로 컴파일하고 운영체제는 이 프로그램을 실행시킵니다. 반면에 자바는 C언어와는 다르게 자바 컴파일러(javac) 하나만 존재합니다. 이 컴파일러는 자신들만 인식할 수 있는 바이트 코드로 변경시킵니다. 운영체제는 이 언어를 인식할 수 없습니다. 그럼 어떻게 자바 프로그램이 돌아가는 것일까요?
바로 JVM이 문제를 해결해줍니다.
JVM은 Java Virtual Machine의 약자로 자바 가상 머신이라고 부릅니다. JVM은 실 운영체제를 대신해서 컴파일된 바이트 코드를 실행하는 가상의 운영체제 역할을 합니다. 정리하자면, 각 운영체제에서 실행할 수 있는 기계어로 번역해주는 것입니다.
JVM은 인기가 많아 Groovy, Scala, Kotlin에서도 많이 사용된다고 합니다.
자세한 내용은 다음에 다루도록 하겠습니다.
JRE
JRE는 Java Runtime Environment, 즉 자바 실행 환경의 약자입니다.
JRE는 JVM, 자바 클래스 라이브러리, 자바 명령 및 기타 인프라를 포함한 컴파일된 자바 프로그램을 실행하는데 필요한 패키지입니다.
JDK
JDK는 Java Development Kit의 약자로 JRE에 있는 모든 것뿐만 아니라 javac(컴파일러)와 javadocs(자바 문서 생성기) 및 jdb(자바 디버거)와 같은 도구들이 포함되어있습니다. 즉, 실행도 필요하고 개발 작업까지 필수적으로 해야 하는 개발자들이 설치하는 Kit라고 생각하시면 됩니다.
정리하자면, JDK는 JRE를 포함하고 있고, JRE는 JVM을 포함하고 있습니다.
아래의 그림을 보시면 전체적인 구조를 이해하시기 편하실 겁니다.
JDK 버전이 올라갈수록 위 그림의 내부 구조들이 성능 향상, 사용자의 편의를 위해 추가되고 수정되는 것입니다.
JDK 버전별 주요 변경 사항
Java 7
1) Type Inference (타입 추론)
제네릭 클래스의 생성자 호출 시 필요한 타입 인자를 컴파일러가 추론할 수 있으면, 꺽쇠 괄호(< >)로 대체할 수 있게 되었습니다.
// 7 버전 이전
List<String> list = new ArrayList<String>();
//7 버전 이후
List<String> list = new ArrayList<>();
2) Switch문 문자열 허용
말 그래로 Swtich문에서 문자열 사용이 가능해졌습니다.
switch (a) {
case "ronaldo":
...
break;
case "messi":
...
break;
default:
...
break;
}
3) try-with-resources
이 전까지만 해도 자원을 생성하고 사용한 뒤 finally 블록으로 해제하는 코드가 항상 중복되었습니다. 하지만 7부터는 try-with-resources라는 특징이 추가되어 try에서 자원 생성하고 사용해도 finally 블록으로 종료할 필요 없이 try 블록이 끝나면 자동 종료를 해주게 되었습니다.
// 7 버전 이전
Resource resource = null;
try {
//리소스 생성 후 사용
} catch(...) {
...
} finally {
//리소스 해제
}
// 7 버전 이후
Resource resource = null;
try {
//리소스 생성 후 사용
} catch(...) {
...
}
//리소스 자동 해제
4) 멀티 캐치
try-catch문에서 멀티 캐치를 사용할 수 있게 되었습니다.
// 7 버전 이전
try {
...
} catch (예외 1) {
...
} catch (예외 2) {
...
}
// 7 버전 이후
try {
...
} catch (예외 1 | 예외 2) {
...
} catch (예외 3){
...
}
반복되는 코드를 줄일 수 있게 되었습니다. 다만, 위의 catch문의 예외가 아래의 catch문의 예외보다 하위 클래스이어야 합니다. 그렇지 않으면 아래 catch문의 예외 처리가 올바르게 작동하지 않습니다.
5) ForkJoinPool
자바에서 분할-정복 방식으로 작업을 실행하려면 관련 코드를 작성해야 했습니다. 그러나 7 버전 이후부터는 분할-정복 방식을 위한 ForkJoinPool 클래스가 제공되었습니다. 이 클래스는 분할된 작업을 병렬로 처리해주기 때문에 CPU의 성능을 최대로 뽑아낼 수 있어 처리 시간을 단축할 수 있습니다.
자세한 내용은 이 곳을 블로그 참고하시면 좋을 것 같습니다.
Java 8
1) Lambdas
익명 내부 클래스를 람다 표현식으로 바꿀 수 있게 되었습니다.
// 8 버전 이전
Runnable runnable = new Runnable(){
@Override
public void run(){
System.out.println("Hello world!");
}
};
// 8 버전 이후
Runnable runnable = () -> System.out.println("Hello world!");
2) Interface 클래스에 구현체 작성 가능
추상 클래스뿐만 아니라 인터페이스 클래스에서도 default와 static 키워드를 사용해 구현 메서드를 작성할 수 있게 되었습니다.
3) Optional 클래스
자바에서는 null을 다루는 것이 까다롭고, NPE 또한 많이 발생합니다. 그래서 대부분의 코드에 null 체크 로직이 포함됩니다. 8 버전 이후부터는 null 체크를 깔끔하게 해줄 수 있는 Optional 클래스를 사용할 수 있게 되었습니다.
4) Collections & Streams
List를 반복문 대신 깔끔하고 구현을 편하게 해주는 Stream API를 사용할 수 있게 되었습니다.
list.stream()
.filter(name -> name.startsWith("f"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
Java 9
1) Collections
컬렉션에 배열을 쉽게 생성할 수 있는 메서드가 추가되었습니다.
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");
2) Stream
자바 9 이전까지는 filter 메서드를 이용해 전체 스트림을 반복하면서 Predicate를 적용시켰습니다.
그러나 자바 9 이후부터는 위와 같이 takeWhile, dropWhile, iterate 메서드를 사용할 수 있게 되었습니다.
// 50보다 작은 요소를 선택
List<Integer> list = numbers.stream()
.takeWhile(i -> i < 50)
.collect(toList());
// 50보다 작은 요소를 삭제
List<Integer> list = numbers.stream()
.dropWhile(i -> i < 50)
.collect(toList());
3) Optional
자바 9에서는 Optional 클래스에 유용한 메서드들이 추가된 것 같습니다.
첫 번째로는 or() 메서드가 추가되었습니다.
or() 메서드는 리턴 타입이 Optional 이기 때문에 계속해서 orElse()와 같은 Optional 클래스의 메서드를 연쇄적으로 호출할 수 있게 합니다.
기존의 orElseGet() 이나 orElse()가 주로 메서드 체인의 끝에서 사용되었다면, or() 메서드로 중간에서 null 처리를 할 수 있습니다.
String coffee = Optional
.ofNullable(order)
.map(Order::getCoffee)
.or(() -> Optional.ofNullable(user.getFavoriteCoffee()))
.orElse("아메리카노");
두 번째로는 ifPresentOrElse() 메서드가 추가되었습니다.
기존의 ifPresent() 메서드는 null이 아닐 경우에 처리할 내용만 정의했다면, ifPresentOrElse() 메서드는 null 일 경우에 처리할 내용까지 정의할 수 있습니다.
Optional.ofNullable(order)
.map(Order::getCoffee)
.ifPresentOrElse(
coffee -> System.out.println("주문 : " + coffee),
() -> System.out.println("주문 : " + user.getFavoriteCoffee())
);
4) Interface 클래스
인터페이스에 private 메서드를 정의할 수 있게 되었습니다.
public interface MyInterface {
private static void myPrivateMethod(){
System.out.println("Yay, I am private!");
}
}
Java 10
1) 지역 변수 타입 추론 (var)
지역 변수 선언을 var를 이용해 선언할 수 있게 되었습니다.
var list = new ArrayList<String>(); // ArrayList<String>
var stream = list.stream(); // Stream<String>
주의할 점은 초기화된 지역 변수, 반복분에서 지역 변수를 선언할 때 사용할 수 있습니다.
Java 11
1) String, Files 클래스
String, Files 클래스에 새로운 메서드가 추가되었습니다.
"Marco".isBlank();
"Mar\nco".lines();
"Marco ".strip();
Path path = Files.writeString(Files.createTempFile("helloworld", ".txt"), "Hi, my name is!");
String s = Files.readString(path);
2) Lambdas Type Reference (var)
람다 표현식에서 타입 추론(var)을 사용할 수 있게 되었습니다.
// 자바 11 이전
Consumer<String> testFoo = s -> System.out.println("s = " + s);
// 자바 11 이후
Consumer<String> testFoo = (@Nonnull var s) -> System.out.println("s = " + s);
var을 사용하시면 변수 앞에 어노테이션을 추가할 수 있다는 장점이 있습니다.
마무리
자바 11을 자바 8처럼 사용했다는 생각이 들었습니다. 앞으로 계속 공부해가면서 변경된 부분에 대해 깊게 알아봐야 할 것 같습니다.
읽어주셔서 감사합니다.
참고
'프로그래밍 언어 > Java' 카테고리의 다른 글
[Java] Garbage Collection (0) | 2021.10.31 |
---|---|
[Java] JVM (0) | 2021.10.25 |
[스터디 할래] 6주차 - 상속 (0) | 2021.05.26 |
[스터디 할래] 5주차 - 클래스 (0) | 2021.05.26 |
java.util.Optional이란? (1) | 2021.05.26 |
댓글