예외 처리 (Handling Exceptions)
오류와 예외
- 프로그램이 동작 중에 비정상 종료가 발생하는 상황
- 심각도에 따라 오류와 예외로 분류할 수 있음
오류 (Errors)
- 시스템의 메모리가 부족하거나, 무한히 메소드 호출이 발생하는 등 프로그램 복구가 불가능한 상황
- 오류가 발생하는 원인을 찾아서 제거하는 방법으로 해결
예외 (Exceptions)
- 오류에 비해 심각도가 낮으며, 프로그램의 정상적인 흐름을 방해
ex) 파일명으로 파일을 읽으려 하는데, 파일을 찾을 수 없음 - 문제 상황을 해결하는 로직을 별도로 구성하여 해결 가능
예외 처리
- 예외가 발생했을 때, 이 상황을 감지하고 해결하기 위해 동작하는 코드
- try ~ catch 구문과 Exception 클래스와 그 자식 클래스를 활용
Throwable 클래스
- Throwable 클래스는 Exception과 Error 클래스에 의해 상속
- Exception
- Checked Exceptions: 예외 처리되지 않으면 컴파일이 되지 않는 예외 - Exception Class 상속
- Unchecked Exception: 예외 처리되지 않아도 컴파일이 되는 예외 - RuntimeException Class 상속
- Error: 프로그램이 복구 불가능한 상황
- Exception
Exception 클래스
Throwable 클래스의 주요 메소드
메소드 설명 public String getMessage()
발생한 예외에 대한 메세지를 반환 public String getLocalizedMessage()
오버라이드하여 추가 메세지 제공 (오버라이드하지 않으면 getMessage()와 동일) public Throwable getCause()
예외의 원인이 되는 Throwable 객체 반환 public void printStackTrace()
예외가 발생된 메소드가 호출될 때의 Method call stack을 출력
예외 처리 기법
try ~ catch 구문
1 | try { |
다중 예외 처리
여러 개의 catch 구문을 사용하면 다중 예외를 처리할 수 있음
if ~ else if 구문처럼, 순차적으로 검사하면서 적용 가능한 예외를 처리
다형성이 적용되어, 자식 예외를 처리 가능
한가지의 catch 문에 대해서 예외가 잡힘
상위 Exception을 제일 밑의 catch문에 배치
1
2
3
4
5
6
7
8
9
10
11try {
// 예외 발생 가능 코드 영역
} catch (AException e) {
// A예외 처리
} catch (BException e) {
// B예외 처리
} catch (CException e) {
// C예외 처리
} catch (Exception e) {
// 나머지 모든 예외 처리
}
try ~ catch ~ finally 구문
try 구문 실행 중에 어떤 일이 발생해도 반드시 실행되어야 하는 구문은 finally 블록에 작성
try 구문 내에 return문이 있는 경우에도 finally 블록은 실행됨
try 구문 내에서 접근한 System 자원을 안전하게 복구하기 위해 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15FileInputStream file = null;
try {
file = new FileInputStream(fileName);
file.read();
} catch (IOException e) {
System.out.println("파일처리실패");
} finally {
if (file != null) {
try {
file.close();
} catch (IOException e) {
System.out.println("파일인풋스트림 종료 실패");
}
}
}
try ~ with ~ resources 구문
Java1.7에서 추가된 기능
AutoClosable 인터페이스를 구현하는 리소스를 자동으로 close 처리
1
2
3
4
5try (FileInputStream file = new FileInputStream(fileName)) {
file.read();
} catch (IOException e) {
System.out.println("파일처리실패");
}
예외 처리 위임
throws
호출한 메소드로 예외 처리를 전달하는 방식
Checked Exception의 경우 throws로 위임 가능하나, 최종적으로 try ~ catch를 만나야 함
1
2
3
4
5
6
7
8
9
10
11
12
13class CheckedException {
void methodA() throws ClassNotFoundException {
Class.forname("A Class");
}
void methodB() {
try {
methodA();
} catch (ClassNotFoundException e) {
...
}
}
}Unchecked Exception의 경우 throws로 위임하지 않아도 자동으로 전달
1
2
3
4
5
6
7
8
9
10
11
12
13class UncheckedException {
void methodA() {
int x = 10;
x = 10/0;
}
void methodB() {
try {
methodA();
} catch (ArithmeticException e) {
...
}
}
}메소드를 오버라이드한 경우, 조상 클래스의 메소드보다 조상 예외는 던질 수 없음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Foo {
void methodA() throws IOException {}
}
class BarOne extends Foo{
void methodA() throws IOException {} // possible
}
class BarTwo extends Foo{
void methodA() throws FileNotFoundException {} // possible
}
class BarThree extends Foo{
void methodA() throws Exception {} // *NOT* possible
}
throw
예외를 발생시키는 키워드
new 키워드로 새 Exception 객체를 생성하여 예외 내용을 작성
1
2
3
4
5
6
7void exceptMethod() throws Exception {
...
if (Err) {
throw new Exception("Some Error"); // 예외 발생 및 Message 전달
}
...
}
사용자 정의 예외 (Custom Exceptions)
Exception 또는 RuntimeException 클래스를 상속하여 작성
- Exception을 상속한경우 Checked Exception이 되어 반드시 예외를 처리해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class MyException extends RuntimeException {
enum ErrorCode {
ERROR_A, ERROR_B;
}
private ErrorCode errorCode;
public MyException(ErrorCode errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public String getLocalizedMessage() {
String message = getMessage();
...
return localizedMessage;
}
}
클래스 다이어그램
Class Diagram
- 클래스의 구성요소 및 클래스 간의 관계를 묘사하는 다이어그램
- 시간에 따라 변하지 않는 정적인 시스템 구조를 표현
클래스 다이어그램의 목적
- 문제 해결을 위한 도메인 구조를 표현
Unified Modeling Language (UML)
- 표준화된 모델링 표기 체계
- 클래스를 구현하기 전에 설계하는 단계에서 사용
- 클래스 이름, 파라미터, 리턴 타입 등
IDEA에서 UML 작성하기 (Plant UML)
Plant UML 플러그인 설치
Graphviz 설치 https://graphviz.org/download/
Plant UML의 문법
클래스, 추상클래스, 인터페이스
1
2
3
4
5
6
7
8
9
10
11
12interface IFoo {
void method();
}
abstract class Bar
class Foo {
int [] array;
int add(int x, int y);
setArray(int [] array);
void method();
}가시성 (Visibility)
- public : +
- protected : #
- default : ~
- private : -
클래스 간의 관계
- Extension
<|-
- Aggregation
o--
- Composition
*--
- Extension
타이틀
1
title 클래스 다이어그램 제목
노트
1
2
3note left of Foo
노트 <b>내용</b> 작성
end note
IDEA에서 UML 자동 생성
Wrapper 클래스 (Wrapper Class)
Wrapper 클래스란
- 기본형 타입을 객체로 사용하기 위한 클래스
Wrapper 클래스의 종류
기본형 | Wrapper 클래스 |
---|---|
byte | Byte |
char | Character |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
Wrapper 객체 생성
생성자를 이용한 객체 생성
1
Integer integer = new Integer(10);
valueOf를 이용한 객체 생성
1
Integer integer = Integer.valueOf(10);
Autoboxing & Unboxing
오토박싱 (Autoboxing)
- Java1.5부터 추가된 기능으로, 객체로 다루어야 할 때 자동으로 Wrapper 클래스로 변경하는 기능
언박싱 (Unboxing)
- Wrapper 객체를 기본형으로 자동으로 변경하는 기능
1
2
3int i = 10;
Integer wrapped = i;
int b = 20 + wrapped;
Wrapper 타입의 값 비교
Wrapper 타입은 객체이므로, ==를 이용하여 값을 비교할 수 없다.
1
2
3
4
5
6Integer intOne = new Integer(100);
Integer intTwo = new Integer(100);
System.out.println(intOne == intTwo); // false
System.out.println(intOne.equals(intTwo)) // true
System.out.println(intOne == 100) // true (Unboxing)
문자열의 기본 자료형 변환
Parsing 정적 메소드를 이용한 변환
1
2int x = Integer.parseInt("100");
long y = Long.parseLong("512345124");Wrapper 객체로의 변환
1
2Integer intObj = Integer.valueOf("1000");
Integer intObjTwo = new Integer("1234");
제네릭 (Generic)
제네릭이란
- 대상 객체의 타입을 입력받아 사용할 수 있는 형식
- 미리 사용할 수 있는 타입을 명시하여 컴파일 타임에 체크할 수 있음
- 입력을
Object
로 한 경우 런타임을 체크하기 위한instanceof
를 많이 사용해야 함 - 제네릭을 사용할 경우 간결하게 코드를 작성할 수 있다.
- 입력을
제네릭 클래스
제네릭 타입
클래스와 인터페이스에 제네릭이 적용된 타입
클래스를 선언할 때에는 타입이 알려지지 않으며, 타입 파라미터를 사용
1
2
3
4
5
6
7
8
9
10
11public class GenericClass<T> { // T: 타입 파라미터
...
}
public interface GenericInterface<T> { // T: 타입 파라미터
...
}
public class HashMap<K,V> { // K, V: 타입 파라미터
...
}제네릭 타입은 실제로 사용될 때 타입 파라미터에 자료형을 입력받는다.
1
2GenericClass<String> generic = new GenericClass<String>();
GenericClass<String> generic2 = new GenericClass<>();
타입 파라미터 주의점
static
멤버 변수는 타입 파라미터를 사용할 수 없다.정적 메소드에도 타입 파라미터를 사용할 수 없다.
이유 : 객체를 생성했을 때 타입 T가 결정되기 때문이다.
1
2
3
4public class Generic<T> {
static T classVar; // not possible
static void method(T localVar) {} // not possible
}new
키워드를 사용하여 객체 생성을 할 수 없다. - 안전성1
2
3public class Generic<T> {
T var = new T(); // not possible
}instanceof
의 피연산자로 사용할 수 없다. - 안전성1
2
3
4
5
6
7
8public class Generic<T> {
{
Object obj = new Object();
if(obj instanceof T) { // not possible
...
}
}
}
제네릭 타입의 상속
부모 클래스 또는 인터페이스에 선언한 타입 파라미터는 반드시 자식에서도 선언
자식 클래스에서 추가적인 타입 파라미터 선언할 수 있다.
1
2
3
4
5
6
7
8
9
10
11class Foo<T> {
...
}
interface IBar<D> {
...
}
class FooBar<C, T, D> extends Foo<T> implements IBar<D> {
...
}
파라미터 타입의 제한
extends
를 이용하여 파라미터 타입을 제한할 수 있다.- 인터페이스의 경우에도
extends
키워드를 사용한다. - 클래스와 인터페이스를 동시에 제약하려면
&
로 연결한다.
- 인터페이스의 경우에도
제한한 자료형의 자식 클래스는 모두 사용할 수 있다.
1
2
3
4
5
6
7class Generic<T extends Number> {
...
}
class Generic<T extends Number & Cloneable> {
...
}
제네릭 메소드
메소드에 선언된 제네릭
메소드의 리턴 타입 앞에 타입 파라미터 변수를 선언하여 사용
1
2
3
4
5class GenericMethod {
public <T> T method(T x) {
return x;
}
}메소드에 선언된 제네릭은 정적 메소드에도 사용 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14class GenericMethod<T> {
public static void method(T t) { // Error
...
}
public static <P> P method (P p) { // Possible
...
}
public static <P, V> V methodA(P p){
// Error : V Type Not Definition
...
}
}와일드카드
<?>
=><? extends Object>
와 동일<? extends T>
=> 와일드카드의 상한을 제한, T 포함 상속 클래스<? super T>
=> 와일드카드의 하한을 제한, T 포함 상위 클래스
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Foo {
...
}
class Bar extends Foo {
...
}
class Generic<T> {
...
}
class WildCard {
public void method1(Generic<?> x) {}
public void method2(Generic<? extends Foo> x) {}
public void method3(Generic<? super Bar> x) {}
}
열거형 (Enumeration)
열거형이란
- 몇가지 한정된 상수로 표현될 수 있는 자료형을 구현
enum
키워드를 이용하며, 내부적으로는java.lang.Enum
클래스를 상속
열거형의 특징
- 접근 제한자는
public
과default
만 사용 가능 - 다른 클래스를 상속받을 수 없으나, 인터페이스 구현은 가능
- 열거형 타입에는 열거형 상수와 null 값을 할당할 수 있음
열거형의 구현
일반적인 열거형의 구현
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
30enum Job {
STUDENT, MARKETING, DEVELOPER, CHIEF_EXECUTIONAL_OFFICER;
}
public class Main {
public static void main(String[] args) {
Job job = Job.STUDENT;
if (job == Job.CHIEF_EXECUTIONAL_OFFICER) {
System.out.println("사장님, 안녕하세요?");
}
switch (job) {
case STUDENT: // case에서는 'Job.' 을 쓰지 않는다.
System.out.println("You will be a great one.");
break;
case MARKETING:
System.out.println("You make things sold.");
break;
case DEVELOPER:
System.out.println("You usually don't go home.");
break;
case CHIEF_EXECUTIONAL_OFFICER:
System.out.println("You choose your company's fate.");
break;
default:
System.out.println("I don't recognize what you do.");
}
}
}클래스 내부에서 열거형 구현
1
2
3
4
5
6
7
8
9
10
11public class Foo {
enum MyEnum {
ONE, TWO;
}
}
public class Main {
public static void main(String[] args) {
System.out.println(Foo.MyEnum.ONE);
}
}열거형에 메소드 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24enum Symbol {
ONE, TWO, THREE, FOUR;
public Symbol nextSymbol() {
if(this.equals(ONE)) {
return TWO;
} else if(this.equals(TWO)) {
return THREE;
} else if(this.equals(THREE)) {
return FOUR;
} else {
return ONE;
}
}
}
public class Main {
public static void main(String[] args) {
Symbol sym = Symbol.ONE; // ONE
Symbol nextSym = sym.nextSymbol(); // TWO
nextSym = nextSym.nextSymbol(); // THREE
}
}열거형 생성자를 이용한 enum 상수 초기화
- 열거형의 생성자는 항상
private
이며 생략 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23enum Family {
FATHER("아버지"), MOTHER("어머니"), SON("아들"), DAUGHTER("딸"); // 생성자 호출
private String koreanWord;
Family(String koreanWord) { // private 생성자
this.koreanWord = koreanWord;
}
public String getKoreanWord() {
return koreanWord;
}
public void setKoreanWord(String koreanWord) {
this.koreanWord = koreanWord;
}
}
public class Main {
public static void main(String[] args) {
Family fam = Family.SON;
System.out.println(fam.koreanWord) // 아들
}
}- 열거형의 생성자는 항상
인터페이스 (Interface)
인터페이스란
- 클래스를 사용하는 방식, 접점만을 선언하는 클래스와 유사한 틀
- 아무런 구현이 되어 있지 않으며, 모든 메소드가 추상 메소드
인터페이스의 특징
class
가 아닌interface
키워드로 선언public
또는default
접근 제어자만 사용 가능
멤버 변수는 항상
public static final
이며, 생략 가능멤버 메소드는 항상
public abstract
이며, 생략 가능클래스와 달리 인터페이스는 여러 개 상속받을 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public interface IFoo {
public static final int MEMBER_VAR = 10;
int MEMBER_VAR2 = 20; // public static final
public abstract void methodA(int param);
void methodB(int param); // public abstract
}
public class Foo implements IFoo {
void methodA(int param) {
System.out.println(param);
}
void methodB(int param) {
System.out.println(param);
}
}
인터페이스간의 상속
인터페이스 간의 ‘IS-A’ 관계
인터페이스가 인터페이스를 상속할 경우
extends
로 상속클래스-클래스와 달리 다중 상속 가능
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
28interface Walkable {
void walk();
}
interface Runnable {
void run();
}
public interface Jumpable extends Walkable, Runnable {
void jump();
}
public class Jumper implements Jumpable {
void walk() {
System.out.println("walk");
}
void run() {
System.out.println("run");
}
void jump() {
System.out.println("jump");
}
}
JDK 1.8
기본 메소드 (Default method): 자식 클래스에서 구현할 필요가 없는 메소드
- 인터페이스에
default
메소드를 구현할 수 있다. default
메소드는 항상public
이다.- 인터페이스의 철학과 맞지 않으나, 인터페이스가 개선되었을 때 손쉽게 기능 추가를 위해 만들어짐
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
35interface IFoo {
void abstractMethod();
default void defaultMethod() { // 구현 클래스에서 구현할 필요가 없다.
System.out.println("Default method");
}
}
class SuperFoo {
void defaultMethod() {
System.out.println("Super method");
}
}
class FooOne implements IFoo {
void abstractMethod() {
return;
}
}
class FooTwo extends SuperFoo implements IFoo {
void abstractMethod() {
return;
}
}
public class Main {
public static void main(String[] args) {
FooOne fooOne = new FooOne();
fooOne.defaultMethod(); // Default method
FooTwo fooTwo = new FooTwo();
fooTwo.defaultMethod(); // Super method
}
}- 인터페이스에
static 메소드: 클래스 메소드와 동일하게 사용 가능
- 인터페이스 이름으로 호출 가능
- 클래스 구현체의 이름으로도 호출 가능
1
2
3
4
5
6
7
8
9
10
11interface IFoo {
static void staticMethod() {
System.out.println("static method");
}
}
public class Main {
public static void main(String[] args) {
IFoo.staticMethod(); // static method
}
}
다형성 (Polymorphism)
다형성의 다양한 특징
부모 클래스 타입으로 자식 클래스 객체를 참조하는 특징
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class Foo {
public void methodA() {
return;
}
}
public class Bar extends Foo {
public void methodB() {
return;
}
}
public class Main {
public static void main(String args[]) {
Bar bar = new Bar();
Foo foo = (Foo)bar;
}
}부모 클래스로 자식 클래스를 참조한 경우, 자식 클래스의 메소드는 사용할 수 없다.
1
2
3
4
5
6
7
8
9public class Main {
public static void main(String args[]) {
Bar bar = new Bar();
Foo foo = (Foo)bar;
foo.methodA(); // works
// foo.methodB(); // error
}
}자식 클래스로 부모 클래스를 참조하려 하면 java.lan.ClassCastException 오류 발생
1
2
3
4
5
6
7
8
9
10
11public class Main {
public static void main(String args[]) {
Foo foo = new Foo();
Bar bar;
// bar = (Bar)foo; // error
if (foo instanceof Bar) { // returns false
bar = (Bar)foo;
}
}
}멤버 변수 재정의는 선언된 객체의 타입을 따른다.
메소드 오버라이딩은 메모리상의 객체의 타입을 따른다. (가상 메소드 호출; Virtual method call)
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
30public class Foo {
public String x = "Super";
public void methodA() {
System.out.println("Super");
}
}
public class Bar extends Foo {
public String x = "Sub";
public void methodA() {
System.out.println("Sub");
return;
}
}
public class Main {
public static void main(String args[]) {
Bar bar = new Bar();
Foo foo = (Foo)bar;
System.out.println(bar.x); // Sub
bar.methodA(); // Sub
System.out.println(foo.x); // Super
foo.methodA(); // Sub
}
}공변 반환 타입 (Covariant return type)
1
2
3
4
5
6
7
8
9
10
11class Foo {
public Foo getInstance() {
return this;
}
}
class Bar extends Foo {
public Bar getInstance() { // Foo 대신 Bar로 리턴 가능
return this;
}
}
제어자 (Modifier)
제어자란
- 클래스, 변수, 메소드에 부가 기능을 부여하는 키워드
- 접근 제어자 (Access modifiers)
- 접근할 수 있는 범위를 정하는 키워드
public
,protected
,(default = package)
,private
- 그 외 제어자 (Other modifiers)
- 특별한 기능을 부여하는 제어자
static
,final
,abstract
,synchronized
제어자의 기능
접근 제어자
접근 가능한 범위를 정해, 데이터 은닉/보호 (Encapsulation) 기능을 추가한다.
접근 제어자별 접근 범위
제어자 같은 클래스 같은 패키지 다른 패키지에 속한 자식 클래스 전체 public
O O O O protected
O O O default
O O private
O private
또는protected
변수에 접근하기 위해 getter와 setter 사용1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class public Foo {
private int x = 0;
private int y = 1;
public void setX(int x) { // setter
this.x = x;
}
public void setY(int y) { // setter
if (y >= 0) {
this.y = y;
}
}
public int getX() { // getter
return x;
}
public int getY() { // getter
return y;
}
}
그 외의 제어자
final
더 이상 바뀔 수 없음을 의미
클래스, 메소드, 변수에 사용할 수 있음
클래스: 더 이상 상속이 불가능해진다.
메소드: 자식 클래스에서 오버라이드할 수 없다.
변수: 변수의 값이 초기화 이후에 변하지 않는다.
- 생성자에서 초기화가 이루어지는 것을
blank final
변수라 한다.
1
2
3
4
5
6
7
8
9public class Foo {
final int x = 0; // final variable
final int y; // blank finial variable
public Foo(int y) {
this.y = y; // blank final variable initialization
}
}- 생성자에서 초기화가 이루어지는 것을
static
- 클래스 변수, 클래스 메소드 등을 선언하기 위해 사용
abstract
- 추상 클래스에서 사용
synchronized
- 동시성 프로그래밍에 사용
싱글톤 패턴 (Singletone)
객체가 단 하나만 존재할 수 있는 클래스
private
생성자를 이용한다.1
2
3
4
5
6
7
8class SingletonClass {
private static SingletoneClass instance = new SingletonClass();
private SingletonClass() {}
public static SingletonClass getInstance() {
return instance;
}
}