Java 8 정리 - java 8 lambda Stream Optional Null LocalDate Time example 자바 람다 스트림 타입 패키지
Github
https://github.com/syakuis/java8
키워드
java 8 lambda Stream Optional Null LocalDate Time 자바 람다 스트림 타입 패키지
Java 8 정리자바 설치 - OpenJDK 8람다 표현식함수 인터페이스인수가 두개인 경우인수가 한개인 경우인수가 없는 경우OptionalOptional 생성Optional 조건Stream스트림 생성중간 연산최종 연산Time package - 새로운 타임 패키지LocalDate사전 정의 된 포매터Nashorn (나즈혼) - 자바스크립트 엔진
자바 설치 - OpenJDK 8
macOS - brew package manager
AdoptOpenJDK 는 IBM 과 RedHat 이 공동으로 배포하는 open jdk 이다.
$ brew tap AdoptOpenJDK/openjdk
$ brew cask install adoptopenjdk8
windows - chocolatey package manager
> choco install zulu8
람다 표현식
장점
자바의 메서드들은 일반적으로 먼저 실행되어 왔다. 하지만 자바8 에서 람다는 지연 연산 혹은 게으른 이라는 표현을 사용하는 필요에 의해 메서드가 실행된다.
// 가장 적절한 예로 조건문을 들 수 있다.
if (false || test()) {
// true
} else {
// false
}일반적인 조건문에서 false 선행되면 작업이 종료되는 데. 위와 같은 경우에는 test 메서드가 실행되고 조건을 판단한다. 불필요한 작업을 하고 있다.
자료형을 추론할 수 있어 코드가 간결해진다.
동작 가능한 코드를 인수(아규먼트)로 전달할 수 있다.
public void test(String a) {} // 여기 a 는 인수 : argument test("테스트"); // 여기서 '테스트' 는 인자, 매개변수 : parameter
람다식 예시
( parameters ) -> expression body
( parameters ) -> { expression body }
() -> { expression body }
() -> expression body
함수 인터페이스
자바는 변수에는 값과 객체만 담을 수 있다. 즉 일급 함수를 지원하지 않았다. 하지만 자바8 부터 인터페이스를 이용하여 변수에 함수를 담을 수 있다. 기존에 있던 인터페이스와 같지만 이것은 람다를 위한 인터페이스이고 람다를 위한 인터페이스만 일급 함수로 사용할 수 있다. 직접 함수 인터페이스를 만들때는 @FunctionalInterface
를 설정해야 람다를 위한 인터페이스로 인식할 수 있다. 또한 람다용 인터페이스는 한개의 추상 메서드만 가질수 있다.
함수 인터페이스를 이용하여 동작 파라메터화를 구현할 수 있다. 동작 파라메터화란 동작을 수행할 수 있는 코드를 인수로 전달하는 것을 말한다.
예제
package org.syaku.study.java8.lambda;
public class Functional {
interface StringUtils {
String concat(String value, String value2);
}
public void 인터페이스() {
StringUtils string = (v1, v2) -> v1 + v2;
assertEquals(string.concat("Hello", " World!!!"), "Hello World!!!");
}
private String concat(String value, String value2, Function<String, String> fn) {
return fn.apply(value + value2);
}
public void 메서드() {
assertEquals(concat("Hello", " World!!!", p -> p), "Hello World!!!");
}
public void 직접() {
Function<String, String> string = v -> v + " World!!!";
assertEquals(string.apply("Hello"), "Hello World!!!");
}
}
함수 인터페이스를 만들기 위한 Function 인터페이스를 사용했다. 인터페이스에는 한개의 abstract method, 2개의 default method 그리고 1개의 static method가 있다.
디폴트 메서드를 보면 compose 와 andThen 이 있는 데 이둘은 연산 순서를 의미한다.
public void composeAndThen() {
Function<Integer, Integer> one = v -> 10 * v;
Function<Integer, Integer> two = v -> v * v;
assertEquals(one.compose(two).apply(2).intValue(), 40);
assertEquals(one.andThen(two).apply(2).intValue(), 400);
}
결과와 같이 componse 는 자신의 two 인자부터 실행되고 andThen 는 two 안자가 나중에 실행된다.
자바 8부터 default 메서드를 사용할 수 있다. 디폴트 메서드를 이용하여 인터페이스에 구현 메서드를 정의할 수 있다. 이전에는 중복되는 메서드를 클래스나 추상 클래스에 구현하여 사용했지만 이젠 인터페이스에 디폴트 메서드를 구현하여 사용하면 된다. 떠힌 상속한 클래스에 디폴트 메서드를 오버라이드할 수 있다.
오버라이딩: 부모의 메서드를 재정의할 수 있다.
오버로딩: 같은 클래스에 같은 메서드를 결과와 인수의 형식을 다르게하여 여러개 정의할 수 있다.
자주 사용되는 함수 인터페이스를 자바에서 기본적으로 제공된다.
공식 https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
인수가 두개인 경우
biConsumer: 원하는 유형 두개의 인수를 받고 결과는 없다.
biFunction: 원하는 유형 두개의 인수를 받고 원하는 유형의 결과를 반환한다.
binaryOperator: 원하는 유형 두개의 인수를 받고 결과를 반환한다. 단 인수와 결과의 유형이 모두 동일해야 한다.
biPredicate: 원하는 유형 두개의 인수를 받고 결과를 boolean 으로 반환한다.
인수가 한개인 경우
Consumer: 원하는 유형 한개의 인수를 받고 결과는 없다.
Function: 원하는 유형 한개의 인수를 받고 원하는 유형의 결과를 반환한다.
UnaryOperator: 원하는 유형 한개의 인수를 받고 결과를 반환한다. 단 인수와 결과의 유형이 모두 동일해야 한다.
Predicate: 원하는 유형 한개의 인수를 받고 결과를 boolean 으로 반환한다.
인수가 없는 경우
supplier: 인수가 없고 원하는 유형의 결과를 반환한다.
public class Functional {
interface StringUtils {
String concat(String value, String value2);
}
public void 인터페이스() {
StringUtils string = (v1, v2) -> v1 + v2;
assertEquals(string.concat("Hello", " World!!!"), "Hello World!!!");
}
private String concat(String value, String value2, Function<String, String> fn) {
return fn.apply(value + value2);
}
public void 메서드() {
assertEquals(concat("Hello", " World!!!", p -> p), "Hello World!!!");
}
public void 직접() {
Function<String, String> string = v -> v + " World!!!";
assertEquals(string.apply("Hello"), "Hello World!!!");
}
public void composeAndThen() {
Function<Integer, Integer> one = v -> 10 * v;
Function<Integer, Integer> two = v -> v * v;
assertEquals(one.compose(two).apply(2).intValue(), 40);
assertEquals(one.andThen(two).apply(2).intValue(), 400);
}
public void biConsumer() {
BiConsumer<String, String> func = (v1, v2) -> {
assertEquals(v1, "a");
assertEquals(v2, "b");
};
func.accept("a", "b");
}
public void biFunction() {
BiFunction<String, String, String> func = (v1, v2) -> v1 + v2;
assertEquals(func.apply("syaku", "syaku"), "syakusyaku");
}
public void binaryOperator() {
BinaryOperator<List<String>> func = (v, v2) -> {
List<String> result = new ArrayList<>(v);
result.addAll(v2);
return result;
};
List<String> kr = Arrays.asList("가", "나");
List<String> us = Arrays.asList("a", "b", "c");
List<String> result = new ArrayList<>(kr);
result.addAll(us);
assertEquals(func.apply(kr, us), result);
BinaryOperator<Integer> max = BinaryOperator.maxBy(Comparator.naturalOrder());
assertEquals(max.apply(1, 2).intValue(), 2);
BinaryOperator<Integer> min = BinaryOperator.minBy(Comparator.naturalOrder());
assertEquals(min.apply(1, 2).intValue(), 1);
}
public void biPredicate() {
// (v, v2) -> Objects.equals(v, v2);
BiPredicate<String, String> func = Objects::equals;
assertFalse(func.test("A", "B"));
assertTrue(func.test("A", "A"));
}
public void booleanSupplier() {
BooleanSupplier func = () -> {
log.debug("실행됨.");
return false;
};
assertFalse(func.getAsBoolean());
// 매개변수 한개가 false 이므로 두개다 실행되지 않는 다.
BiFunction<BooleanSupplier, BooleanSupplier, Boolean> bool = (v, v2) -> v.getAsBoolean() && v2.getAsBoolean();
bool.apply(func, func);
}
public void consumer() {
Consumer<String> func = v -> assertEquals(v, "a");
func.accept("a");
}
public void doubleBinaryOperator() {
DoubleBinaryOperator func = (v, v2) -> v + v2;
assertEquals(func.applyAsDouble(1.1, 1.1), 2.2, 2.2);
}
public void doubleConsumer() {
DoubleConsumer func = v -> assertEquals(v, 1.1, 1.1);
func.accept(1.1);
}
public void doubleFunction() {
// v -> String.valueOf(v)
DoubleFunction<String> func = String::valueOf;
assertEquals(func.apply(1.2), "1.2");
}
public void doublePredicate() {
DoublePredicate func = v -> v > 1.1;
assertTrue(func.test(1.2));
}
public void doubleSupplier() {
DoubleSupplier func = () -> 1.1;
assertEquals(func.getAsDouble(), 1.1, 1.1);
}
public void doubleToIntFunction() {
DoubleToIntFunction func = v -> new Double(v).intValue();
assertEquals(func.applyAsInt(1.1), 1);
assertEquals(func.applyAsInt(1.9), 1);
}
public void doubleToLongFunction() {
DoubleToLongFunction func = v -> new Double(v).longValue();
assertEquals(func.applyAsLong(1.1), 1L);
assertEquals(func.applyAsLong(1.9), 1L);
}
public void doubleUnaryOperator() {
DoubleUnaryOperator func = v -> v * 2;
assertEquals(func.applyAsDouble(2.1), 4.2, 4.2);
DoubleUnaryOperator func2 = v -> v * v;
assertEquals(func.andThen(func2).applyAsDouble(5), 100, 100);
assertEquals(func.compose(func2).applyAsDouble(5), 50, 50);
}
public void function() {
Function<String, String> func = v -> v + "a";
assertEquals(func.apply("a"), "aa");
Function<String, String> func2 = v -> v + v;
// func2 call
assertEquals(func.compose(func2).apply("b"), "bba");
// func call
assertEquals(func.andThen(func2).apply("b"), "baba");
}
// Int, Long, Obj Function 은 Double 유사하므로 생략한다.
public void predicate() {
Predicate<String> func = v -> Objects.equals(v, "ok");
assertTrue(func.test("ok"));
// 부정
assertFalse(func.negate().test("ok"));
// v -> "ok".contains(v)
Predicate<String> func2 = "aaaaa"::contains;
assertFalse(func.and(func2).test("ok"));
assertTrue(func.or(func2).test("ok"));
}
public void supplier() {
String a = null;
Supplier<Boolean> func = () -> {
log.debug("실행됨.");
return a != null;
};
// 매개변수 한개가 false 이므로 두개다 실행되지 않는 다.
BiFunction<Supplier, Supplier, Boolean> bool = (v, v2) -> (boolean) v.get() && (boolean) v2.get();
bool.apply(func, func);
}
public void unaryOperator() {
UnaryOperator<String> func = v -> v + "a";
assertEquals(func.apply("a"), "aa");
}
}
Optional
null 을 사용했을 때 문제점을 해결하기 위해 사용된다. null 을 사용하지 않고 값을 다룰수 있게 한다.
java.util.Optional<T>
에 대해 알아보겠다.
Optional 생성
empty - null 을 가진 Optional 객체를 생성한다.
of - 값을 가진 Optional 객체를 생성한다.
ofNullable - 매개변수가 null 인 경우 Optional.empty 로 아닌 경우 Optional.of 로 객체를 생성한다.
get - 값을 반환한다. null 인 경우
NoSuchElementException
발생한다.
Optional 조건
isPresent - 값이 null 인 경우 false, 아닌 경우 true 를 반환한다.
ifPresent - 값이 null 이 아닌 경우 지정한 Consumer 를 호출한다. 이전에는
if (val != null) { 실행영역 }
구문으로 사용했다.orElse - 값이 null 인 경우 원하는 값으로 변경한다.
orElseGet - 값이 있는 경우 값을 반환하고 null 인 경우 지정된 Supplier 를 호출한다. 리터럴이 아닌 로직에 의한 값을 반환할 수 있다.
orElseThrow - 값이 있는 경우 값을 반환하고 null 인 경우 원하는 예외를 발생한다.
filter - 스트림의 filter 와 유사하나 값을 순차적으로 처리하지 않고 생성한 Optional 객체를 인수로 받고 Optional 로 반환한다.
map - 스트림의 map 과 유사하나 값을 순차적으로 처리하지 않고 생성한 Optional 객체를 인수로 받고 Optional 로 반환한다.
flatMap - 스트림의 flatMap 과 유사하나 값을 순차적으로 처리하지 않고 생성한 Optional 객체를 인수로 받고 Optional 로 반환한다.
OptionalInt, OptionalLong, OptionalDouble 에서 원시 자료형을 다룰 수 있다.
Stream
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
순차 연산과 병렬 연산 처리가 있다.
최종 연산을 하지 않으면 실제로 동작되지 않는 다.
순차적으로 처리되므로 반복작업을 줄일 수 있는 연산부터 처리해야 한다.
일반적인 순환문은 for 문이 좋다.
스트림 생성
builder - 빌더 패턴으로 스트림 생성
empty - 빈 스트림 생성
of - 하나 또는 그 이상의 매개변수를 설정하여 스트림을 생성한다.
iterate - 순환문을 이용하여 스트림을 생성한다.
generate
concat - 두 스트림을 하나로 만든다.
public class InitialStream {
public void builder() {
List<Integer> result = Stream
// 스트림은 제너릭 타입을 사용하므로 값의 타입을 추론할 수 있도록 파라미터 타입을 설정했다.
// 타입을 설정하지 않으면 기본적 타입은 Object 로 선언된다
.<Integer>builder().add(1).add(2).build()
// 결과를 얻는 다.
.collect(Collectors.toList());
assertEquals(result, asList(1,2));
}
public void empty() {
List<Integer> result = Stream.<Integer>empty().collect(Collectors.toList());
assertEquals(result, Collections.emptyList());
}
public void of() {
// todo of 는 파라미터 타입을 선언하지 않아도 추론이 가능하다. why?
List<String> kor = Stream.of("가").collect(Collectors.toList());
List<Integer> num = Stream.of(1, 2, 3).collect(Collectors.toList());
assertEquals(kor, Collections.singletonList("가"));
assertEquals(num, asList(1,2, 3));
}
public void iterate() {
// todo 어디에 쓸까?
List<String> result = Stream
// 무한 반복이 된다. limit 를 이용하여 횟수를 설정한다.
.iterate("안녕", n -> n).limit(2)
.collect(Collectors.toList());
assertEquals(result, asList("안녕", "안녕"));
}
public void generate() {
// todo 어디에 쓸까?
List<String> result = Stream
// Supplier 함수 인터페이스를 사용 - 인수 없고 결과 있음
.generate(() -> "안녕")
.limit(2).collect(Collectors.toList());
assertEquals(result, asList("안녕", "안녕"));
}
public void concat() {
List<Integer> result = Stream.concat(Stream.of(1,2), Stream.of(3,4)).collect(Collectors.toList());
assertEquals(result, asList(1,2,3,4));
}
}
중간 연산
filter - 순차적으로 지정한 Perdicate 를 호출한다. 스트림으로 반환한다.
map
mapToInt
mapToLong
mapToDouble
flatMap
flatMapToInt
flatMapToLong
flatMapToDouble
distinct
sorted
peek
limit
skip
boxed
박싱은 int -> Integer 변경하는 것이고 언박싱은 반대이다. 즉 원시 유형을 참조 유형으로 변경하는 것을 의미하면 이를 자동으로 변경하는 것을 오토박싱이라고 한다.
public class IntermediateStream {
private List<String> color = Arrays.asList("Green", "Black", "Yellow", "Red", "Black", "Yellow");
private List<Integer> number = Arrays.asList(4, 5, 1, 3, 6, 5, 1);
public void filter() {
List<String> result =
// stream() 컬랙션을 스트림으로 사용한다.
color.stream()
// 필터를 이용하여 R 을 가진 데이터만 추출한다.
.filter(n -> n.contains("R"))
.collect(Collectors.toList());
assertEquals(result, Collections.singletonList("Red"));
}
public void map() {
List<Integer> result = color.stream()
// Function 함수 인터페이스를 사용하고 원하는 자료형의 데이터로 반환한다.
// n -> n.length() 의 축약
.map(String::length)
.collect(Collectors.toList());
assertEquals(result, Arrays.asList(5, 5, 6, 3, 5, 6));
}
public void mapToInt() {
// mapToInt 는 최종 데이터를 숫자로 반환하는 것을 목표하는 것 같다.
// 컬랙션의 모든 합과 같은 최종 결과를 숫자로 표기할때 사용된다.
int total = color.stream()
// mapToInt 는 IntStream 으로 반환하며 IntStream 원시 자료형 int 로 반환한다.
.mapToInt(String::length).sum();
assertEquals(total, 30);
}
// mapToLong , mapToDouble 생략함
public void flatMap() {
// 테스트
// 2차 배열을 1차 배열로 만든다라는 설명으로 이해할 수 없었다.
List<List<String>> list =
Arrays.asList(Arrays.asList("a"), Arrays.asList("b", "c"));
// 이해하기 위해 두가 코드로 작성하고 반환 자료형을 확인하였다.
// map 과 flatMap 의 차이.
// map 은 인자 그대로 반환하고 즉 아래처럼 스트림으로 만들면 스트림으로 반환된다.
// flatMap 은 반환 자료형은 스트림의 제너릭 자료형으로 스트림의 값을 반환한다.
list.stream()
// n -> n.stream()
.map(Collection::stream)
// List<Stream<String>>
.collect(Collectors.toList()).forEach(log::debug);
List<String> result = list.stream().flatMap(Collection::stream)
// List<String>
.collect(Collectors.toList());
result.forEach(log::debug);
assertEquals(result, Arrays.asList("a", "b", "c"));
}
public void flatMapToInt() {
List<List<String>> list =
Arrays.asList(Collections.singletonList("a"), Arrays.asList("b", "c"));
int total = list.stream()
// flatMapToInt 은 IntStream 으로 반환해야 한다. 즉 반환 자료형은 int 여야 한다.
// mapToInt 를 이용하면 스트림의 값을 IntStream 으로 변환할 수 있다.
.flatMapToInt(n -> n.stream().mapToInt(String::length)).sum();
assertEquals(total, 3);
}
// flatMapToLong , flatMapToDouble 생략
public void distinct() {
List<String> result = color.stream().distinct().collect(Collectors.toList());
assertEquals(result, Arrays.asList("Green", "Black", "Yellow", "Red"));
}
public void sorted() {
List<Integer> result = number.stream().sorted().collect(Collectors.toList());
assertEquals(result, Arrays.asList(1, 1, 3, 4, 5, 5, 6));
List<Integer> result2 = number.stream().sorted((o1, o2) -> o2 - o1).collect(Collectors.toList());
assertEquals(result2, Arrays.asList(6, 5, 5, 4, 3, 1, 1));
result.forEach(log::debug);
result2.forEach(log::debug);
}
public void peek() {
// peek = void consumer(T s)
List<String> result = color.stream().peek(s -> {
assertTrue(color.indexOf(s) > -1);
}).collect(Collectors.toList());
assertEquals( result,color);
}
public void limit() {
List<String> result = color.stream().limit(2).collect(Collectors.toList());
assertEquals(result, Arrays.asList("Green", "Black"));
}
public void skip() {
List<String> result = color.stream().skip(1).collect(Collectors.toList());
assertEquals(result, Arrays.asList("Black", "Yellow", "Red", "Black", "Yellow"));
}
}
최종 연산
forEach
forEachOrdered
toArray
collect
min
max
count
anyMatch
allMatch
noneMatch
findFirst
findAny
reduce
public class TerminalStream {
private List<String> color = Arrays.asList("Green", "Black", "Yellow", "Red", "Black", "Yellow");
private List<Integer> number = Arrays.asList(4, 5, 1, 3, 6, 5, 1);
public void forEach() {
// peek 은 중간 연산이고 forEach 는 최종 연산이다.
// peek 는 최종 연산을 하지 않으면 실행되지 않는 지연 연산에 속한다.
color.forEach(s -> {
log.debug(s);
assertTrue(color.indexOf(s) > -1);
});
}
public void forEachOrdered() {
// forEach 병렬 스트림에서 정렬을 보장받지 못한다. 보장받기 위해 forEachOrdered 를 사용해야 한다.
color.forEach(s -> {
log.debug(s);
assertTrue(color.indexOf(s) > -1);
});
}
public void toArray() {
String[] result = color.stream().toArray(String[]::new);
assertArrayEquals(result, color.toArray());
}
public void collect() {
List<String> result = number.stream().map(String::valueOf).collect(Collectors.toList());
for(int i = 0; i < number.size(); i++) {
assertEquals(result.get(i), String.valueOf(number.get(i)));
}
}
public void min() {
// orElse 는 null 인 경우 0 을 반환한다.
int result = number.stream().min(Integer::compareTo).orElse(0);
assertEquals(result, 1);
}
public void max() {
int result = number.stream().max(Integer::compareTo).orElse(0);
assertEquals(result, 6);
}
public void count() {
long result = number.stream().count();
assertEquals(result, 7);
}
public void anyMatch() {
boolean aTrue = number.stream().anyMatch(s -> s == 1);
assertTrue(aTrue);
boolean aFalse = number.stream().anyMatch(s -> s == 0);
assertFalse(aFalse);
}
public void allMatch() {
// anyMatch 와 다름 점은 and 조건이다.
boolean aFalse = number.stream().allMatch(s -> s == 1);
assertFalse(aFalse);
List<String> color = Arrays.asList("Green", "Black");
boolean aTrue = color.stream().allMatch(s -> s.length() == 5);
assertTrue(aTrue);
}
public void noneMatch() {
// allMatch 의 반대이다.
boolean aTrue = color.stream().noneMatch(s -> s.equals("Gray"));
assertTrue(aTrue);
}
public void findFirst() {
// 비어있지 않는 첫번째 값을 반환한다.
String result = color.stream().filter(s -> s.equals("Black")).findFirst().orElse("none");
assertEquals(result, "Black");
}
public void findAny() {
// 순서와 상관없이 일치하는 값을 하나 반환한다.
String result = color.stream().filter(s -> s.equals("Black")).findFirst().orElse("none");
assertEquals(result, "Black");
}
public void reduce() {
// 다양한 결과를 만들 수 있다.
Map<String, Integer> result = color.stream().reduce(new HashMap<>(), (map, value) -> {
map.put(value, value.length());
return map;
}, (map1, map2) -> {
map1.putAll(map2);
return map1;
});
color.forEach(s -> assertEquals(s.length(), result.get(s).intValue()));
}
}
Time package - 새로운 타임 패키지
이전의 Date, Calendar 클래스에는 오랜전에 개발되어 다양한 문제를 가지고 있다. 이를 극복하기 위해 새로운 클래스가 등장하게 되었다. LocalDate, LocalTime 과 LocalDateTime 에 대해 알아본다.
LocalDate
LocalDate 클래스 아래와 같은 특징이 있다.
불변으로 스레드에 대해서 안전하다.
ISO-8601 (YYYY-MM-DD) 으로 시간이 없다.
값 기반 클래스이므로 참조 동등성(==)이 아닌 동일성(eqauls)으로 비교해야 한다.
모든 메서드는 정적으로 작성되어 있다.
사전 정의 된 포매터
포매터 | 기술 | 예 |
---|---|---|
ofLocalizedDate(dateStyle) | 로케일의 날짜 스타일이있는 포매터 | '2011-12-03' |
ofLocalizedTime(timeStyle) | 로케일에서 시간 스타일이있는 포매터 | '10 : 15 : 30 ' |
ofLocalizedDateTime(dateTimeStyle) | 로케일에서 날짜와 시간 스타일을 가진 포매터 | '3 Jun 2008 11:05:30' |
ofLocalizedDateTime(dateStyle,timeStyle) | 로케일의 날짜 및 시간 스타일이있는 포매터 | '3 Jun 2008 11:05' |
BASIC_ISO_DATE | 기본 ISO 날짜 | '20111203' |
ISO_LOCAL_DATE | ISO 현지 날짜 | '2011-12-03' |
ISO_OFFSET_DATE | 오프셋이있는 ISO 날짜 | '2011-12-03 + 01 : 00' |
ISO_DATE | 오프셋이 있거나없는 ISO 날짜 | '2011-12-03 + 01 : 00'; '2011-12-03' |
ISO_LOCAL_TIME | 오프셋없는 시간 | '10 : 15 : 30 ' |
ISO_OFFSET_TIME | 오프셋 시간 | '10 : 15 : 30 + 01 : 00 ' |
ISO_TIME | 오프셋이 있거나없는 시간 | '10 : 15 : 30 + 01 : 00 '; '10 : 15 : 30 ' |
ISO_LOCAL_DATE_TIME | ISO 지역 날짜 및 시간 | '2011-12-03T10 : 15 : 30' |
ISO_OFFSET_DATE_TIME | 오프셋이있는 날짜 시간 | 2011-12-03T10 : 15 : 30 + 01 : 00 ' |
ISO_ZONED_DATE_TIME | 지역 날짜 시간 | '2011-12-03T10 : 15 : 30 + 01 : 00 [유럽 / 파리]' |
ISO_DATE_TIME | ZoneId의 날짜 및 시간 | '2011-12-03T10 : 15 : 30 + 01 : 00 [유럽 / 파리]' |
ISO_ORDINAL_DATE | 연중 무휴 | '2012-337' |
ISO_WEEK_DATE | 년 및 주 | 2012-W48-6 ' |
ISO_INSTANT | 순간의 날짜와 시간 | '2011-12-03T10 : 15 : 30Z' |
RFC_1123_DATE_TIME | RFC 1123 / RFC 822 | 'Tue, 3 Jun 2008 11:05:30 GMT' |
Symbol Meaning Presentation Examples
------ ------- ------------ -------
G era text AD; Anno Domini; A
u year year 2004; 04
y year-of-era year 2004; 04
D day-of-year number 189
M/L month-of-year number/text 7; 07; Jul; July; J
d day-of-month number 10
Q/q quarter-of-year number/text 3; 03; Q3; 3rd quarter
Y week-based-year year 1996; 96
w week-of-week-based-year number 27
W week-of-month number 4
E day-of-week text Tue; Tuesday; T
e/c localized day-of-week number/text 2; 02; Tue; Tuesday; T
F week-of-month number 3
a am-pm-of-day text PM
h clock-hour-of-am-pm (1-12) number 12
K hour-of-am-pm (0-11) number 0
k clock-hour-of-am-pm (1-24) number 0
H hour-of-day (0-23) number 0
m minute-of-hour number 30
s second-of-minute number 55
S fraction-of-second fraction 978
A milli-of-day number 1234
n nano-of-second number 987654321
N nano-of-day number 1234000000
V time-zone ID zone-id America/Los_Angeles; Z; -08:30
z time-zone name zone-name Pacific Standard Time; PST
O localized zone-offset offset-O GMT+8; GMT+08:00; UTC-08:00;
X zone-offset 'Z' for zero offset-X Z; -08; -0830; -08:30; -083015; -08:30:15;
x zone-offset offset-x +0000; -08; -0830; -08:30; -083015; -08:30:15;
Z zone-offset offset-Z +0000; -0800; -08:00;
p pad next pad modifier 1
' escape for text delimiter
'' single quote literal '
[ optional section start
] optional section end
# reserved for future use
{ reserved for future use
} reserved for future use
출처: https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html
public class LocalDateTest {
public void instance() {
LocalDate localDate = LocalDate.now();
log.debug(localDate);
}
public void format() {
LocalDate localDate = LocalDate.now();
log.debug(localDate.format(DateTimeFormatter.ofPattern("yyyy년 MM월 dd일")));
}
public void ymd() {
LocalDate localDate = LocalDate.now();
log.debug("{} 년 {} 월 {} 일({}) 올해 {} 일째 이번달 총 {}일",
localDate.getYear(),
localDate.getMonthValue(),
localDate.getDayOfMonth(),
localDate.getDayOfWeek(),
localDate.getDayOfYear(),
localDate.lengthOfMonth());
}
}
타임 패키지는 엄청 쉽고 이해하기 좋게 잘 만들어졌다. 솔직히 쓸 내용도 없다 메뉴얼 보고 쓱쓱 짜면 된다. 만세!!!
https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html
https://docs.oracle.com/javase/8/docs/api/java/time/LocalTime.html
https://docs.oracle.com/javase/8/docs/api/java/time/LocalDateTime.html
Nashorn (나즈혼) - 자바스크립트 엔진
나즈혼이 먼가 했더니 자바의 자바스크립트 엔진이다. 프론트엔드 SPA 를 개발하다보면 모든 코드가 자바스크립트로 개발되어 있어 검색엔진이 이를 해독하지 못하게 된다. 그래서 SEO 라는 걸 구현해야 하는 데. 주로 Node.js 를 활용하는 게 일반적인다. 하지만 백엔드 서버 프로그램이 자바로 개발되었는 데 SEO 때문에 Node.js 구축해야 한다는 건 비효율적이다. 자바 8 나즈혼으로 SEO 를 원할하게 서비스 할 수 있는 지는 나도 모른다.
나즈혼은 ECMAScript 5.1 까지 지원되며 앞으로 표준으로 따라 지원을 확장해나간다고 했다.
예제 코딩은 따로 다뤄야할 것 같다. 웹팩을 이용하여 번들링된 리액트 코드를 나즈혼으로 실행해볼 생각이다.
'Back-end' 카테고리의 다른 글
Spring security Role Hierarchy - 역할 계층 구현하기 (0) | 2019.07.05 |
---|---|
spring boot RestDocs 코드 리팩토링과 Spring RestDocs 적용 (0) | 2018.11.01 |
spring boot security 스프링 부트 시큐리티 설정과 사용자 인증 #1 (0) | 2018.10.29 |
spring boot jpa 스프링 부트 블로그 만들기 #2 - blog (0) | 2018.10.20 |