(오키) 자바의정석, 챕터 11 - Collections Framework

11-1~2 컬렉션프레임웍과 핵심 인터페이스

  • 컬렉션(collection) : 여러 객체를 모아 놓은것
  • 프레임웍(framework) : 표준화, 정형화 된 체계적인 프로그래밍 형식
  • collection framework: 컬렉션을 다루기 위한 표준화된 프로그래밍 형식, java.util 패키지에 포함, JDK1.2 부터 표준화
  1. List: 순서 유지 O, 중복 허용 O (ArrayList, LinkedList, Stack, Vector 등)
  2. Set: 순서 유지 X, 중복 허용 X (HashSet, TreeSet 등)
  3. Map: 키(key)와 값(value)의 쌍(pair), 순서 유지 X, 키 중복 허용 X, 값 중복 허용 O
    (HashMap, TreeMap, Hashtable, Properties)

11-3~6 Collection - List, Set

List와 Set은 Collection 인터페이스를 가진다.

Collection 인터페이스의 주요 메서드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
boolean add(Object o);  // (추가)
boolean add(Collection c);

boolean remove(Object o); // (삭제)
boolean removeAll(Collection c);
boolean retainAll(Collection c) ; //포함된 객체만 남기고 다른 객체 삭제
void clear(); // 전체 삭제

boolean contains(Object o); // 포함 여부 확인 (검색)
boolean contains(Collection c);

boolean isEmpty(); // 비어 있는지 확인
int size(); // 저장된 객체수 확인

Iterator iteraotr(); // iterator 반환

Object[] toArray(); // 객체배열로 반환
Object[] toArray(Object[] a); // 지정된 배열에 Collection 객체를 저장해서 반환한다?

11-4 List 인터페이스 - 순서 O, 중복 O

구현체: ArrayList, LinkedList, Vector <- Stack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

//추가
void add(int index, Object element);
boolean addAll(int index, Collection c);
Object set (int index, Object element);

//검색
Object get(int index); // 읽기
int indexOf(Object o);
int lastIndexOf(Object o);

//삭제
Object remove(int index);

//정렬
void sort(Comparator c);
List subList(int fromIndex, int toIndex);

11-5 Set 인터페이스 - 순서 X, 중복 X

구현체: HashSet, SortedSet <- TreeSet
주요 메서드: Collection 과 동일

1
2
3
4
boolean addAll(Collection c); // 합집합
boolean containsAll(Collection c); // 포함 확인 (부분 집합)
boolean removeAll(Collection c); // 차집합
boolean retailAll (Collection c); // 교집합 (포함된 객체만 남기고 나머지는 삭제)

11-6 Map 인터페이스 - 순서X, 중복(키X, 값O)

구현체: Hashtable, HashMap <- LinkedHashMap (순서 O), SortedMap <- TreeMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 추가
Object put(Object key, Object value);
void putAll(Map t);

//삭제
Object remove(Object key);
void clear();

//검색
boolean containsKey(Object key);
boolean containsValue(Object value);
Object get(Object key);
boolean isEmpty();
int size();

Set entrySet(); // 저장된 모든 객체를 key, value 의 객체로 반환
Set keySet(); // Map 에 저장된 모든 key 객체를 반환
Collection values(); // Map 에 저장된 모든 value 객체를 반환

11-7 ArrayList

  • 내부적으로 Array 잡음
  • thread-safe X

11-8 ArrayList 의 메서드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//생성자
ArrayList();
ArrayList(Collection c);
ArrayList(int initialCapacity);

// 추가
boolean add(Object o);
void add(int index, Object element);
boolean addAll(Collection c);
boolean addAll(int index, Collection c);

//삭제
boolean remove(Object o);
Object remove(int index);
boolean removeAll(Collection c);
void clear();

//검색
int indexOf(Object o); // 못 찾으면 -1
int lastIndexOf(Object o);
boolean contains(Object o);
Object get(int index); // 특정 위치 객체 읽기
Object set(int index, Object element); // 특정 위치 객체 변경

//이외
List subList(int fromIndex, int toIndex);
Object[] toArray();
Object[] toArray(Object[] a);
boolean isEmpty();
void trimToSize(); // 빈공간 제거
int size(); // 저장된 객체 반환

ch 11-12 LinkedList - 배열의 장단점

우선 배열, ArrayList 에 대한 장단점

  • 장점: 배열의 구조 간단, 데이터를 읽는 데 걸리는 시간이 짧다

예: int arr -> 1, 2, 3, 4, 5 이면 n번째 arr 주소 + 4 byte * n

  • 단점1: 크기 변경시 데이터 복사가 일어남
  • 단점2: 비순차적 데이터 추가 삭제에 시간이 많이 걸린다, 맨끝에 하는것 제외

LinkedList는 배열의 단점 보완, 단점은 데이터를 읽는 시간이 더 걸린다.
구현에 따라 Double LinkedList, Circular Double LinkedList가 있다.

ch11-15 Stack and Queue

Stack: LIFO 구조, 마지막 저장 제일 먼저 꺼낸다, push, pop
자바에서 Stakc이란 클래사가 있음

1
2
3
4
5
6
7
Stack s = new Stack();

boolean empty();
Object pop(); // 비었을때는 EmptyStackException 발생
Object push(Object item);
int search(Object o); // 못찾으면 -1, index가 1부터 시작
Object peek();

Queue: FIFO 구조, 제일 먼저 저장 제일 먼저 꺼낸다, offer, poll
자바에서 Queue는 인터페이스 이다. 구현체로 new 해야함, API 문서 보면 많은 구현체가 있음
(LinkedList, Abstract Queue, ArrayBlockingQueue, ArrayDeque, PriorityQueue 등등)

1
2
3
4
5
6
7
8
9
10
Queue q = new LinkedList();

boolean offer(Object o); // 추가
boolean add(Object o); // 저장 공간 부족하면 IllegalStateException 발생

Object poll(); // 꺼내기, 비어 있으면 null 반환
Object remove(); // 비어 있으면 NoSuchElementException 발생

Object peek(); // 비어 있으면 null 반환
Object element(); // peek 비어 있으면 NoSuchElementException 발생

ch11-19 스택과 큐 의 활용

스택: 수식계산, 수식괄호 검사, 워드프로세서의 undo/redo, 웹브라우저의 뒤로/앞으로
큐: 최근사용문서, 인쇄 작업 대기목록, 버퍼(buffer)

ch11-22~24 Iterator, ListIterator, Enumeration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface Collection {
//...
public Iterator iteartor();
//...
}

boolean hasNext(); // 읽어올 요소가 남아 있는지 확인
Object next(); // 다음 요소를 읽어 온다

List list = new ArrayList();
Iterator it = list.iterator();

while(it.hasNext()) {
System.out.println(it.next());
}

ch11-24 Map 과 Interator

Map 에서 바로 interator() 호출 안되고 keySet(), entrySet(), values() 를 호출해야 된다.

1
2
3
Map map = new HashMap();
//...
Iterator it = map.entrySet().iterator();

