들어가기 전에
금융권 계정계 업무를 하다 보면, 시스템이 굉장히 오래되었다고 생각이 들 때가 있습니다. 필자가 실제로 실무에서 사용하고 있는 시스템의 스펙도 Spring 4.x.x 에 JAVA 7 버전입니다. 다양한 버전들이 주기적으로 출시되면서 버전별로 차이점이 궁금하여 이렇게 글을 작성해 봅니다. 앞으로 JAVA 7, 8을 시작으로 11 이상 등 두 버전별 비교글을 계속 작성하려 합니다. 대부분의 코드 예시는 Chatgpt로 도움을 받았습니다.
Spring 공식 문서에서는 새로운 버전이 나왔을 때, JDK 호환에 대한 내용을 릴리즈 노트에 적어 넣고 있습니다. 아래 내용 참고하시면 좋을 것 같습니다.
Spring Framework Reference Documentation
Spring Boot 2.0 에서 JAVA 8
스프링 부트를 이용한다면 비교적 빠른 속도로 개발을 할 수 있습니다. 스프링을 사용할 때는 JDK 버전의 호환성이 점차 중요해졌고, 스프링 부트 2.0 (스프링 프레임워크 5.0 적용)부터 JDK 8 이상 사용이 강제되었습니다. 이때 개발자들이 조금 더 'JAVA 8에 관심을 가지게 되지 않았을까?' 싶습니다.
JAVA 7 버전에 비해, JAVA 8 버전 사용시 이점은?
1. 람다 표현식 (Lambda Expressions)
JAVA 8에서는 함수형 프로그래밍을 지원하기 위해 람다 표현식이 도입되었습니다. 이를 통해 익명 함수를 간결하게 표현할 수 있습니다.
정렬을 위해 Comparator 인터페이스를 사용한 코드
import java.util.Arrays;
import java.util.Comparator;
public class LambdaExample {
public static void main(String[] args) {
// 문자열 배열 생성
String[] names = {"A", "B", "C", "D"};
// Arrays.sort를 사용하여 문자열 배열을 길이로 정렬
Arrays.sort(names, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return Integer.compare(s1.length(), s2.length());
}
});
// 정렬된 배열 출력
for (String name : names) {
System.out.println(name);
}
}
}
람다 표현식을 활용한 코드
import java.util.Arrays;
public class LambdaExample {
public static void main(String[] args) {
// 문자열 배열 생성
String[] names = {"A", "B", "C", "D"};
// 람다 표현식을 사용하여 문자열 배열을 길이로 정렬
Arrays.sort(names, (s1, s2) -> Integer.compare(s1.length(), s2.length()));
// 정렬된 배열 출력
for (String name : names) {
System.out.println(name);
}
}
}
처음봤을 때, 자바스크립트 화살표 함수가 생각이 났습니다. 람다 표현식을 사용하면 코드가 훨씬 간결해지고 가독성이 향상됩니다.
2. Stream API (스트림 API) - 굉장히 효율적으로 사용할 수 있었던 기능 중 하나입니다.
Java 8에서는 스트림 API가 도입되어 데이터를 처리하는 더 간단하고 효율적인 방법을 제공합니다. 스트림을 사용하면 데이터를 컬렉션에서 함수형 스타일로 처리할 수 있으며, 병렬 처리도 지원합니다
Stream API 예제 코드
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamExample {
public static void main(String[] args) {
// 정수 리스트 생성
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
numbers.add(6);
// Stream API를 사용하여 짝수만 필터링하고 제곱한 결과를 새 리스트로 수집
List<Integer> evenSquares = numbers.stream()
.filter(n -> n % 2 == 0) // 짝수만 필터링
.map(n -> n * n) // 제곱
.collect(Collectors.toList()); // 새 리스트로 수집
// 결과 출력
System.out.println(evenSquares); // 출력: [4, 16, 36]
}
}
Stream API를 사용하면 데이터 처리 코드를 간결하게 작성하고 가독성을 향상할 수 있습니다.
3. 인터페이스의 디폴트 메서드와 정적메서드 추가
JAVA 8에서는 인터페이스에 디폴트 메서드와 정적 메서드를 추가할 수 있게 되었습니다. 이를 통해 기존 인터페이스를 확장하고 새로운 메서드를 추가할 때 구현 클래스를 변경하지 않아도 됩니다.
디폴트 메서드 (Default Method)
디폴트 메서드는 인터페이스 내에 구현이 제공되며, 해당 인터페이스를 구현하는 클래스에서 필요한 경우 이 메서드를 재정의할 수 있습니다.
interface MyInterface {
void regularMethod(); // 일반 추상 메서드
default void defaultMethod() {
System.out.println("디폴트 메서드 호출");
}
}
class MyClass implements MyInterface {
@Override
public void regularMethod() {
System.out.println("일반 메서드 호출");
}
}
public class Main {
public static void main(String[] args) {
MyInterface myObject = new MyClass();
myObject.regularMethod(); // 출력: 일반 메서드 호출
myObject.defaultMethod(); // 출력: 디폴트 메서드 호출
}
}
정적 메서드(Static Method)
정적 메서드는 인터페이스 내에서 정적 메서드로 정의됩니다. 인터페이스 이름을 통해 직접 호출할 수 있습니다.
interface MyInterface {
void regularMethod();
static void staticMethod() {
System.out.println("정적 메서드 호출");
}
}
public class Main {
public static void main(String[] args) {
MyInterface.staticMethod(); // 출력: 정적 메서드 호출
}
}
4. 날짜 및 시간 API
Java 8에서는 새로운 날짜 및 시간 API가 도입되어 이전의 Date 및 Calendar 클래스보다 편리하고 유연한 날짜 및 시간 처리를 지원합니다. Java 8에서 도입된 날짜 및 시간 API인 java.time 패키지의 몇 가지 예시를 작성해 보도록 하겠습니다.
현재 날짜와 시간 가져오기
import java.time.LocalDateTime;
public class DateTimeExample {
public static void main(String[] args) {
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("현재 날짜와 시간: " + currentDateTime);
}
}
날짜 및 시간 파싱
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class DateTimeExample {
public static void main(String[] args) {
String dateStr = "2023-09-08";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate parsedDate = LocalDate.parse(dateStr, formatter);
System.out.println("파싱된 날짜: " + parsedDate);
}
}
날짜 간의 차이 계산
import java.time.LocalDate;
import java.time.Period;
public class DateTimeExample {
public static void main(String[] args) {
LocalDate date1 = LocalDate.of(2023, 9, 1);
LocalDate date2 = LocalDate.of(2023, 9, 8);
Period period = Period.between(date1, date2);
System.out.println("날짜 간의 차이: " + period.getDays() + " 일");
}
}
날짜 및 시간 포맷팅
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class DateTimeExample {
public static void main(String[] args) {
LocalDateTime dateTime = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formattedDateTime = dateTime.format(formatter);
System.out.println("포맷팅된 날짜 및 시간: " + formattedDateTime);
}
}
5. 메서드 레퍼런스 (Method References)
메서드 레퍼런스를 사용하면 메서드를 간결하게 호출할 수 있으며, 코드 가독성을 향상 시킵니다. 메서드를 가리키는 참조를 만들어서 람다 표현식을 더 간결하게 표현할 수 있게 해 줍니다.
메서드 레퍼런스 적용 코드
import java.util.Arrays;
import java.util.List;
public class MethodReferenceExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
// 람다 표현식을 사용한 문자열 길이로 정렬
names.sort((s1, s2) -> s1.length() - s2.length());
// 메서드 레퍼런스를 사용한 문자열 길이로 다시 정렬
names.sort(String::compareTo);
// 정렬된 리스트 출력
names.forEach(System.out::println);
}
}
코드에서 names.sort(String::compareTo) 부분은 메서드 레퍼런스를 사용하여 문자열을 비교하고 정렬하며, String::compareTo는 compareTo 메서드를 가리키는 참조입니다.
6. 스크립트 엔진 (Nashorn JavaScript Engine)
Nashorn JavaScript 엔진이 포함되어 JavaScript 코드를 실행할 수 있습니다.
스크립트 엔진 적용 코드
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class NashornExample {
public static void main(String[] args) {
// Nashorn JavaScript 엔진 초기화
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
try {
// JavaScript 코드 실행
engine.eval("var message = 'Hello, Nashorn!';");
engine.eval("print(message);");
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
7. PermGen 영역의 제거
Permanent Generation (PermGen) 영역이 제거되고 Metaspace로 대체되었습니다. 이로써 메모리 관리가 개선되었습니다.
PermGen 영역의 문제점
- 고정된 크기 : PermGen 영역은 고정된 크기를 가지고 있었기 때문에 클래스 메타데이터 및 클래스로더의 활동이 늘어나면 OutOfMemoryError 오류가 발생할 수 있었습니다.
- 가비지 컬렉션 : PermGen 영역은 일반적인 가비지 컬렉션과는 별개로 수행되어야 했습니다. 이로 인해 PermGen 메모리 누수가 발생할 수 있었습니다.
Metaspace 영역의 도입
- 크기 동적 조정 : Metaspace는 힙 영역에서 동적으로 할당되므로 PermGen 영역보다 메모리 관리가 유연해졌습니다.
- 가비지 컬렉션: Metaspace는 일반적인 가비지 컬렉션과 함께 수행되기 때문에 메모리 누수 문제가 감소했습니다.
- PermGen 관련 에러 방지: PermGen 관련 OutOfMemoryError 오류가 사라졌습니다.
8. 스트링 관련 변경
문자열 처리를 개선하기 위해 String 클래스에 새로운 메서드가 추가되었습니다.
몇 가지 주요한 메서드
- join(CharSequence delimiter, CharSequence... elements)
문자열 배열 또는 요소들을 지정된 구분자(delimiter)로 연결하여 새로운 문자열을 생성합니다. - chars()와 codePoints()
문자열을 문자 또는 코드 포인트로 분해할 수 있는 스트림 메서드를 제공합니다.
이를 통해 문자열을 개별 문자 또는 코드 포인트로 처리할 수 있습니다. - isEmpty()
문자열이 비어 있는지 여부를 확인하는 메서드로, 빈 문자열인 경우 true를 반환합니다. - chars()와 codePoints()
문자열을 문자 또는 코드 포인트로 분해하여 처리할 수 있는 스트림을 반환합니다.
이를 통해 문자열을 반복하거나 조작할 수 있습니다. - replaceAll(String regex, String replacement)
정규식 패턴에 맞는 부분을 다른 문자열로 대체하는 메서드입니다. - getBytes(Charset charset)
지정된 문자 집합(Charset)을 사용하여 문자열을 바이트 배열로 변환합니다. - chars() 및 codePoints() 메서드
스트림 API와 함께 사용하여 문자열을 더 효과적으로 처리하는 데 도움이 됩니다.
'개발 > JAVA' 카테고리의 다른 글
[JAVA] JAVA8 과 JAVA11 의 차이점을 알아보자. (0) | 2023.09.23 |
---|---|
[JAVA] 함수형 프로그래밍 (0) | 2023.09.22 |
[JAVA] 접근 제어자 (Access Modifier) 에 대하여 (0) | 2023.09.16 |
[JAVA] 예외(Exception) 처리에 대해서 알아보자. (0) | 2023.08.26 |
[JAVA] Synchronized 란? (2) | 2023.08.24 |