JAVA 8은 JAVA 프로그래밍 언어 개발의 주요 기능 릴리스입니다. 초기 버전은 2014년 3월 18일에 출시되었습니다. Java 8 릴리스로 Java는 함수형 프로그래밍 지원, 새로운 JavaScript 엔진, 날짜/시간 조작을 위한 새로운 API, 새로운 스트리밍 API 등을 제공하게 되었습니다.
새로운 기능
람다 표현식 − Java에 함수형 처리 기능을 추가합니다.
메서드 참조 − 함수를 직접 호출하는 대신 이름으로 참조합니다. 함수를 매개변수로 사용합니다.
기본 메서드 − 인터페이스에 기본 메서드 구현을 허용합니다.
새로운 도구 − 'jdeps'와 같이 의존성을 파악하는 새로운 컴파일러 도구와 유틸리티가 추가되었습니다.
스트림 API − 파이프라인 처리를 용이하게 하는 새로운 스트림 API입니다.
날짜/시간 API − 개선된 날짜/시간 API입니다.
Optional − null 값을 올바르게 처리하기 위한 모범 사례 강조.
Nashorn, JavaScript 엔진 − JavaScript 코드를 실행하는 Java 기반 엔진입니다.
이제 'Path' 변수를 변경하여 Java 실행 파일의 경로도 포함하도록 하세요. 예를 들어, 경로가 현재 'C:\WINDOWS\SYSTEM32'로 설정되어 있다면, 'C:\WINDOWS\SYSTEM32;c:\Program Files\java\jdk\bin'으로 변경하세요.
람다 표현식은 주로 함수형 인터페이스(단일 메서드만 있는 인터페이스)의 인라인 구현을 정의하는 데 사용됩니다. 위 예제에서는 다양한 유형의 람다 표현식을 사용하여 MathOperation 인터페이스의 operation 메서드를 정의했습니다. 그런 다음 GreetingService의 sayMessage 구현을 정의했습니다.
람다 표현식은 익명 클래스의 필요성을 없애고 Java에 매우 단순하면서도 강력한 함수형 프로그래밍 기능을 제공합니다.
범위
람다 표현식을 사용하면 모든 final 변수 또는 실질적으로 final인 변수(한 번만 할당된 변수)를 참조할 수 있습니다. 변수에 두 번째로 값이 할당되면 람다 표현식은 컴파일 오류를 발생시킵니다.
범위 예제
원하는 편집기를 사용하여 다음 Java 프로그램을 작성하세요. (예: C:\> JAVA)
Java8Tester.java
Live Demo
public class Java8Tester {
final static String salutation = "Hello! ";
public static void main(String args[]) {
GreetingService greetService1 = message -> System.out.println(salutation + message);
greetService1.sayMessage("Mahesh");
}
interface GreetingService {
void sayMessage(String message);
}
}
결과 확인
javac 컴파일러를 사용하여 클래스를 컴파일하세요.
C:\JAVA>javac Java8Tester.java
다음과 같이 Java8Tester를 실행하세요.
C:\JAVA>java Java8Tester
다음과 같은 출력이 나와야 합니다.
Hello! Mahesh
Java 8 - 메서드 참조
메서드 참조는 메서드를 이름으로 가리키는 데 도움이 됩니다. 메서드 참조는 "::" 기호로 설명됩니다. 메서드 참조는 다음 유형의 메서드를 가리키는 데 사용될 수 있습니다.
정적 메서드
인스턴스 메서드
new 연산자를 사용한 생성자 (TreeSet::new)
메서드 참조 예제
원하는 편집기를 사용하여 다음 Java 프로그램을 작성하세요. (예: C:\> JAVA)
Java8Tester.java
import java.util.List;
import java.util.ArrayList;
public class Java8Tester {
public static void main(String args[]) {
List names = new ArrayList();
names.add("Mahesh");
names.add("Suresh");
names.add("Ramesh");
names.add("Naresh");
names.add("Kalpesh");
names.forEach(System.out::println);
}
}
여기서 System.out::println 메서드를 정적 메서드 참조로 전달했습니다.
결과 확인
javac 컴파일러를 사용하여 클래스를 컴파일하세요.
C:\JAVA>javac Java8Tester.java
다음과 같이 Java8Tester를 실행하세요.
C:\JAVA>java Java8Tester
다음과 같은 출력이 나와야 합니다.
Mahesh
Suresh
Ramesh
Naresh
Kalpesh
Java 8 - 함수형 인터페이스
함수형 인터페이스는 단일 기능을 나타냅니다. 예를 들어, 단일 메서드 'compareTo'를 가진 Comparable 인터페이스는 비교 목적으로 사용됩니다. Java 8은 람다 표현식에서 광범위하게 사용될 많은 함수형 인터페이스를 정의했습니다. 다음은 java.util.function 패키지에 정의된 함수형 인터페이스 목록입니다.
번호
인터페이스 및 설명
1
BiConsumer<T,U>
두 개의 입력 인수를 허용하고 결과를 반환하지 않는 연산을 나타냅니다.
2
BiFunction<T,U,R>
두 개의 인수를 허용하고 결과를 생성하는 함수를 나타냅니다.
3
BinaryOperator<T>
동일한 타입의 두 피연산자에 대한 연산을 나타내며, 피연산자와 동일한 타입의 결과를 생성합니다.
4
BiPredicate<T,U>
두 개의 인수에 대한 프레디케이트(불리언 값 함수)를 나타냅니다.
5
BooleanSupplier
불리언 값 결과를 제공하는 공급자를 나타냅니다.
6
Consumer<T>
단일 입력 인수를 허용하고 결과를 반환하지 않는 연산을 나타냅니다.
7
DoubleBinaryOperator
두 개의 double 값 피연산자에 대한 연산을 나타내며 double 값 결과를 생성합니다.
8
DoubleConsumer
단일 double 값 인수를 허용하고 결과를 반환하지 않는 연산을 나타냅니다.
9
DoubleFunction<R>
double 값 인수를 허용하고 결과를 생성하는 함수를 나타냅니다.
10
DoublePredicate
하나의 double 값 인수에 대한 프레디케이트(불리언 값 함수)를 나타냅니다.
11
DoubleSupplier
double 값 결과를 제공하는 공급자를 나타냅니다.
12
DoubleToIntFunction
double 값 인수를 허용하고 int 값 결과를 생성하는 함수를 나타냅니다.
13
DoubleToLongFunction
double 값 인수를 허용하고 long 값 결과를 생성하는 함수를 나타냅니다.
14
DoubleUnaryOperator
단일 double 값 피연산자에 대한 연산을 나타내며 double 값 결과를 생성합니다.
15
Function<T,R>
하나의 인수를 허용하고 결과를 생성하는 함수를 나타냅니다.
16
IntBinaryOperator
두 개의 int 값 피연산자에 대한 연산을 나타내며 int 값 결과를 생성합니다.
17
IntConsumer
단일 int 값 인수를 허용하고 결과를 반환하지 않는 연산을 나타냅니다.
18
IntFunction<R>
int 값 인수를 허용하고 결과를 생성하는 함수를 나타냅니다.
19
IntPredicate
하나의 int 값 인수에 대한 프레디케이트(불리언 값 함수)를 나타냅니다.
20
IntSupplier
int 값 결과를 제공하는 공급자를 나타냅니다.
21
IntToDoubleFunction
int 값 인수를 허용하고 double 값 결과를 생성하는 함수를 나타냅니다.
22
IntToLongFunction
int 값 인수를 허용하고 long 값 결과를 생성하는 함수를 나타냅니다.
23
IntUnaryOperator
단일 int 값 피연산자에 대한 연산을 나타내며 int 값 결과를 생성합니다.
24
LongBinaryOperator
두 개의 long 값 피연산자에 대한 연산을 나타내며 long 값 결과를 생성합니다.
25
LongConsumer
단일 long 값 인수를 허용하고 결과를 반환하지 않는 연산을 나타냅니다.
26
LongFunction<R>
long 값 인수를 허용하고 결과를 생성하는 함수를 나타냅니다.
27
LongPredicate
하나의 long 값 인수에 대한 프레디케이트(불리언 값 함수)를 나타냅니다.
28
LongSupplier
long 값 결과를 제공하는 공급자를 나타냅니다.
29
LongToDoubleFunction
long 값 인수를 허용하고 double 값 결과를 생성하는 함수를 나타냅니다.
30
LongToIntFunction
long 값 인수를 허용하고 int 값 결과를 생성하는 함수를 나타냅니다.
31
LongUnaryOperator
단일 long 값 피연산자에 대한 연산을 나타내며 long 값 결과를 생성합니다.
32
ObjDoubleConsumer<T>
객체 값과 double 값 인수를 허용하고 결과를 반환하지 않는 연산을 나타냅니다.
33
ObjIntConsumer<T>
객체 값과 int 값 인수를 허용하고 결과를 반환하지 않는 연산을 나타냅니다.
34
ObjLongConsumer<T>
객체 값과 long 값 인수를 허용하고 결과를 반환하지 않는 연산을 나타냅니다.
35
Predicate<T>
하나의 인수에 대한 프레디케이트(불리언 값 함수)를 나타냅니다.
36
Supplier<T>
결과를 제공하는 공급자를 나타냅니다.
37
ToDoubleBiFunction<T,U>
두 개의 인수를 허용하고 double 값 결과를 생성하는 함수를 나타냅니다.
38
ToDoubleFunction<T>
double 값 결과를 생성하는 함수를 나타냅니다.
39
ToIntBiFunction<T,U>
두 개의 인수를 허용하고 int 값 결과를 생성하는 함수를 나타냅니다.
40
ToIntFunction<T>
int 값 결과를 생성하는 함수를 나타냅니다.
41
ToLongBiFunction<T,U>
두 개의 인수를 허용하고 long 값 결과를 생성하는 함수를 나타냅니다.
42
ToLongFunction<T>
long 값 결과를 생성하는 함수를 나타냅니다.
43
UnaryOperator<T>
단일 피연산자에 대한 연산을 나타내며 피연산자와 동일한 타입의 결과를 생성합니다.
함수형 인터페이스 예제
Predicate<T> 인터페이스는 객체를 테스트하여 불리언 값을 반환하는 test(Object) 메서드를 가진 함수형 인터페이스입니다. 이 인터페이스는 객체가 true 또는 false로 테스트됨을 의미합니다.
원하는 편집기를 사용하여 다음 Java 프로그램을 작성하세요. (예: C:\> JAVA)
Java8Tester.java
Live Demo
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Java8Tester {
public static void main(String args[]) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// Predicate<Integer> predicate = n -> true
// n은 Predicate 인터페이스의 test 메서드에 매개변수로 전달됨
// test 메서드는 n의 값에 관계없이 항상 true 반환
System.out.println("모든 숫자 출력:");
// n을 매개변수로 전달
eval(list, n -> true);
// Predicate<Integer> predicate1 = n -> n%2 == 0
// n은 Predicate 인터페이스의 test 메서드에 매개변수로 전달됨
// n%2가 0이면 test 메서드는 true 반환
System.out.println("짝수 출력:");
eval(list, n -> n % 2 == 0);
// Predicate<Integer> predicate2 = n -> n > 3
// n은 Predicate 인터페이스의 test 메서드에 매개변수로 전달됨
// n이 3보다 크면 test 메서드는 true 반환
System.out.println("3보다 큰 숫자 출력:");
eval(list, n -> n > 3);
}
public static void eval(List<Integer> list, Predicate<Integer> predicate) {
for (Integer n : list) {
if (predicate.test(n)) {
System.out.println(n + " ");
}
}
}
}
여기서는 단일 입력을 받아 불리언을 반환하는 Predicate 인터페이스를 전달했습니다.
결과 확인
javac 컴파일러를 사용하여 클래스를 컴파일하세요.
C:\JAVA>javac Java8Tester.java
다음과 같이 Java8Tester를 실행하세요.
C:\JAVA>java Java8Tester
다음과 같은 출력이 나와야 합니다.
모든 숫자 출력:
1 2 3 4 5 6 7 8 9
짝수 출력:
2 4 6 8
3보다 큰 숫자 출력:
4 5 6 7 8 9
Java 8 - 기본 메서드
Java 8은 인터페이스에 기본 메서드 구현이라는 새로운 개념을 도입했습니다. 이 기능은 이전 인터페이스가 Java 8의 람다 표현식 기능을 활용할 수 있도록 하위 호환성을 위해 추가되었습니다.
예를 들어, 'List' 또는 'Collection' 인터페이스에는 'forEach' 메서드 선언이 없습니다. 따라서 이러한 메서드를 추가하면 컬렉션 프레임워크 구현이 깨질 수 있습니다. Java 8은 기본 메서드를 도입하여 List/Collection 인터페이스가 forEach 메서드의 기본 구현을 가질 수 있게 했고, 이 인터페이스를 구현하는 클래스는 이를 구현할 필요가 없습니다.
구문
public interface vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
}
다중 기본 메서드
인터페이스에 기본 함수가 있으면, 한 클래스가 동일한 기본 메서드를 가진 두 인터페이스를 구현할 가능성이 있습니다. 다음 코드는 이 모호성을 해결하는 방법을 설명합니다.
public interface vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
}
public interface fourWheeler {
default void print() {
System.out.println("I am a four wheeler!");
}
}
첫 번째 해결책은 기본 구현을 재정의하는 자체 메서드를 만드는 것입니다.
public class car implements vehicle, fourWheeler {
public void print() {
System.out.println("I am a four wheeler car vehicle!");
}
}
두 번째 해결책은 super를 사용하여 지정된 인터페이스의 기본 메서드를 호출하는 것입니다.
public class car implements vehicle, fourWheeler {
public void print() {
vehicle.super.print();
}
}
정적 기본 메서드
인터페이스는 Java 8부터 정적 헬퍼 메서드를 가질 수도 있습니다.
public interface vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
static void blowHorn() {
System.out.println("Blowing horn!!!");
}
}
기본 메서드 예제
원하는 편집기를 사용하여 다음 Java 프로그램을 작성하세요. (예: C:\> JAVA)
Java8Tester.java
Live Demo
public class Java8Tester {
public static void main(String args[]) {
Vehicle vehicle = new Car();
vehicle.print();
}
}
interface Vehicle {
default void print() {
System.out.println("I am a vehicle!");
}
static void blowHorn() {
System.out.println("Blowing horn!!!");
}
}
interface FourWheeler {
default void print() {
System.out.println("I am a four wheeler!");
}
}
class Car implements Vehicle, FourWheeler {
public void print() {
Vehicle.super.print();
FourWheeler.super.print();
Vehicle.blowHorn();
System.out.println("I am a car!");
}
}
결과 확인
javac 컴파일러를 사용하여 클래스를 컴파일하세요.
C:\JAVA>javac Java8Tester.java
다음과 같이 Java8Tester를 실행하세요.
C:\JAVA>java Java8Tester
다음과 같은 출력이 나와야 합니다.
I am a vehicle!
I am a four wheeler!
Blowing horn!!!
I am a car!
Java 8 - 스트림
스트림은 Java 8에 도입된 새로운 추상 계층입니다. 스트림을 사용하면 SQL 문과 유사하게 선언적 방식으로 데이터를 처리할 수 있습니다. 예를 들어, 다음 SQL 문을 고려해 보세요.
SELECT max(salary), employee_id, employee_name FROM Employee
위 SQL 표현식은 개발자의 계산 없이 최대 급여 직원의 세부 정보를 자동으로 반환합니다. Java의 컬렉션 프레임워크를 사용하면 개발자는 루프를 사용하고 반복적인 검사를 수행해야 합니다. 또 다른 우려는 효율성입니다. 멀티코어 프로세서를 쉽게 사용할 수 있지만, Java 개발자는 병렬 코드 처리를 작성해야 하며 이는 오류가 발생하기 쉽습니다.
이러한 문제를 해결하기 위해 Java 8은 개발자가 선언적으로 데이터를 처리하고 멀티코어 아키텍처를 활용할 수 있게 하는 스트림 개념을 도입했습니다. 이를 위해 특별한 코드를 작성할 필요가 없습니다.
스트림이란?
스트림은 소스에서 온 객체의 시퀀스를 나타내며 집계 연산을 지원합니다. 스트림의 특징은 다음과 같습니다.
요소의 시퀀스 − 스트림은 특정 타입의 요소 집합을 순차적으로 제공합니다. 스트림은 요소를 요청 시 가져오거나 계산합니다. 요소를 저장하지 않습니다.
소스 − 스트림은 컬렉션, 배열, I/O 리소스를 입력 소스로 사용합니다.
집계 연산 − 스트림은 filter, map, limit, reduce, find, match 등 집계 연산을 지원합니다.
파이프라이닝 − 대부분의 스트림 연산은 스트림 자체를 반환하여 결과를 파이프라이닝할 수 있습니다. 이러한 연산을 중간 연산이라 하며, 입력을 받아 처리하고 출력을 대상으로 반환하는 역할을 합니다. collect() 메서드는 파이프라이닝 연산의 끝에 일반적으로 존재하는 종단 연산으로 스트림의 끝을 표시합니다.
자동 반복 − 스트림 연산은 제공된 소스 요소에 대해 내부적으로 반복을 수행합니다. 이는 명시적 반복이 필요한 컬렉션과 대조됩니다.
스트림 생성
Java 8에서 Collection 인터페이스는 스트림을 생성하는 두 가지 메서드를 가집니다.
Java 8에서는 스트림 처리가 수행될 때 모든 통계를 계산하기 위해 통계 수집기가 도입되었습니다.
List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("리스트에서 가장 큰 숫자: " + stats.getMax());
System.out.println("리스트에서 가장 작은 숫자: " + stats.getMin());
System.out.println("모든 숫자의 합: " + stats.getSum());
System.out.println("모든 숫자의 평균: " + stats.getAverage());
스트림 예제
원하는 편집기를 사용하여 다음 Java 프로그램을 작성하세요. (예: C:\> JAVA)
Java8Tester.java
Live Demo
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.Map;
public class Java8Tester {
public static void main(String args[]) {
System.out.println("Java 7 사용: ");
// 빈 문자열 개수 세기
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
System.out.println("List: " + strings);
long count = getCountEmptyStringUsingJava7(strings);
System.out.println("빈 문자열: " + count);
count = getCountLength3UsingJava7(strings);
System.out.println("길이 3인 문자열: " + count);
// 빈 문자열 제거
List<String> filtered = deleteEmptyStringsUsingJava7(strings);
System.out.println("Filtered List: " + filtered);
// 빈 문자열 제거하고 쉼표로 결합
String mergedString = getMergedStringUsingJava7(strings, ", ");
System.out.println("Merged String: " + mergedString);
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 고유한 숫자의 제곱 리스트 얻기
List<Integer> squaresList = getSquares(numbers);
System.out.println("Squares List: " + squaresList);
List<Integer> integers = Arrays.asList(1,2,13,4,15,6,17,8,19);
System.out.println("List: " + integers);
System.out.println("리스트에서 가장 큰 숫자: " + getMax(integers));
System.out.println("리스트에서 가장 작은 숫자: " + getMin(integers));
System.out.println("모든 숫자의 합: " + getSum(integers));
System.out.println("모든 숫자의 평균: " + getAverage(integers));
System.out.println("난수: ");
// 10개의 난수 출력
Random random = new Random();
for (int i = 0; i < 10; i++) {
System.out.println(random.nextInt());
}
System.out.println("Java 8 사용: ");
System.out.println("List: " + strings);
count = strings.stream().filter(string -> string.isEmpty()).count();
System.out.println("빈 문자열: " + count);
count = strings.stream().filter(string -> string.length() == 3).count();
System.out.println("길이 3인 문자열: " + count);
filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("Filtered List: " + filtered);
mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("Merged String: " + mergedString);
squaresList = numbers.stream().map(i -> i*i).distinct().collect(Collectors.toList());
System.out.println("Squares List: " + squaresList);
System.out.println("List: " + integers);
IntSummaryStatistics stats = integers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("리스트에서 가장 큰 숫자: " + stats.getMax());
System.out.println("리스트에서 가장 작은 숫자: " + stats.getMin());
System.out.println("모든 숫자의 합: " + stats.getSum());
System.out.println("모든 숫자의 평균: " + stats.getAverage());
System.out.println("난수: ");
random.ints().limit(10).sorted().forEach(System.out::println);
// 병렬 처리
count = strings.parallelStream().filter(string -> string.isEmpty()).count();
System.out.println("빈 문자열: " + count);
}
private static int getCountEmptyStringUsingJava7(List<String> strings) {
int count = 0;
for (String string : strings) {
if (string.isEmpty()) {
count++;
}
}
return count;
}
private static int getCountLength3UsingJava7(List<String> strings) {
int count = 0;
for (String string : strings) {
if (string.length() == 3) {
count++;
}
}
return count;
}
private static List<String> deleteEmptyStringsUsingJava7(List<String> strings) {
List<String> filteredList = new ArrayList<String>();
for (String string : strings) {
if (!string.isEmpty()) {
filteredList.add(string);
}
}
return filteredList;
}
private static String getMergedStringUsingJava7(List<String> strings, String separator) {
StringBuilder stringBuilder = new StringBuilder();
for (String string : strings) {
if (!string.isEmpty()) {
stringBuilder.append(string);
stringBuilder.append(separator);
}
}
String mergedString = stringBuilder.toString();
return mergedString.substring(0, mergedString.length() - 2);
}
private static List<Integer> getSquares(List<Integer> numbers) {
List<Integer> squaresList = new ArrayList<Integer>();
for (Integer number : numbers) {
Integer square = new Integer(number.intValue() * number.intValue());
if (!squaresList.contains(square)) {
squaresList.add(square);
}
}
return squaresList;
}
private static int getMax(List<Integer> numbers) {
int max = numbers.get(0);
for (int i = 1; i < numbers.size(); i++) {
Integer number = numbers.get(i);
if (number.intValue() > max) {
max = number.intValue();
}
}
return max;
}
private static int getMin(List<Integer> numbers) {
int min = numbers.get(0);
for (int i = 1; i < numbers.size(); i++) {
Integer number = numbers.get(i);
if (number.intValue() < min) {
min = number.intValue();
}
}
return min;
}
private static int getSum(List numbers) {
int sum = (int) (numbers.get(0));
for (int i = 1; i < numbers.size(); i++) {
sum += (int) numbers.get(i);
}
return sum;
}
private static int getAverage(List<Integer> numbers) {
return getSum(numbers) / numbers.size();
}
}
결과 확인
javac 컴파일러를 사용하여 클래스를 컴파일하세요.
C:\JAVA>javac Java8Tester.java
다음과 같이 Java8Tester를 실행하세요.
C:\JAVA>java Java8Tester
다음과 같은 결과가 나와야 합니다.
Java 7 사용:
List: [abc, , bc, efg, abcd, , jkl]
빈 문자열: 2
길이 3인 문자열: 3
Filtered List: [abc, bc, efg, abcd, jkl]
Merged String: abc, bc, efg, abcd, jkl
Squares List: [9, 4, 49, 25]
List: [1, 2, 13, 4, 15, 6, 17, 8, 19]
리스트에서 가장 큰 숫자: 19
리스트에서 가장 작은 숫자: 1
모든 숫자의 합: 85
모든 숫자의 평균: 9
난수:
-1279735475
903418352
-1133928044
-1571118911
628530462
18407523
-881538250
-718932165
270259229
421676854
Java 8 사용:
List: [abc, , bc, efg, abcd, , jkl]
빈 문자열: 2
길이 3인 문자열: 3
Filtered List: [abc, bc, efg, abcd, jkl]
Merged String: abc, bc, efg, abcd, jkl
Squares List: [9, 4, 49, 25]
List: [1, 2, 13, 4, 15, 6, 17, 8, 19]
리스트에서 가장 큰 숫자: 19
리스트에서 가장 작은 숫자: 1
모든 숫자의 합: 85
모든 숫자의 평균: 9.444444444444445
난수:
-1009474951
-551240647
-2484714
181614550
933444268
1227850416
1579250773
1627454872
1683033687
1798939493
빈 문자열: 2
Java 8 - Optional 클래스
Optional은 null이 아닌 객체를 담는 컨테이너 객체입니다. Optional 객체는 값이 없을 때 null을 표현하는 데 사용됩니다. 이 클래스는 값을 '사용 가능' 또는 '사용 불가'로 처리할 수 있는 다양한 유틸리티 메서드를 제공하여, 직접 null을 확인하는 대신 안전하게 다룰 수 있게 해줍니다. Java 8에 도입되었으며, Guava의 Optional과 유사합니다.
클래스 선언
다음은 java.util.Optional<T> 클래스의 선언입니다.
public final class Optional<T> extends Object
클래스 메서드
번호
메서드 및 설명
1
static <T> Optional<T> empty()
빈 Optional 인스턴스를 반환합니다.
2
boolean equals(Object obj)
다른 객체가 이 Optional과 "같은지" 여부를 나타냅니다.
3
Optional<T> filter(Predicate<? super T> predicate)
값이 존재하고 주어진 프레디케이트와 일치하면 해당 값을 설명하는 Optional을 반환하고, 그렇지 않으면 빈 Optional을 반환합니다.
4
<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
값이 존재하면 제공된 Optional을 반환하는 매핑 함수를 적용하여 그 결과를 반환하고, 그렇지 않으면 빈 Optional을 반환합니다.
5
T get()
이 Optional에 값이 있으면 해당 값을 반환하고, 없으면 NoSuchElementException을 던집니다.
6
int hashCode()
값이 있으면 해당 값의 해시 코드를 반환하고, 없으면 0을 반환합니다.
7
void ifPresent(Consumer<? super T> consumer)
값이 존재하면 지정된 소비자(consumer)를 값과 함께 호출하고, 그렇지 않으면 아무 작업도 하지 않습니다.
8
boolean isPresent()
값이 있으면 true를 반환하고, 없으면 false를 반환합니다.
9
<U> Optional<U> map(Function<? super T, ? extends U> mapper)
값이 존재하면 제공된 매핑 함수를 적용하고, 결과가 null이 아니면 그 결과를 설명하는 Optional을 반환합니다.
10
static <T> Optional<T> of(T value)
지정된 null이 아닌 값이 존재하는 Optional을 반환합니다.
11
static <T> Optional<T> ofNullable(T value)
지정된 값이 null이 아니면 해당 값을 설명하는 Optional을 반환하고, null이면 빈 Optional을 반환합니다.
12
T orElse(T other)
값이 있으면 해당 값을 반환하고, 없으면 other를 반환합니다.
13
T orElseGet(Supplier<? extends T> other)
값이 있으면 해당 값을 반환하고, 없으면 other를 호출하여 그 결과를 반환합니다.
14
<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)
값이 있으면 해당 값을 반환하고, 없으면 제공된 공급자(supplier)가 생성하는 예외를 던집니다.
15
String toString()
디버깅에 적합한 이 Optional의 비어 있지 않은 문자열 표현을 반환합니다.
이 클래스는 다음 클래스에서 메서드를 상속받습니다.
java.lang.Object
Optional 예제
원하는 편집기를 사용하여 다음 Java 프로그램을 작성하세요. (예: C:\> JAVA)
Java8Tester.java
Live Demo
import java.util.Optional;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8Tester = new Java8Tester();
Integer value1 = null;
Integer value2 = new Integer(10);
// Optional.ofNullable - 전달된 매개변수가 null일 수 있음
Optional<Integer> a = Optional.ofNullable(value1);
// Optional.of - 전달된 매개변수가 null이면 NullPointerException 발생
Optional<Integer> b = Optional.of(value2);
System.out.println(java8Tester.sum(a,b));
}
public Integer sum(Optional<Integer> a, Optional<Integer> b) {
// Optional.isPresent - 값이 존재하는지 확인
System.out.println("첫 번째 매개변수 존재 여부: " + a.isPresent());
System.out.println("두 번째 매개변수 존재 여부: " + b.isPresent());
// Optional.orElse - 값이 있으면 반환, 없으면 기본값 반환
Integer value1 = a.orElse(new Integer(0));
// Optional.get - 값 가져오기, 값이 있어야 함
Integer value2 = b.get();
return value1 + value2;
}
}
결과 확인
javac 컴파일러를 사용하여 클래스를 컴파일하세요.
C:\JAVA>javac Java8Tester.java
다음과 같이 Java8Tester를 실행하세요.
C:\JAVA>java Java8Tester
다음과 같은 출력이 나와야 합니다.
첫 번째 매개변수 존재 여부: false
두 번째 매개변수 존재 여부: true
10
Java 8 - Nashorn JavaScript
Java 8에서는 기존 Rhino를 대체할 크게 개선된 JavaScript 엔진인 Nashorn이 도입되었습니다. Nashorn은 코드를 메모리에서 직접 컴파일하여 바이트코드를 JVM에 전달하기 때문에 2~10배 더 나은 성능을 제공합니다. Nashorn은 Java 7에서 도입된 invokedynamics 기능을 활용하여 성능을 향상시킵니다.
jjs
Nashorn 엔진을 위해 Java 8은 콘솔에서 JavaScript 코드를 실행할 수 있는 새로운 명령줄 도구 jjs를 도입했습니다.
C:\JAVA> jjs -- a b c
jjs> print('letters: ' + arguments.join(", "))
letters: a, b, c
jjs>
Java에서 JavaScript 호출
ScriptEngineManager를 사용하여 Java에서 JavaScript 코드를 호출하고 해석할 수 있습니다.
예제
원하는 편집기를 사용하여 다음 Java 프로그램을 작성하세요. (예: C:\> JAVA)
Java8Tester.java
Live Demo
import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
public class Java8Tester {
public static void main(String args[]) {
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine nashorn = scriptEngineManager.getEngineByName("nashorn");
String name = "Mahesh";
Integer result = null;
try {
nashorn.eval("print('" + name + "')");
result = (Integer) nashorn.eval("10 + 2");
} catch(ScriptException e) {
System.out.println("스크립트 실행 오류: "+ e.getMessage());
}
System.out.println(result.toString());
}
}
결과 확인
javac 컴파일러를 사용하여 클래스를 컴파일하세요.
C:\JAVA>javac Java8Tester.java
다음과 같이 Java8Tester를 실행하세요.
C:\JAVA>java Java8Tester
다음과 같은 결과가 나와야 합니다.
Mahesh
12
JavaScript에서 Java 호출
다음 예제는 JavaScript에서 Java 클래스를 가져와 사용하는 방법을 설명합니다.
sample.js 파일을 c:\> JAVA 폴더에 생성하고 저장하세요.
sample.js
var BigDecimal = Java.type('java.math.BigDecimal');
function calculate(amount, percentage) {
var result = new BigDecimal(amount).multiply(new BigDecimal(percentage)).divide(
new BigDecimal("100"), 2, BigDecimal.ROUND_HALF_EVEN);
return result.toPlainString();
}
var result = calculate(568000000000000000023,13.9);
print(result);
콘솔을 열고 다음 명령을 사용하세요.
C:\JAVA>jjs sample.js
다음과 같은 출력이 나와야 합니다.
78952000000000000003.20
Java 8 - 새로운 날짜/시간 API
Java 8에서는 기존 날짜/시간 API의 단점을 보완하기 위해 새로운 Date-Time API가 도입되었습니다.
스레드 안전하지 않음 − java.util.Date는 스레드 안전하지 않아 개발자가 날짜 사용 시 동시성 문제를 직접 처리해야 합니다. 새로운 날짜/시간 API는 불변(immutable)하며 setter 메서드가 없습니다.
부실한 설계 − 기본 Date가 1900년부터 시작하고, 월은 1부터, 일은 0부터 시작하여 일관성이 없습니다. 기존 API는 날짜 연산을 위한 직접적인 메서드가 부족했습니다. 새로운 API는 이러한 연산을 위한 다양한 유틸리티 메서드를 제공합니다.
어려운 시간대 처리 − 개발자는 시간대 문제를 처리하기 위해 많은 코드를 작성해야 했습니다. 새로운 API는 도메인별 설계를 염두에 두고 개발되었습니다.
Java 8은 java.time 패키지 아래에 새로운 날짜/시간 API를 도입했습니다. java.time 패키지에 도입된 주요 클래스는 다음과 같습니다.
Local − 시간대 처리의 복잡성 없이 단순화된 날짜/시간 API입니다.
Zoned − 다양한 시간대를 다루기 위한 특화된 날짜/시간 API입니다.
Local Date-Time API
LocalDate, LocalTime, LocalDateTime 클래스는 시간대가 필요 없는 개발을 단순화합니다. 실제로 어떻게 작동하는지 살펴보겠습니다.
원하는 편집기를 사용하여 다음 Java 프로그램을 작성하세요. (예: C:\> JAVA)
Java8Tester.java
Live Demo
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.LocalDateTime;
import java.time.Month;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testLocalDateTime();
}
public void testLocalDateTime() {
// 현재 날짜와 시간 가져오기
LocalDateTime currentTime = LocalDateTime.now();
System.out.println("현재 날짜시간: " + currentTime);
LocalDate date1 = currentTime.toLocalDate();
System.out.println("date1: " + date1);
Month month = currentTime.getMonth();
int day = currentTime.getDayOfMonth();
int seconds = currentTime.getSecond();
System.out.println("월: " + month + " 일: " + day + " 초: " + seconds);
LocalDateTime date2 = currentTime.withDayOfMonth(10).withYear(2012);
System.out.println("date2: " + date2);
// 2014년 12월 12일
LocalDate date3 = LocalDate.of(2014, Month.DECEMBER, 12);
System.out.println("date3: " + date3);
// 22시 15분
LocalTime date4 = LocalTime.of(22, 15);
System.out.println("date4: " + date4);
// 문자열 파싱
LocalTime date5 = LocalTime.parse("20:15:30");
System.out.println("date5: " + date5);
}
}
결과 확인
javac 컴파일러를 사용하여 클래스를 컴파일하세요.
C:\JAVA>javac Java8Tester.java
다음과 같이 Java8Tester를 실행하세요.
C:\JAVA>java Java8Tester
다음과 같은 출력이 나와야 합니다.
현재 날짜시간: 2014-12-09T11:00:45.457
date1: 2014-12-09
월: DECEMBER 일: 9 초: 45
date2: 2012-12-10T11:00:45.457
date3: 2014-12-12
date4: 22:15
date5: 20:15:30
Zoned Date-Time API
시간대를 고려해야 할 때 Zoned date-time API를 사용합니다. 실제로 어떻게 작동하는지 살펴보겠습니다.
원하는 편집기를 사용하여 다음 Java 프로그램을 작성하세요. (예: C:\> JAVA)
Java8Tester.java
Live Demo
import java.time.ZonedDateTime;
import java.time.ZoneId;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testZonedDateTime();
}
public void testZonedDateTime() {
// 현재 날짜와 시간 가져오기
ZonedDateTime date1 = ZonedDateTime.parse("2007-12-03T10:15:30+05:30[Asia/Karachi]");
System.out.println("date1: " + date1);
ZoneId id = ZoneId.of("Europe/Paris");
System.out.println("ZoneId: " + id);
ZoneId currentZone = ZoneId.systemDefault();
System.out.println("CurrentZone: " + currentZone);
}
}
java.time.temporal.ChronoUnit 열거형은 기존 API에서 일, 월 등을 나타내던 정수 값을 대체하기 위해 Java 8에 추가되었습니다. 실제로 어떻게 작동하는지 살펴보겠습니다.
원하는 편집기를 사용하여 다음 Java 프로그램을 작성하세요. (예: C:\> JAVA)
Java8Tester.java
Live Demo
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testChromoUnits();
}
public void testChromoUnits() {
// 현재 날짜 가져오기
LocalDate today = LocalDate.now();
System.out.println("현재 날짜: " + today);
// 현재 날짜에 1주일 추가
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS);
System.out.println("다음 주: " + nextWeek);
// 현재 날짜에 1개월 추가
LocalDate nextMonth = today.plus(1, ChronoUnit.MONTHS);
System.out.println("다음 달: " + nextMonth);
// 현재 날짜에 1년 추가
LocalDate nextYear = today.plus(1, ChronoUnit.YEARS);
System.out.println("다음 해: " + nextYear);
// 현재 날짜에 10년 추가
LocalDate nextDecade = today.plus(1, ChronoUnit.DECADES);
System.out.println("10년 후 날짜: " + nextDecade);
}
}
결과 확인
javac 컴파일러를 사용하여 클래스를 컴파일하세요.
C:\JAVA>javac Java8Tester.java
다음과 같이 Java8Tester를 실행하세요.
C:\JAVA>java Java8Tester
다음과 같은 결과가 나와야 합니다.
현재 날짜: 2014-12-10
다음 주: 2014-12-17
다음 달: 2015-01-10
다음 해: 2015-12-10
10년 후 날짜: 2024-12-10
Period와 Duration
Java 8에서는 시간 차이를 다루기 위한 두 가지 특화된 클래스가 도입되었습니다.
Period − 날짜 기반의 시간량을 다룹니다.
Duration − 시간 기반의 시간량을 다룹니다.
실제로 어떻게 작동하는지 살펴보겠습니다.
원하는 편집기를 사용하여 다음 Java 프로그램을 작성하세요. (예: C:\> JAVA)
Java8Tester.java
Live Demo
import java.time.temporal.ChronoUnit;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Duration;
import java.time.Period;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testPeriod();
java8tester.testDuration();
}
public void testPeriod() {
// 현재 날짜 가져오기
LocalDate date1 = LocalDate.now();
System.out.println("현재 날짜: " + date1);
// 현재 날짜에 1개월 추가
LocalDate date2 = date1.plus(1, ChronoUnit.MONTHS);
System.out.println("다음 달: " + date2);
Period period = Period.between(date2, date1);
System.out.println("Period: " + period);
}
public void testDuration() {
LocalTime time1 = LocalTime.now();
Duration twoHours = Duration.ofHours(2);
LocalTime time2 = time1.plus(twoHours);
Duration duration = Duration.between(time1, time2);
System.out.println("Duration: " + duration);
}
}
결과 확인
javac 컴파일러를 사용하여 클래스를 컴파일하세요.
C:\JAVA>javac Java8Tester.java
다음과 같이 Java8Tester를 실행하세요.
C:\JAVA>java Java8Tester
다음과 같은 출력이 나와야 합니다.
현재 날짜: 2014-12-10
다음 달: 2015-01-10
Period: P-1M
Duration: PT2H
Temporal Adjusters
TemporalAdjuster는 날짜 계산을 수행하는 데 사용됩니다. 예를 들어, "이달의 둘째 토요일" 또는 "다음 화요일"을 구할 수 있습니다. 실제로 어떻게 작동하는지 살펴보겠습니다.
원하는 편집기를 사용하여 다음 Java 프로그램을 작성하세요. (예: C:\> JAVA)
Java8Tester.java
Live Demo
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.time.DayOfWeek;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testAdjusters();
}
public void testAdjusters() {
// 현재 날짜 가져오기
LocalDate date1 = LocalDate.now();
System.out.println("현재 날짜: " + date1);
// 다음 화요일 구하기
LocalDate nextTuesday = date1.with(TemporalAdjusters.next(DayOfWeek.TUESDAY));
System.out.println("다음 화요일: " + nextTuesday);
// 다음 달의 둘째 토요일 구하기
LocalDate firstInYear = LocalDate.of(date1.getYear(), date1.getMonth(), 1);
LocalDate secondSaturday = firstInYear.with(TemporalAdjusters.nextOrSame(DayOfWeek.SATURDAY))
.with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
System.out.println("둘째 토요일: " + secondSaturday);
}
}
결과 확인
javac 컴파일러를 사용하여 클래스를 컴파일하세요.
C:\JAVA>javac Java8Tester.java
다음과 같이 Java8Tester를 실행하세요.
C:\JAVA>java Java8Tester
다음과 같은 결과가 나와야 합니다.
현재 날짜: 2014-12-10
다음 화요일: 2014-12-16
둘째 토요일: 2014-12-13
하위 호환성
기존 Date와 Calendar 객체에 toInstant() 메서드가 추가되어 이를 새로운 Date-Time API로 변환할 수 있습니다. ofInstant(Instant, ZoneId) 메서드를 사용하여 LocalDateTime 또는 ZonedDateTime 객체를 얻을 수 있습니다. 실제로 어떻게 작동하는지 살펴보겠습니다.
원하는 편집기를 사용하여 다음 Java 프로그램을 작성하세요. (예: C:\> JAVA)
Java8Tester.java
Live Demo
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.Date;
import java.time.Instant;
import java.time.ZoneId;
public class Java8Tester {
public static void main(String args[]) {
Java8Tester java8tester = new Java8Tester();
java8tester.testBackwardCompatability();
}
public void testBackwardCompatability() {
// 현재 날짜 가져오기
Date currentDate = new Date();
System.out.println("현재 날짜: " + currentDate);
// 현재 날짜의 Instant를 밀리초 단위로 가져오기
Instant now = currentDate.toInstant();
ZoneId currentZone = ZoneId.systemDefault();
LocalDateTime localDateTime = LocalDateTime.ofInstant(now, currentZone);
System.out.println("Local date: " + localDateTime);
ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(now, currentZone);
System.out.println("Zoned date: " + zonedDateTime);
}
}
결과 확인
javac 컴파일러를 사용하여 클래스를 컴파일하세요.
C:\JAVA>javac Java8Tester.java
다음과 같이 Java8Tester를 실행하세요.
C:\JAVA>java Java8Tester
다음과 같은 출력이 나와야 합니다.
현재 날짜: Wed Dec 10 05:44:06 UTC 2014
Local date: 2014-12-10T05:44:06.635
Zoned date: 2014-12-10T05:44:06.635Z[Etc/UTC]
Java 8 - Base64
Java 8에서 Base64가 드디어 제자리를 찾았습니다. Java 8에는 Base64 인코딩을 위한 내장 인코더와 디코더가 있습니다. Java 8에서는 세 가지 유형의 Base64 인코딩을 사용할 수 있습니다.
단순(Simple) − 출력이 A-Za-z0-9+/ 문자 집합에 매핑됩니다. 인코더는 출력에 줄 바꿈을 추가하지 않으며, 디코더는 A-Za-z0-9+/ 이외의 문자를 거부합니다.
URL − 출력이 A-Za-z0-9+_ 문자 집합에 매핑됩니다. 출력이 URL 및 파일명으로 안전합니다.
MIME − 출력이 MIME 친화적 형식으로 표현됩니다. 출력은 최대 76자씩 줄로 표현되며, 캐리지 리턴('\r')과 라인피드('\n')를 줄 구분자로 사용합니다. 인코딩된 출력의 끝에는 줄 구분자가 없습니다.
중첩 클래스
번호
중첩 클래스 및 설명
1
static class Base64.Decoder
RFC 4648 및 RFC 2045에 지정된 Base64 인코딩 방식을 사용하여 바이트 데이터를 디코딩하는 디코더를 구현합니다.
2
static class Base64.Encoder
RFC 4648 및 RFC 2045에 지정된 Base64 인코딩 방식을 사용하여 바이트 데이터를 인코딩하는 인코더를 구현합니다.
메서드
번호
메서드 이름 및 설명
1
static Base64.Decoder getDecoder()
Basic 유형 base64 인코딩 방식을 사용하여 디코딩하는 Base64.Decoder를 반환합니다.
2
static Base64.Encoder getEncoder()
Basic 유형 base64 인코딩 방식을 사용하여 인코딩하는 Base64.Encoder를 반환합니다.
3
static Base64.Decoder getMimeDecoder()
MIME 유형 base64 디코딩 방식을 사용하여 디코딩하는 Base64.Decoder를 반환합니다.
4
static Base64.Encoder getMimeEncoder()
MIME 유형 base64 인코딩 방식을 사용하여 인코딩하는 Base64.Encoder를 반환합니다.