ch11-25 Arrays - 배열을 다루기 편리한 메서드(static) 제공

  1. 배열의 출력 - toString()
  2. 배열의 복사 - copyOf(), copyOfRange()
  3. 배열 채우기 - fill(), setAll()
  4. 배열의 정렬과 검색 - sort(), binarySearch()
1
2
3
4
5
6
7
8
9
int[] arr = new int[5];
Arrays.fill(arr, 9);
Arrays.setAll(arr, (i) -> (int) (Math.randome()*5)+1)

int[] arr = { 3, 2, 0, 1, 4};
int index = Arrays.binarySearch(arr, 2); // 정렬되어 있을때만 호출 가능, 아니면 잘못된 결과

Arrays.sort(arr);
int idx = Arrays.binarySearch(arr, 2);

ch11-30 Comparator 와 Comparable

  • 객체 정렬에 필요한 메서드 (정렬 기준 제공)를 정의한 인터페이스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public interface Comparator {
int compare(Object o1, Object o2); // o1, o2 두 객체를 비교
// 양수 왼쪽 큼, 0, 같음, 음수: 오른쪽 큼
boolean equals(Object obj); // equals를 오버라이딩 하라는 뜻
}

public interface Comparable {
int compareTo(Object o); // 주어진 객체(o)를 자신과 비교
}

public final class Integer extends Number implements Comparable {
//...
public int compareTo(Integer anotherInteger) {
int v1 = this.value;
int v2 = anotherInteger.value;

return (v1 < v2 ? -1 : (v1==v2? 0 : 1));
}
}

Arrays.sort(strArr, String.CASE_INSENSITIVE_ORDER);

public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();

Arrays.sort(strArr, new Descending());
class Descending implements Comparator {
public int compare(Object o1, Object o2) {
if( o1 instanceof Comparable && o2 instanceof Comparable) {
Comparable c1 = (Comparable) o1;
Comparable c2 = (Comparable) o2;
return c1.compareTo(c2) * -1;
}
return -1;
}
}

ch11-34 HashSet - 순서 X, 중복 X

  • HashSet : Set 인터페이스를 구현한 대표적인 컬렉션 클래스
  • TreeSet: 범위 검색과 정렬에 유리, HashSet 보다 데이터 추가, 삭제에 시간이 더 걸림
  • boolean add(Object o)는 저장할 객체의 equals()와 hashCode()를 호출
  • equals()와 hashCode()가 오버라이딩 되어 있어야 함
1
2
3
4
5
6
7
8
9
10
11
12
13
class Person {
String name;
int age;
//..
}
public boolean equals(Object obj) {
if(!(obj instanceof Person)) return false;
Person tmp = (Person) obj;
return name.equals(tmp.name) && age==tmp.age;
}
public int hashCode() {
return Objects.hash(name, age);
}

ch11-39 TreeSet - 범위 탐색, 정렬

  • 이진 탐색 트리(binary search tree)로 구현. 범위 탐색과 정렬에 유리
  • 노트가 최대 2개의 하위 노드를 가지고 있음 (LinkedList의 변형) 나무 모양

ch11-41 TreeSet - 데이터 저장 과정 boolean add(Object o)

  • Tree 형태로 만들어짐
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
TreeSet(); // 기본 생성자
TreeSet(Collection c);
TreeSet(Comparator comp); // 주어진 정렬 기준으로 TreeSet 생성

Object first();
Object last();

Object ceiling(Object o);
Object floor(Object o);
Object higher(Object o);
Object lower(Object o);

SortedSet subSet(Object fromElement, Object toElement); // 범위 검색
SortedSet headSet(Object toElement); // 지정 된 객체보다 작은 것을 반환
SortedSet tailSet(Object fromElement); // 지정된 객체보다 큰것을 반환

ch11-46 HashMap과 Hashtable - 순서 X, 중복 (키X, 값O)

  • Map 인터페이스를 구현. 데이터를 키와 값의 쌍으로 저장
  • 해시 함수(hash function)로 해시 테이블(Hash table)에 데이터를 저장, 검색
  • 키(key) -> 해시함수(hash function) -> 해시코드(hash code) = 배열 index

ch11-48 HashMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
HashMap(); 
HashMap(int initialCapacity);
HashMap(int initialCapacity, float loadFactor);
HashMap(Map m);

Set entrySet();
Set keySet();
Collection values();

Object put(Object key, Object value);
void putAll(Map m);
Object remove(Object key);
Object replace(Object key, Object value);
boolean replace(Object key, Object oldValue, Object newValue);

ch11-52~54 Collections - 컬렉션을 위한 메서드(static)를 제공

  • 컬렉션 채우기, 복사, 정렬, 검색 - fill(), copy(), sort(), binarySearch() 등
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 동기화 버전
static Collection synchronizedCollection(Collection c);
static List synchronizedList(List list);
static Set synchronizedSet(Set s);
static Map synchronizedMap(Map s);
static SortedSet synchronizedSortedSet(SortedSet s);

List syncList = Collections.synchronizedList(new ArrayList(...)); // 동기화 되지 않은

// 변경 불가 (readonly) 버전
static Collection unmodifiedCollection(Collection c);
//....

// 싱글톤 컬렉션 만들기
static List singletonList(Object o); // 객체 1개만 저장
static Set singleton(Object o);
static Map singletonMap(Object key, Object value);

// 한 종류의 객체만 저장 하는 컬렉션 만들기
static Collection checkedCollection(Collection c, Class type);
static List checkedList(List list, Class type);
static Set checkedSet(Set s, Class type);
//....

List list = new ArrayList();
List checkedList = checkedList(list, String.class);
checkedList.add("abc");
checkedList.add(new Integer(3)); // 에러

// 여러 메서드 등
import static java.util.Collections.*;
List list = new ArrayList();
addAll(list, 1,2,3,4,5);
rotate(list,2);
swap(list,0,2);
shuffle(list);
sort(list);
binarySearch(list,3);
max(list);
min(list);
fill(list,9);
nCopies(list.size(), 2);

List newList = nCopies(list.size(), 2);
disjoint(list, newList); // 공통 요소가 없으면 true
copy(list, newList);
replaceAll(list, 2, 1);

Enumeration e = enumeration(list);
ArrayList list2 = list(e);

포토폴리오

Java, SpringBoot, JSP, JPA - 게시판

국비 자바 과정에서 만든 REST 기반의 CRUD 기능의 게시판 입니다.
DB로는 MySQL 사용 했습니다.

https://github.com/miromike/board-jsp-jpa-mysql
목록화면
등록화면
업데이트 화면
DB화면

이외 다른 국비 자바 과정 Spring boot

  • 네이버 맛집
  • 서버, 클라이언트 통신
    (올리기 위해 정리중)

Java, SpringBoot, JPA, Mustache, Boostrap

이동욱(진행중)

인프런 강좌 Spring boot, Thymeleaf

(진행중)

파이썬 - 공공 데이터, 코인, 트위터 API 활용 검색 및 매매 프로그램

공공 데이터, 네이버, 코인베이스, 트위터 등 OPEN API 활용 하는 파이썬 프로그램

https://replit.com/@miromike
코인검색
트윗검색
코인매매
부동산매매정보검색

