(오키) 자바의정석, 챕터 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); // 객체를 문자열로

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