C#, Winform - 회원관리 프로그램

회원관리 프로그램 입니다.
DB로 Firebase 사용했습니다.

https://github.com/miromike/register
동작화면
DB화면

학습노트4 - 머스테치로 화면 구성 하기

템플릿 엔진이란?

지정된 템플릿 양식과 데이터 합쳐 HTML 문서 출력

서버 템플릿 엔진과 클라이언트 템플릿 엔진

서버 템플릿 엔진: JSP, Freemarker
클라이언트 템플릿 엔진: 리액트(React), 뷰(Vue)의 View 파일

React.js, Vue.js

Single Page Application SPA 라고 함
Json 혹은 XML이 클라이언트로 전달 됨
최근 진화 되어 자바스크립트 프레임워크에서 서버 사이드 렌더링(Server Side Rendering) 도 가능

자바 진영 템플릿 엔진

다양한 서버 템플릿 엔진 이 존재

  1. JSP, Velocity: 스프링 부트에서 권장 X
  2. Freemarker: 과하게 많은 기능 지원
  3. Thymealeaf: 스프링 진영에서 밀고 있음. HTML 태크에 속성 문법 어려울수 있음 단 이런 방식을 쓰는 Vue.js 에 익숙한 개발자에겐 편함

머스테치 http://mustache.github.io

많은 언어 지원 하는 가장 심플한 템플릿 엔진

  1. 문법이 심플
  2. 로직 코드 사용 할수 없어 View역할만
  3. Mustache.js 와 Mustache.java 2가지 있어, 하나의 문법으로 클라이언트/서버 템플릿 사용 가능

인텔리제이의 Plugin 설치

mustache 로 검색 하여 설치, 문법체크, HTML 문법 지원, 자동완성 됨

의존성

spring boot 에서 공식 지원 하는 것임으로 알수 있음
implementation 'org.springframework.boot:spring-boot-starter-mustache'

첫 예제, index.html

resources/templates/index.mustache

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE HTML>
<html>
<head>
<title> 스프링 부트 웹서비스 </title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
</head>
<body>
<h1>스프링 부트로 시작하는 웹 서비스</h1>
</body>
</html>
1
2
3
4
5
6
7
8
9
@Controller
public class IndexController {

@GetMapping("/")
String index()
{
return "index";
}
}

게시판 화면 만들기

HTML 만 사용하기엔 멋이 없으니 부트 스트랩, 제이쿼리등 프로트엔드 라이브러리 사용

부트 스트랩, 제이쿼리 사용 방식

2가지 방식 존재: 외부 CDN 사용, 직접 라이브러리 받아서 사용
외부 CDN 사용 활용 해보기로 함, 실제 서비스는 외부 의존이 생김으로 자주 안 쓰임

구조 및 파일 작성

  1. 레이아웃 방식: 공통 영역을 별도의 파일로 분리 하여 필요한 곳에서 가져다 쓰는 방식
  2. resources/templates 에 layout 디렉토리 추가
  3. footer.mustache, header.mustache 파일 생성

웹페이지는 위에서 아래로 로딩 되니 header에 CSS, body 제일 하단에 자바 스크립트.
css 가 적용 안된 깨진 화면을 사용자에게 안 보여주기 위함임

index.mustache 의 코드 변경

1
2
3
{{>laytout/header}}
<h1> 스프링 부트로 시작 하는 웹서비스 </h1>
{{>laytout/footer}}

header.mustache 파일 생성

1
2
3
4
5
6
7
8
9
<!DOCTYPE HTML>
<html>
<head>
<title>스프링부트 웹서비스</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
</head>
<body>

footer.mustache 파일 생성

1
2
3
4
5
6
7
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>

<!--index.js 추가-->
<script src="/js/app/index.js"></script>
</body>
</html>

게시글 등록 HTML 생성

posts-save.mustache 파일 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{{>layout/header}}

<h1>게시글 등록</h1>

<div class="col-md-12">
<div class="col-md-4">
<form>
<div class="form-group">
<label for="title">제목</label>
<input type="text" class="form-control" id="title" placeholder="제목을 입력하세요">
</div>
<div class="form-group">
<label for="author"> 작성자 </label>
<input type="text" class="form-control" id="author" placeholder="작성자를 입력하세요">
</div>
<div class="form-group">
<label for="content"> 내용 </label>
<textarea class="form-control" id="content" placeholder="내용을 입력하세요"></textarea>
</div>
</form>
<a href="/" role="button" class="btn btn-secondary">취소</a>
<button type="button" class="btn btn-primary" id="btn-save">등록</button>
</div>
</div>

{{>layout/footer}}

Footer에 넣은 index.js 파일 생성

static/js/app 폴더에 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var index = {

init : function () {
var _this = this;
$('#btn-save').on('click', function() {
_this.save();
});
},

save : function () {
var data = {
title: $('#title').val(),
author: $('#author').val(),
content: $('#content').val()
};

$.ajax({
type: 'POST',
url: '/api/v1/posts',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
data: JSON.stringify(data)
}).done(function () {
alert('글이 등록 되었습니다.');
window.location.href = '/';
}).fail(function (error) {
alert(JSON.stringify(error));
});
}
};

index.init();

Controller에 URL mapping

URL 요청의 뷰 지정은 전부 IndexController.java 파일에 추가

1
2
3
4
@GetMapping("/posts/save")
String postsSave() {
return "posts-save";
}

목록 List 나오게 수정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{{>layout/header}}
<h1>스프링 부트로 시작하는 웹 서비스</h1>
<div class="col-md-12">
<div class="row">
<div class="col-md-6">
<a href="/posts/save" role="button" class="btn btn-primary">글 등록</a>
</div>
</div>
<!-- 목록 -->
<table class ="table table-horizontal table-bordered">
<thead class="thead-strong">
<tr>
<th>게시물번호</th>
<th>제목</th>
<th>작성자</th>
<th>최종수정일</th>
</tr>
</thead>
<tbody id="tbody">
{{#posts}}
<tr>
<td>{{id}}</td>
<td>{{title}}</td>
<td>{{author}}</td>
<td>{{modifiedDate}}</td>
</tr>
{{/posts}}
</tbody>
</table>
</div>
{{>layout/footer}}

DB Query 사용

FK 조인 및 복잡한 조건 일때 조회용 프레임워크 사용을 하기도 한다.
querydsl(타입 안정성 보장), jooq, myBatis
여기서는 간단히 JPA가 제공 하는 @Query 사용

1
2
3
4
5
6
7
8
import org.springframework.data.jpa.repository.Query;

public interface PostsRepository extends JpaRepository<Posts, Long> {

@Query("SELECT p FROM Posts p ORDER BY p.id DESC")
List<Posts> findAllDesc();

}

readOnly는 조회만 하는 메서드 에 넣어 주면 속도 개선
.map(PostsListResponseDto::new).map(posts -> new PostsListResponseDto(posts)) 와 동일

1
2
3
4
5
6
7
8

@Transactional(readOnly = true)
public List<PostsListResponseDto> findAllDesc()
{
return postsRepository.findAllDesc().stream()
.map(PostsListResponseDto::new)
.collect(Collectors.toList());
}

이제 Controller 부분에 목록을 넘겨주면 된다.
Model model은 템플릿 엔진 에서 사용하게 되는 객체이고 여기에 posts 에 목록을 담아서 넘겨주게 된다.

1
2
3
4
5
6
7
8
9
10
11
@RequiredArgsConstructor
@Controller
public class IndexController {

private final PostsService postsService;

@GetMapping("/")
String index(Model model) {
model.addAttribute("posts", postsService.findAllDesc());
return "index";
}

Update 기능 추가

posts-update.mustache view 파일 추가

  1. readonly는 input 태크에 수정 안되게 해주는 속성
  2. btn-update 버튼 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{{>layout/header}}

<h1>게시글 수정</h1>

<div class="col-md-12">
<div class="col-md-4">
<form>
<div class="form-group">
<label for="title">글 번호</label>
<input type="text" class="form-control" id="id" value="{{post.id}}" readonly>
</div>
<div class="form-group">
<label for="title">제목</label>
<input type="text" class="form-control" id="title" value="{{post.title}}">
</div>
<div class="form-group">
<label for="author"> 작성자 </label>
<input type="text" class="form-control" id="author" value="{{post.author}}" readonly>
</div>
<div class="form-group">
<label for="content"> 내용 </label>
<textarea class="form-control" id="content">{{post.content}}</textarea>
</div>
</form>
<a href="/" role="button" class="btn btn-secondary">취소</a>
<button type="button" class="btn btn-primary" id="btn-update">수정 완료</button>
<button type="button" class="btn btn-danger" id="btn-delete">삭제</button>
</div>
</div>

{{>layout/footer}}

js file 에 btn-update 기능 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...

$('#btn-update').on('click', function () { // 버튼 이벤트
_this.update();
});

...
update : function () {
var data = {
title: $('#title').val(),
content: $('#content').val()
};

var id = $('#id').val();

$.ajax({
type: 'PUT', // PUT 사용
url: '/api/v1/posts/'+id, // 만든 api 을 호출
dataType: 'json',
contentType:'application/json; charset=utf-8',
data: JSON.stringify(data)
}).done(function() {
alert('글이 수정되었습니다.');
window.location.href = '/';
}).fail(function (error) {
alert(JSON.stringify(error));
});
},

Index 뷰파일 수정

1
2
3
4
5
6
<tr>
<td>{{id}}</td>
<td><a href="/posts/update/{{id}}">{{title}}</a></td>
<td>{{author}}</td>
<td>{{modifiedDate}}</td>
</tr>

학습노트3 - JPA

JPA 란?

Database 접근을 자바에서 객체 지향으로 할수 있도록 하는 기술
이를 ORM 기술이라고 하고 JPA는 자바 진영의 ORM 표준 이름
Spring 에서는 Hiberate -> Spring Data JPA -> 으로 사용 하게 된다

MyBatis

SQL Mapping 기술
DAO 작성 해서 사용

Gradle에 library 추가

implementation ‘org.springframework.data:spring-data-jpa’
implementation ‘com.h2database:h2’

도메인 구조

Domain 패키지를 새로 만들고 안에 Entity와 Repository을 같이 작성 할 것이다.
(더 공부 하려면 최범균 DDD Start 책을 참고 할수 있다고 한다.)
Entity는 DB 테이블 이라고 보면 되고 클래스로 만들어 줘야 한다.
Repository는 DB에 접근해주는기 위한 셋팅 이고 interface로 만들어 줘야 한다.

1. Posts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Getter
@NoArgsConstructor
@Entity
public class Posts {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(length = 500, nullable = false)
private String title;

@Column(columnDefinition = "TEXT", nullable = false)
private String content;

private String author;

@Builder
public Posts(String title, String content, String author) {
this.title = title;
this.content = content;
this.author = author;
}

public void update(String title, String content) {
this.title = title;
this.content = content;
}
}

public interface PostsRepository extends JpaRepository<Posts, Long> {

}

테스트 코드

  • @SpringBootTest 시 별다른 설정 없으면 H2 데이터베이스 사용 된다
  • deleteAll(), findAll()
  • save(): insert 혹은 update SQL 호출
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@RunWith(SpringRunner.class)
@SpringBootTest
public class PostsRepositoryTest {

@Autowired
PostsRepository postsRepository;

@After
public void cleanup() {
postsRepository.deleteAll();
}

@Test
public void 게시글저장_불러오기() {
//given
String title = "테스트 게시글";
String content = "테스트 본문";

postsRepository.save(Posts.builder()
.title(title)
.content(content)
.author("miromike@gmail.com")
.build());

//when
List<Posts> postsList = postsRepository.findAll();

//then
Posts posts = postsList.get(0);
assertThat(posts.getTitle()).isEqualTo(title);
assertThat(posts.getContent()).isEqualTo(content);
}
}

쿼리 확인을 위해 쿼리 로그 열기

  • 이런 설정은 원래 자바 코드 작성 해서 설정 할수 있으니 스프링 부트 에선 application.properties 나 application.yml 으로 설정 가능

spring.jpa.show_sql=true

1
2
3
4
5
6
Hibernate: create table posts (id bigint generated by default as identity, author varchar(255), content TEXT not null, title varchar(500) not null, primary key (id))

Hibernate: insert into posts (id, author, content, title) values (default, ?, ?, ?)
Hibernate: select posts0_.id as id1_0_, posts0_.author as author2_0_, posts0_.content as content3_0_, posts0_.title as title4_0_ from posts posts0_
Hibernate: select posts0_.id as id1_0_, posts0_.author as author2_0_, posts0_.content as content3_0_, posts0_.title as title4_0_ from posts posts0_
Hibernate: delete from posts where id=?

H2 문법 -> MySQL로 변경

책: spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

최신 Spring boot 버전 사용시 옵션 아래로 변경 필요

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
spring.jpa.properties.hibernate.dialect.storage_engine=innodb
spring.datasource.hikari.jdbc-url=jdbc:h2:mem:testdb;MODE=MYSQL
spring.datasource.hikari.username=sa

1
2
Hibernate: drop table if exists posts
Hibernate: create table posts (id bigint not null auto_increment, author varchar(255), content TEXT not null, title varchar(500) not null, primary key (id)) engine=InnoDB

등록, 수정, 조회 API 만들기

3개의 클래스 필요

  1. Request 데이터를 받을 DTO
  2. API 요청을 받을 Controller
  3. 트랜잭션, 도메인 기능 간의 순서를 보장할 Service

비지니스 로직은 서비스가 아닌 도메인에 넣는다.
서비스 @Transactional 사용 하고 트랜잭션 스크립트 화 한다.
서비스 메소드는 트랜잭션과 도메인간의 순서만 보장하는 방식으로 한다.

등록 기능 만들기 (책에 오타)

오타 부분: Controller 에 @GetMapping 이 아니고 @PostMapping 이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@RequiredArgsConstructor
@RestController
public class PostsApiController {

private final PostsService postsService;

@PostMapping("/api/v1/posts")
public Long save(@RequestBody PostsSaveRequestDto requestDto)
{
return postsService.save(requestDto);
}
}

@RequiredArgsConstructor
@Service
public class PostsService {
private final PostsRepository postsRepository;

@Transactional
public Long save(PostsSaveRequestDto requestDto) {
return postsRepository.save(requestDto.toEntity()).getId();

}

}

@Getter
@NoArgsConstructor
public class PostsSaveRequestDto {
private String title;
private String content;
private String author;

@Builder
public PostsSaveRequestDto(String title, String content, String author) {
this.title = title;
this.content = content;
this.author = author;
}

public Posts toEntity() {
return Posts.builder()
.title(title)
.content(content)
.author(author)
.build();
}
}

테스트

  • JPA 까지 물려서 써야 함으로 mvctest 안쓰고 대신 SprintBootTest 와 TestRestTemplate을 사용 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class PostsApiControllerTest {

@Autowired
private TestRestTemplate restTemplate;

@Autowired
private PostsRepository postsRepository;

@LocalServerPort
private int port;

@After
public void cleanup() throws Exception
{
postsRepository.deleteAll();
}

@Test
public void 등록_테스트() throws Exception
{
//given
String url = "http://localhost:" + port + "/api/v1/posts";
String title = "test title";
String content = "test content";

PostsSaveRequestDto requestDto = PostsSaveRequestDto.builder()
.title(title)
.content(content)
.author("test author")
.build();

//when
ResponseEntity<Long> responseEntity = restTemplate.postForEntity(url, requestDto, Long.class);

//then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(responseEntity.getBody()).isGreaterThan(0L);

List<Posts> all = postsRepository.findAll();
assertThat(all.get(0).getTitle()).isEqualTo(title);
assertThat(all.get(0).getContent()).isEqualTo(content);
}
}

수정, 조회

Controller 에서 서비스 부르는 부분
서비스 에서 Repository 부르는 부분
결과나 반환 데이타를 위해 DTO 추가

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@RequiredArgsConstructor
@RestController
public class PostsApiController {

private final PostsService postsService;

@PostMapping("/api/v1/posts")
public Long save(@RequestBody PostsSaveRequestDto requestDto)
{
return postsService.save(requestDto);
}

@PutMapping("/api/v1/posts/{id}")
public Long update(@PathVariable Long id, @RequestBody PostsUpdateRequestDto requestDto) {
return postsService.update(id, requestDto);
}

@DeleteMapping("/api/v1/posts/{id}")
public Long delete(@PathVariable Long id) {
postsService.delete(id);
return id;
}

@GetMapping("/api/v1/posts/{id}")
public PostsResponseDto findById(@PathVariable Long id) {
return postsService.findById(id);
}

@GetMapping("/api/v1/posts/list")
public List<PostsListResponseDto> findAll() {
return postsService.findAllDesc();
}

}
  • @Transactional 이 메서드에 없으면 SQL을 호출 하지 않는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@RequiredArgsConstructor
@Service
public class PostsService {
private final PostsRepository postsRepository;

@Transactional
public Long save(PostsSaveRequestDto requestDto) {
return postsRepository.save(requestDto.toEntity()).getId();
}

@Transactional
public Long update(Long id, PostsUpdateRequestDto requestDto)
{
Posts post = postsRepository.findById(id)
.orElseThrow(()-> new IllegalArgumentException("해당 사용자가 없습니다. id=" + id));

post.update(requestDto.getTitle(), requestDto.getContent());

return id;
}
  • 업데이트 하는데 exchange 을 써야 해서 좀 복잡해진다.
  • BODY 에 업데이트 할 DATA을 넣어 보낸다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Test
public void 업데이트_테스트() throws Exception
{
//given
Posts post = postsRepository.save(Posts.builder()
.title("test title")
.content("test content")
.author("test author")
.build());
Long id = post.getId();
String updatedTitle = "updated Title";
String updatedContent = "updated Content";

PostsUpdateRequestDto requestDto = PostsUpdateRequestDto.builder()
.title(updatedTitle)
.content(updatedContent)
.build();

String url = "http://localhost:" + port + "/api/v1/posts/" + id;

HttpEntity<PostsUpdateRequestDto> requestEntity =
new HttpEntity<>(requestDto);

//when
ResponseEntity<Long> responseEntity =
restTemplate.exchange(url, HttpMethod.PUT, (HttpEntity<?>) requestEntity, Long.class);

//then
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(responseEntity.getBody()).isGreaterThan(0L);

List<Posts> all = postsRepository.findAll();
assertThat(all.get(0).getTitle()).isEqualTo(updatedTitle);
assertThat(all.get(0).getContent()).isEqualTo(updatedContent);
}

데이타베이스 H2 접속 해서 보기

  1. application.resources에 spring.h2.console.enabled-true
  2. http://localhost:8080/h2-console 로 접속 하고 JDBC URL: jdbc:h2:mem:testdb

등록된 글 확인

  1. SELECT * FROM POSTS ;
  2. insert into posts (author, content, title) values (‘author’, ‘content’, ‘title’);
  3. http://localhost:8080/api/v1/posts/1

406 에러가 발생, 디버깅 해보니 PostsResponseDto.java 에 @Getter 가 없었음

Getter 가 없는데 406 Not Acceptable 가 나오다니, 디버깅하기 어려움

JPA Audit 으로 생성 시간/ 수정 시간 자동화 하기

생성 및 수정 시간을 자동으로 넣어 주는 기능 으로 abstract class를 아래 처럼 구현

1
2
3
4
5
6
7
8
9
10
11
12
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseTimeEntity {

@CreatedDate
private LocalDateTime createdDate;

@LastModifiedDate
private LocalDateTime modifiedDate;

}

그리고 최 상위 Application 에 @EnableJpaAuditing 추가
@EnableJpaAuditing
@SpringBootApplication
public class Application {

그리고 적용 할 Entity에 상속
public class Posts extends BaseTimeEntity

테스트 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
public void BaseTimeEntity_테스트() {
//given
LocalDateTime now = LocalDateTime.of(2022, 5, 15, 0, 0, 0);
postsRepository.save(Posts.builder()
.title("title")
.content("content")
.author("author")
.build());
//when
List<Posts> postsList = postsRepository.findAll();

//then
Posts posts = postsList.get(0);

System.out.println(">>>>>>>>> createDate=" + posts.getCreatedDate() + ", modifiedDate=" + posts.getModifiedDate());

assertThat(posts.getCreatedDate()).isAfter(now);
assertThat(posts.getModifiedDate()).isAfter(now);
}

(오키) 자바의정석, 챕터 8,9,10 - 예외 처리~사용시 유용한 클래스

8-1 프로그램 오류, 예외 클래스의 계층 구조

  1. 컴파일 에러
  2. 런타임 에러
  3. 로직 에러

자바의 런타임 에러

  • 에러 (error) : 프로그램 코드에 의해서 수습될수 없는 심각한 오류
  • 예외 (exception) : 프로그램 코드에 의해서 수습 될수 있는 다소 미약한 오류

8-2 예외처리의 정의와 목적 (exception handling)

프로그램의 비정상 종료를 막기 위함
Object <–상속– Throwable (클래스 모든 오류의 조상)
<– Exception 의 자손들 RuntimeException, IOException 등등
<– Error 의 자손들 OutOfMemoryError

8-3 Exception 와 RuntimeException

Exception 클래스들 : 사용자의 실수와 같은 외적인 요인에 의해 발생 하는 예외
RuntimeException 클래스들: 프로그래머의 실수로 발생 하는 예외

8-4 Exception 처리, Try-catch 문

1
2
3
4
5
6
7
8
9
try {
//
} catch (ArithmeticException e1) {
//
} catch (Exception e2) { // 모든예외의 조상
//
} finally {

}

8-7 printStackTrace() 와 getMessage()

printStackTrace(): 예외 발생 당시의 호출스택(Call Stack)에 있었던 메서드의 정보와 예외 메시지를 화면에 출력 한다.

getMessage(): 발생한 예외클래스의 인스턴스에 저장된 메시지를 얻을 수 있다.

1
2
3
4
5
6
7
8
try {
System.out.println(0/0);
} catch (ArithmeticException ae) {
ae.printStackTrace();
System.out.println(ae.getMessage());
} catch (Exception e) {
...
}

8-8 멀티 catch 블럭 (JDK 1.7 부터)

1
2
3
4
5
6
7
8
9
10
11
12
catch (ExceptionA | ExceptionB e) { // 이런 식으로 멀티 구성 가능

// Exception 의 메서드 함수 호출시 instanceof 사용 하면 된다.

if( e instanceof ExceptionA ) {
ExceptionA e1 = (ExceptionA) e;
e1.methodA();
} else {
// ...
}

}

8-9 예외 발생 시키기

1
2
Exception e = new Exception("고의 발생");
throw e;

8-10 checked예외, unchcked 예외

  • checked예외 : 컴파일러가 예외 처리 여부를 체크 (예외 처리 필수)
  • unchecked예외 : 컴파일러가 예외 처리 여부를 체크 안함 (예외 처리 선택)
1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
throw new Exception();
}

// 컴파일시 에러 > java.lang.Exception; must be caught or declared to be thrown 이라고 나옴

public static void main(String[] args) {
throw new RuntimeException(); // 런타임 에러 발생, 컴파일 에러는 안나옴
}

ch8-11 메서드에 예외 선언하기

  • 예외를 처리하는 방법
  1. try-catch는 직접 처리
  2. 예외 선언은 에외 떠넘기기 (알리기)
  3. 은폐, 덥기 (그냥 넘어가고 진행)

이중에 예외 선언 방법 알아보자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void method() throws Exception1, Exception2, ... ExceptionN {
// 메서드의 내용
}

// method()에서 Exception과 그 자손 예외 발생 가능
void method() throws Exception { // 모든 예외의 최고 조상, 모든 예외가 발생 가능
// 메서드의 내용
}

static void startInstall() throws SpaceException, MemoryException {
if(!enoughSpace())
throw new SpaceException("공간 부족");
if(!enoughMemory())
throw new MemoryException("메모리 부족");
//...
}

예로 Java API 중에 wait 메서드 를 보자

1
2
3
4
5
6
7
8
9
10
11
public final void waits() throws InterruptedException { 
//...
}
/*
Throws :
1. IllegalMonitorStateException
2. InterruptedException

함수 보면 Interrupted Exception 만 예외 선언 중이다.
IllegalMonitorStateException은 RuntimeException의 자손임이다 (즉 예외처리가 선택적)
*/

ch8-14 finally 블럭

  • 예외 발생 여부와 관계 없이 수행되어야 하는 코드를 넣는다

ch8-15 사용자 정의 예외 만들기

  • 우리가 직접 정의 가능
  • 조상은 Exception 과 RuntimeException 중 선택
1
2
3
4
5
class MyException extends Exception {
MyException (String msg) { // 문자열을 매개변수로 받는 생성자
super(msg);// 조상인 Exception 클래스의 생성자를 호출 한다.
}
}

ch8-17 예외 되던지기 (exception re-throwing)

  • 예외를 처리한 후에 다시 예외를 발생 시키는 것
  • 호출한 메서드와 호출된 메서드 양쪽 모두에서 예외 처리 하는 것
  • catch 문 안에서 다시 throw e; 하면 된다.

ch8-18 연결된 예외 (chained exception)

  • 한 예외가 다른 예외를 발생 시킬수 있다
  • 예외 A가 예외 B를 발생 시키면, A는 B의 원인 예외 (cause exception)
1
2
3
4
5
6
7
8
9
10
11
12
Throwable initCause(Throwable cause) : 지정한 예외를 원인 예외로 등록
Throwable getCAuse() : 원인 예외를 반환

public class Throwable implements Serializable { // Exception과 Error 의 조상 클래스
//...
private Throwable cause = this; // 원인 예외를 저장 하기위한 iv
//...
public synchronized Throwable initCause(Throwable cause) {
this.cause = cause;
return this;
}
}

사용 예제 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
void install() throws InstallException {

try {
startInstall();
copyFiles();
} catch (SpaceException e) {
InstallException ie = new InstallException("설치 중 예외발생");
ie.initCause(e); // 원인을 새로 만든 예외에 넣고
throw ie; // 새로 만든 예외로 던짐
} catch (MemoryException me) {
//...
}
}

사용 이유

  1. 여러 예외를 하나로 묶어서 다루기 위해서
    (받는 쪽에서 너무 많은 Exception 종류 처리로 try catch 가 많은데 그걸 묶을수 있다.)
  2. checked 예외를 unchecked예외로 변경 하려 할때

ch9 java.lang 패키지와 유용한 클래스

ch9-1 Object 클래스

  • 모든 클래스의 최고 조상. 11개의 메서드 가지고 있음
  • notify(), wait()등은 쓰레드와 관련된 메서드 임
  • clone() : 복사
  • equals(Object obj) : 같은 객체 인지 확인
  • getClass() : 객체 자신의 클래스 정보를 담고 있는 Class 인스턴스를 반환

ch9-3 equals 의 오버라이딩

  • 참조 변수로 확인 함으로 Class 안의 iv 값 비교 하기 위해서는 오버라이딩 해야 한다.
1
2
3
4
5
6
7
8
9
10
class Person {
long id;

public boolean equals(Object obj) {
if(obj instanceof Person)
return id == ((Person) obj).id;
else
return false;
}
}

ch9-4 hashCode(), toString() 와 이들의 오버라이딩

  • equals()를 오버라이딩 하면, hashCode()도 통상 같이 오버라이딩 해 준다.
1
2
3
4
5
6
7
8
9
10
public class Object {
//...
public native int hashCode();
}

public int hashCode() {
// hash 함수는 가변 인자를 받을수 있다.
// 통상 iv을 쭈욱 넣어 준다.
return Objects.hash(kind, number);
}
  • System.identityHashCode(Object obj)는 Object 클래스의 hashCode()와 동일

  • toString(): 객체를 문자열로 변환

  • 통상 iv을 찍어 주는 것으로 오버라이딩 한다

1
2
3
public String toString() {
return getClass().getName()+"@"+Integer.toHexString(hashCode());
}

ch9-7~10 String 클래스, 문자열 비교, 빈 문자열

  • String 클래스 = 데이터(char[]) + 메서드(문자열 관련)
1
2
3
public final class String implements java.io.Serializable, Comparable {
private char[] value;
//...
  • 내용을 변경 할수 없는 불변(immutable) 클래스
1
2
3
String a = "a";
String b = "b";
a = a + b;
  • 덧셈 연산자(+)를 이용한 문자열 결합은 성능이 떨어짐

ch9-8 문자열의 비교

  • String str = “abc”; 와 String str = new String(“abc”); 의 비교
1
2
3
4
String str1 = "abc";  // "abc" 0x100, str1은 0x100 바라봄
String str2 = "abc"; // str2은 0x100 바라봄
String str3 = new String("abc"); // "abc" 0x200, str3은 0x200 바라봄
String str4 = new String("abc"); // "abc" 0x300, str4은 0x300 바라봄

str1==str2 하면 true 나오고 str3==str4 하면 false 나온다.
equals()을 사용하면 true 나온다.

ch9-9 문자열 리터럴

  • 문자열 리터럴은 프로그램 실행시 자동으로 생성된다. (constant pool/ 상수 저장소에 저장)
  • 같은 내용의 문자열 리터럴은 하나만 만들어 진다.

ch9-10 빈 문자열 (“”, empty string)

  • 내용이 없는 문자열. 크기가 0인 char형 배열을 저장하는 문자열
1
String str = ""; // str을 빈 문자열로 초기화
  • 크기가 0인 배열을 생성하는 것은 어느 타입이나 가능
1
2
char[] chArr = new char[0]; // 길이가 0인 char 배열
int[] iArr = {}; // 길이가 0 인 int 배열
  • 문자(char) 와 문자열(String)의 초기화

// 아래 처림 쓰지 말고
String s = null;
char c = ‘\u0000’;
String str1 = new String(“”);
String str2 = new String(“”);
String str3 = new String(“”);

// 아래 처럼 쓰기를 추천
String s = “”;
char c = ‘’;
String str1 = “”;
String str2 = “”;
String str3 = “”;

ch9-11 String 클래스의 생성자와 메서드 (1/5)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
String(String s); // 생성자
String s = new String("Hello");

String(char[] value); // 문자열로 생성
char[] c = {'H', 'e', 'e'};
String s = new String(c);

String(StringBuffer buf); // String buffer 활용
StringBuffer sb = new StringBuffer("Hello");
String s = new String(sb);

char charAt(int index); // index 위치에 문자 반환
s.charAt(1);

int compareTo(String str); // 문자열과 사전 순서로 비교, 0 같ㅇㅁ, 이전 음수, 이후 양수
int i1 = "aaa".compareTo("aaa"); // 0
int i2 = "aaa".compareTo("bbb"); // -1 // 왼쪽 작음
int i3 = "bbb".compareTo("aaa"); // 1 // 왼쪽 큼

String concat(String str);

boolean contain(CharSequence s); // 포함 인지 확인

boolean endsWith(String suffix);
boolean equals(Object obj);
boolean equalsIgnoreCase(String str);

int indexOf(int ch); // 못찾으면 -1
int indexOf(int ch, int pos);
int indexOf(String str);
int lastIndexOf(int ch);
int lastIndexOf(String str)

int length();

String[] split(String regex); // 정규식(Regular Expression)
String animals = "dog,cat,bear";
String[] arr = animals.split(",");

String[] split(String regex, int limit); // limit 숫자 만큼 나눈다
String animals = "dog,cat,bear";
String[] arr = animals.split(",", 2); // arr[0]="dog", arr[1]="cat,bear";

boolean startsWith(String prefix); // prefix로 시작 하는지 확인

String subString(int begin);
String subString(int begin, int end);

String toLowerCase();
String toUpperCase();

String trim();

static String valueOf(boolean b);
static String valueOf(char b);
static String valueOf(int b);
static String valueOf(long b);
static String valueOf(float b);
static String valueOf(double b);
static String valueOf(Object b); // toString()을 호출한 결과를 반환

boolean contain(CharSequence s); 매개 인자로 쓰이는 CharSequence

  1. interface CharSequence, 인터페이스 이다
  2. 인터페이스 장점: 서로 관계 없는 클래스들의 관계를 맺어준다.
  3. 인자로 여러 클래스를 넣어 줄수 있다, 아래 4번
  4. All Known implementation Classes: CharBuffer, Segment, String, StringBuffer, StringBuilder
  5. 인터페이스 없었다면 contain 함수를 여러개 (들어갈수 있는 매개 타입 별로) 만들었어야 했었다.

ch9-12 join()과 StringJoiner

  • join()은 여러 문자열 사이에 구분자를 넣어서 결합 한다.
1
2
3
String animals = "dog, cat, bear";
String[] arr = animals.split(",");
String str = String.join("-", arr);

ch9-13 문자열과 기본형 간의 변환

1
2
3
4
5
6
7
8
9
10
11
12
//기본형 -> 문자형
static String.valueOf(boolean b);
static String valueOf(char b);
static String valueOf(int b);
static String valueOf(long b);
static String valueOf(float b);
static String valueOf(double b);

//문자형 -> 기본형
boolean Boolean.parseBoolean(String s); // 요즘 Boolean.valueOf(String s)
char Char.parseChar(String s);
//.... 등등 등

요즘 parse 대신 valueOf 사용 한다.
면밀히 보면 parse은 기본형 반환, valueOf는 기본형의 클래스 반환 이다.
하지만 기본형으로 받아도 된다. auto boxing으로 자동 casting 되기 때문이다.

예제로 보면,

1
2
boolean b = Boolean.valueOf("true");
int i = Integer.valueOf("1000");

ch9-15 StringBuffer 클래스

  • String 처럼 문자형 배열(char[])을 내부적으로 가지고 있다.
1
2
3
4
public final class StringBuffer implements java.io.Serializable {
private char[] value;
//...
}
  • 그러나 String과 달리 내용을 변경 할수 있다. (mutable)
  • 배열은 길이 변경 불가, 부족하면 새로운 배열 생성
  • 저장할 문자열 길이 고려, 적절한 크기로 생성
1
2
3
4
5
6
7
public StringBuffer(int length) { // 이렇게 길이를 잡아 줄수 있다.
value = new char[length];
shared = false;
}

// 기본 생성 하면 길이를 16으로 잡는다.
// String 넣어서 생성 하면 문자열 길이 + 16으로 잡는다.
  • append(), 지정된 내용을 StringBuffer에 추가 후, StringBuffer 의 참조를 반환
1
2
3
4
5
Stringbuffer sb = StringBuffer("abc");
sb.append("123");
sb.append("ZZ");

sb.append("123").append("ZZ");

ch9-18 String Buffer의 비교

  • StringBuffer는 equals()가 오버라이딩 되어있지 않다.
1
2
3
4
5
StringBuffer sb1 = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");

// (sb1 == sb2) // false
// sb1.equals(sb2); // false

그럼 어떻게 비교? String으로 변환 해서 비교 해야 한다.

ch9-19 StringBuffer의 생성자와 메서드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
StringBuffer append(/* 각종 타입 인자 가능*/);

int capacity();
char charAt(int index);
StringBuffer delete(int start, int end);
StringBuffer deleteCharAt(int index);

StringBuffer insert(int pos, /*각종 타입 */ boolean b);

int length();
StringBuffer replace(int start, int end, String str);
StringBuffer reverse();

void setCharAt(int index, char ch);
void setLength(int newLength);
String toString();
String substring(int start);
String substring(int start, int end);

ch9-21 StringBuilder

  • StringBuffer의 동기화 버전. 멀티 쓰레드에 안전 (thread-safe)
  • 동기화에 성능저하가 있다.

ch9-22 Math 클래스

  • 수학관련 static 메서드 의 집합

예제: round() 로 원하는 소수점 아래 세번째 자리에서 반올림하기

  1. 원래 값에 100 곱하기
  2. Math.round()
  3. 다시 / 100 나눈다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
static double abs(double a); // 절대값 반환
// float, int, long 버전 존재

static double ceil(double a); // 올림 반환
static double floor(double a); // 버림

static double max(double a, double b); // 둘중 큰거 반환
// float, int, long 버전 존재

static double min(double a, double b);
static double randome();

static double rint (double a); // 짝수 반올림
static double round(double a); // 일반 반올림

ch9-25 래퍼(wrapper) 클래스

  • 8개의 기본형을 감싸는 클래스
  • 기본형의 객체 Wrapper 클래스
1
2
3
4
5
Integer.MAX_VALUE; 
Integer.MIN_VALUE;
Integer.SIZE;
Integer.BYTES;
Integer.TYPE;

ch9-27 Number 클래스

  • 모든 숫자 래퍼 클래스의 조상
1
2
3
4
5
6
7
8
9
10
public abstract class Number implements java.io.Serializable {
public abstract int intValue();
public abstract long longValue();
public abstract float floatValue();
public abstract double doubleValue();

..
..
// 래퍼 객체 -> 객체형으로 변경해주는 메서드를 가지고 있음
}

ch9-30 오토박싱 & 언박싱

오토박싱 10 -> new Integer(10)
언박싱 new Integer(10) -> 10

ch10-1 날짜와 시간

java.util.Date

  • 날짜와 시간을 다룰 목적으로 만들어진 클래스 (JDK 1.0)
  • Date의 메서드는 거의 deprecated 되었음

java.util.Calendar

  • Data 클래스를 개선한 클래스 (JDK1.1)

java.time 패키지

  • Data 와 Calendar 의 단점을 개선한 새로운 클래스들을 제공 (JDK 1.8 -> Java 8)
  • 날짜 + 시간, LocalDate, LocalTime, LocalDateTime

ch10-2 Calendar

  • 추상 클래스 이므로 getInstance()을 통해 구현된 객체를 얻어야 한다.
1
2
3
Calendar cal = new Calendar(); // 생성 안됨, Calendar는 추상화 클래스
Calendar cal = Calendar.getInstance(); // 환경 변수를 참고 해서 생성
Calendar cal = new GregorianCalendar(); // 특정 Calendar의 생성
  • int get(int 필드값) 으로 날짜와 시간 필드 가져오기
  • 주의: Month은 0 부터 시작
1
2
3
Calendar cal = Calendar.getInstance(); // 현재 날짜와 시간
int thisYear = cal.get(Calendar.YEAR); // 올해 가져오기
int lastDayOfMonth = cal.getActualMaximum(Calendar.DATE); // 이날의 마지막 날짜

셋팅 가능한 필드: YEAR, MONTH, WEEK_OF_YEAR, WEEK_OF_MONTH, DATE, DAY_OF_MONTH, DAY_OF_YEAR, DAY_OF_WEEK, DAY_OF_WEEK_IN_MONTH

날짜 관련 필드: HOUR, HOUR_OF_DAY, MINUTE, SECOND, MILLISECOND, ZONE_OFFSET, AM_PM

ch10-4~5 Calendar 클래스 예제

  • set()으로 날짜와 시간 지정 하기

  • 날짜 지정 하는 방법 및 예제

  • 시간 지정 하는 방법 및 예제

  • 두개 날짜의 차이 얻는 방법은 getTimeInMillis() 로 바꾸어서 빼면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
long difference = date1.getTimeInMillis() - date2.getTimeInMillis();

final int[] TIME_UNIT = {3600, 60, 1};
final String[] TIME_UNIT_NAME = {"시간 ", "분 ", "초 " };

String tmp="";

for(int i=0; i< TIME_UNIT.length;i++) {
tmp += difference/TIME_UNIT[i] + TIME_UNIT_NAME[i];
difference %= TIME_UNIT[i];
}

System.out.println(tmp);
  • clear(): Calendar 객체의 모든 필드의 초기화
  • clear(int field): 측정 필드의 초기화

ch10-6~7 Calendar 클래스 예제2

-add() 는 특정 필드의 값을 증가 혹은 감소 (다른 필드에 영향을 준다)
-roll() 는 add와 동일 단 다른 필드에 영향 X

1
2
3
4
5
6
Calendar date = Calendar.getInstance();
date.clear();
date.set(2020, 7, 31); // 2020년 8월 31일 설정

date.add(Calendar.DATE, 1); // 날짜에서 1을 더한다 // 9월 1일이 된다
data.add(Calendar.MONTH, -8); // 월에서 8 을 뺀다// 1월 1일이 된다
  • 달력 찍기 예제

ch10-8 Date와 Calendar 간의 변환

  • Date의 메서드는 대부분 deprecated되었지만 여전히 사용

ch10-9~12 형식화 클래스

  • java.text 패키지의 DecimalFormat(10진수 숫자), SimpleDateFormat (날짜 형식화)
  • 숫자와 날짜가 원하는 형식으로 쉽게 출력 (숫자 <-> 문자열)
1
2
3
double number = 1234567.89;
DecimalFormat df = new DecimalFormat("#.#E0");
String result = df.format(number); // result = "1.2E6"
  • 형식 문자열에서 숫자와 날짜를 뽑아내는 기능
1
2
3
DecimalFormat df = new DecimalFormat("#,###.##");
Number num = df.parse("1,234,567.89");
double d = num.doubleValue(); // 1234567.89
기호 의미 패턴 결과
0 10진수 (값이 없을때 0)
# 10진수
E 지수 기호

예제 10-6 보면 여러 패턴으로 쭈욱 찍는것이 나온다

ch10-13~16 SimpleDataFormat

  • 날짜와 시간을 다양한 형식으로 출력 할 수 있게 해준다.
1
2
3
4
5
6
7
8
Date today = new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
String result = df.format(today);

SimpleDataFormat df2 = new SimpleDataFormat("yyyy년 MM월 dd일");
Date d = df2.parse("2015년 11월 23일"); // 문자열을 Date 객체로

String result = df2.format(d); // 객체를 문자열로

필요할때 찾아서 보자~
형식 으로 쓸수 있는 여러 기호 들 있음