1. 예외 처리
1
2
3
4
5
6
7
8
9
|
class Test
{
public static void main(String[] args0
{
int result = 0;
result = 10/0;
System.out.println("결과 값은 " + result);
}
}
|
cs |
어떠한 수도 0으로 나눌 수 없기 때문에 result=10/0에 의해 에러가 발생하게 된다.
이렇게 문법적으로 잘못된 부분이 없는데도 코드 수행에 오류가 발생하는 경우를 '예외'가 발생한다고 하고 이를 처리하기 위한 방법들이 있다.
2. try~catch 구문으로 예외 처리하기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class Test
{
public static void main(String[] args0
{
int result = 0;
try
{
result = 10/0;
}
catch(ArithmeticException e)
{
System.out.println("0으로 나누면 안돼요.");
}
System.out.println("프로그램 계속");
}
}
|
cs |
result=10/0에서 내부적으로 'ArithmeticException의 객체' 가 생성되고 이 객체는 catch(ArithmeticException e)에서 예외의 값을 e라는 변수로 catch하게 된다. 이렇게 되면 에러 메시지가 뜨지않고 코드가 작동되어 출력문을 실행한 후 정상적으로 프로그램을 이어나가게 된다.
(1) try~catch 구문 예문1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Test
{
public static void main(String[] args)
{
int result = 0;
try
{
result = 10/0;
System.out.println("Hello.");
}
catch(ArithmeticException e)
{
System.out.println("0으로 나누면 안돼요.");
}
System.out.println("프로그램 계속");
}
}
|
cs |
Hello라는 출력문이 추가되었다. 하지만 result=10/0에서 예외가 발생하게 되므로 Hello는 출력되지 않는다.
(2) try~catch 구문 예문2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Test
{
public static void main(String[] args)
{
int result = 0;
try
{
result = 10/2;
System.out.println("Hello.");
}
catch(ArithmeticException e)
{
System.out.println("0으로 나누면 안돼요.");
}
System.out.println("프로그램 계속");
}
}
|
cs |
result=10/0에서 result=10/2로 내용을 바꿔주었다. 때문에 예외가 발생하지 않기 때문에 Hello가 출력되고 예외 상황에 의한 객체 생성되지 않기 때문에 catch에서 받을 내용이 없기 때문에 '0으로 나누면 안돼요'는 출력되지 않고 '프로그램 계속' 이 출력된다.
(3) try~catch 구문 예문3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Test
{
public static void main(String[] args)
{
int result = div(10, 0);
System.out.println(result);
}
public static int div(int num1, int num2)
{
int result = 0;
try{
result = num1/num2;
} catch(ArithmeticException e) {
System.out.println("0으로 나누면 안돼요.");
System.out.println(e.getMessage());
}
return result;
}
}
|
cs |
num1은 10, num2는 0의 값을 갖게되고 result=10/0가 된다. 이 때 예외가 발생하게 되고 catch(Arithmetic Exception e)에서 예외처리에 대한 객체를 받게 된다.
- 출력결과
0으로 나누면 안돼요
/ by zero
0
3. throws를 이용하여 예외 처리하기
(1) throws를 이용 예문1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Test
{
public static void main(String[] args)
{
try{
int result = div(10, 0);
System.out.println(result);
} catch(Arithmetic Exception e) {
System.out.println("0으로 나누면 안돼요.");
}
}
public static int div(int num1, num2) throws ArithmeticException
{
int result = 0;
retult = num1/num2;
return result;
}
}
|
cs |
throws는 예외 처리를 다른 데로 넘기는 방식이다.
result=num1/num2에서 10을 0으로 나누게 되므로 예외가 발생하고 객체가 생성된다. 그리고 이 객체를 처리할 catch가 div 메소드에 없고 throws ArithmeticException 선언에 의해 다시 던져짐을 명시했기 때문에 메인메소드의 catch에 의해 처리가 된다.
(2) throws를 이용 예문2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Test
{
public static void main(String[] args) throws AritmeticException
{
int result = div(10, 0);
System.out.println(result);
}
public static int div(int num1, num2) throws AritmeticException
{
int result = 0;
retult = num1/num2;
return result;
}
}
|
cs |
이렇게 메인 메소드와 div메소드 모두 다 throws 을 이용하여 ArimeticException 을 던져짐을 명시하고 있다. 이렇게 되면 누구도 객체를 받지 않게 되므로 메인 메소드가 받기 전에 받아서 처리할 수 있도록 한다.
4. 예외처리 설명
(1) if문은 예외처리 이외의 용도로도 사용되기 때문에 프로그램 코드상에서 예외처리 부분을 구분하기가 쉽지 않다.
(2) try는 예외상황이 발생할만한 영역을 감싸는 용도로 사용이 되고 catch는 발생한 예외의 처리를 위한 코드를 묶어두기 위한 용도로 사용된다.
1
2
3
4
5
6
7
8
|
try
{
// try 영역
}
catch(AAA e)
{
// catch 영역
}
|
cs |
try와 catch는 각각 별도의 문장처럼 느껴질 수 있지만 이 둘은 하나의 문장이다. 때문에 try와 catch 사이에 다른 문장이 삽입될 수 없다. try 영역에서 발생한 예외 상황은 이어서 등장하는 catcg 영역에서 처리한다. 그런데 catch 영역에서 모든 예외 상황을 처리하는 것은 아니다. 위 그림의 catch 영역을 보면 매개변수 선언(AAA e)이 있는데 이 부분에 명시되어 있는 유형의 예외상황만 처리가 가능하다. 때문에 try 영역에서 발생한 AAA에 해당하는 예외상황은 이어서 등장하는 catch 영역에서 처리된다.
(3) try 영역으로 들어가면 일단 무조건 실행되는 영역이 있다. 이 영역은 예외상황의 발생여부와 상관없이 항상 실행되는데 이는 'finally 영역' 이라고 한다.
(4) 예외상황을 알리는데 필요한 예외 클래스의 정의 : Exception 클래스를 상속한다.
(5) Exception 클래스는 모든 예외 클래스가 상속한다는 Throwable 클래스의 하위 클래스이다. 따라서 이를 상속함으로써 예외 클래스가 되어 try~catch 예외처리 메커니즘에 활용이 가능한 클래스가 된다.
(6) throw는 예외 상황이 발생되었음을 자바 가상 머신에게 알리는 키워드이다. 따라서 이 문장이 실행되면서 자바의 예외처리 메커니즘이 동작하게 된다.
(7) Exception 클래스의 생성자 호출을 통해서 전달된 문자열이 getMessage의 호출을 통해서 반환된다. 예외 상황이 메소드 내에서 처리되지 않으면, 메소드를 호출한 영역으로 예외의 처리가 넘어감(던져짐)을 반드시 명시해야 한다.
(8) Throwable을 상속하는 예외 클래스는 Exteption, Error 2가지이다.
그런데 Error는 그 이름이 의미하듯이 단순히 예외라고 하기에는 심각한 오류의 상황을 표현하기 위해 정의된 클래스이다. 따라서 이 클래스를 상속하여 정의된 클래스는(이는 프로그래머가 정의하는 클래스가 아니다) 프로그램의 실행을 멈춰야 할 정도의 매우 심각한 오류 상황을 표현하는 데에 사용된다.
(9) Error를 상속하는 대표적인 클래스의 이름은 'VirtualMachieError'이다. API문서에서는 이 클래스에 대해서 다음과 같이 설명한다.
- 자바 가상머신에 문제가 생겨서 더 이상 제대로 동작할 수 ㅇ벗는 상황을 아리기 위해서 정의된 클래스이다.
-Error를 상속하는 클래스의 오류상황이 발생하면, 그냥 프로그램이 종료되도록 놔두는 것이 상책이다. (프로그램이 종료된 뒤 소스코드를 수정하는 등의 방식으로 원인을 해결해야 한다.)
(10) Error를 상속하는 대표적인 클래스가 VirtualMachineError이다. 그리고 VirtualMachineError를 상속하는 클래스 중에서 OutofMemoryError라는 클래스가 있는데, 이는 메모리 공간이 부족한 상황을 표현하는 예외 클래스이다. 따라서 이러한 오류가 발생하면 메모가 효율적으로 사용되도록 소스코드 자체를 변경해야 한다. 이렇듯 Error와 관련있는 오류상황은 소스코드의 변경을 통해서 해결해야 하는 경우가 대부분이다.
(11) 메소드를 볼 때에도 throws 절을 참조한다. Exception을 상속하는 클래스의 예외상황이 임의의 메소드 내에서 발생 가능하다면, 해당 메소드는 반드시 다음 두 가지 중 한 가지 방법을 선택해서 정의되어야 한다.
- try~catch 문을 이용해서 메소드 내에서 예외를 처리하도록 정의
- throws를 이용해서 메소드를 호출한 영역으로 예외가 전달되도록 정의
1
2
3
4
5
6
|
public void simpleMethod(int n)
{
MyClass my = new MyClass();
my.clone();
...
}
|
cs |
Object 클래스의 clone 이라는 메소드를 호출해서 사용하고, clone 메소드는 상황에 따라서 CloneNotSupportendException 이라는 예외를 전달하는 메소드이다. 위와 같이 메소드를 정의하면 컴파일 에러가 발생하는데, 어떻게 수정을 해야 할까?
- try~catch문 이용
1
2
3
4
5
6
7
8
9
10
|
public void simpleMethod(int n)
{
MyClass my = new MyClass();
try
{
my.clone();
}
catch(CloneNotSupportedException e) { ... }
...
}
|
cs |
- throws를 이용
1
2
3
4
5
6
|
public void simpleMethod(int n) throws CloneNotSupportedException
{
MyClass my = new MyClass();
my.clone();
...
}
|
cs |
(12) 처리하지 않아도 되는 RuntimeException의 하위 클래스
Exception의 하위 클래스 중에는 RuntimeException이라는 클래스가 존재한다. 그런데 이 클래스는 그 성격이 Error 클래스와 비슷하다.(이는 Exception을 상속하는 다른 예외 클래스들과의 차이점이다.) RuntimeException을 상속하는 예외 클래스도 Error를 상속하는 예외 클래스와 마찬가지로 try~catch문, 또는 throws절을 이용한 예외처리를 필요로 하지 않는다. 하지만 다음과 같이 Error의 하위 클래스들과 구분되는 특징이 있다.
- RuntimeException을 상속하는 예외 클래스는 Error를 상속하는 예외 클래스처럼 치명적인 상황을 표현하지 않는다.
- 따라서 예외발생 이후에도 프로그램의 실행을 이어가기 위해서 try~catch문으로 해당 예외를 처리하기도 한다.
(13) RuntimeException의 하위 클래스
- ArrayIndexOutOfBoundsException
- ClassCastException
- NegativeArraySizeException
- NullPointerException
예외의 성격이 보여주듯이 특별한 경우가 아니면, 이들에 대해서는 try~catch문을 이용해서 예외처리를 하지 않는다.
(14) 가상머신의 예외처리 순서
① getMessage 메소드를 호출한다.
② 예외상황이 발생해서 전달되는 과정을 출력해준다.
③ 프로그램을 종료한다.
5. 예외처리 실습문제
(1) 제수(나누는 수)가 0이 되었을 때 예외 처리하는 소스를 만드시오.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class Test
{
public static void main(String[] args)
{
int num1=10, num2=0;
try
{
System.out.println(num1/num2);
}
catch(ArithmeticException e)
{
System.out.println("0으로 나누면 안된다구요.");
System.out.println(e.getMessage());
}
}
}
|
cs |
(2) 예외 상황을 알리는 클래스
- 배열의 접근에 잘못된 인덱스 값을 사용하는 예외상황
- 허용할 수 없는 형변환 연산을 진행하는 예외상황
- 배열선언 과정에서 배열의 크기를 음수로 지정하는 예외상황
- 참조변수가 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
30
31
32
33
34
35
36
37
38
39
40
41
|
class Test
{
public static void main(String[] args)
{
try
{
int[] arr = new int[3];
arr[-1] = 20;
}
catch(ArrayIndexOutOfBoundsException e)
{
System.out.println(e.getMessage());
}
try
{
Object obj = new int[10];
String str = (String)obj;
}
catch(classCastException e)
{
System.out.println(e.getMessage());
}
try
{
int[] arr = new arr[-10];
}
catch(NegativeArraySizeException e)
{
System.out.println(e.getMessage());
}
try
{
String str = null;
int len = str.length();
}
catch(NullpointerException e)
{
System.out.println(e.getMessage());
}
}
}
|
cs |
(3) if문 소스코드를 try~catch문으로 변경하기
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
|
import java.util.Scanner;
class ExceptionHandleUseIf
{
public static void main(String[] args)
{
Scanner keyboard=new Scanner(System.in);
int[] arr=new int[100];
for(int i=0; i<3; i++)
{
System.out.print("피제수 입력: ");
int num1=keyboard.nextInt();
System.out.print("제수 입력: ");
int num2=keyboard.nextInt();
if(num2==0)
{
System.out.println("제수는 0이 될 수 없습니다.");
i-=1;
continue;
}
System.out.print("연산결과를 저장할 배열의 인덱스 입력: ");
int idx=keyboard.nextInt();
if(idx<0 || idx>99)
{
System.out.println("유효하지 않은 인덱스 값입니다.");
i-=1;
continue;
}
arr[idx]=num1/num2;
System.out.println("나눗셈 결과는 "+arr[idx]);
System.out.println("저장된 위치의 인덱스는 "+idx);
}
}
}
|
cs |
위 소스코드는 if문을 이용한 예외처리 부분이 코드의 중간중간에 삽입되어 있다. 따라서 코드를 분석하는데 있어서 불편함이 따를 수 있다.
그러나 try~catch문을 활용하면 예외처리를 위한 코드를 완전히 별도로 묶을 수 있다.
하나의 try 영역에 둘 이상의 catch 영역을 구성할 수 있기 때문이다.
문제에 있는 소스코드를 try~catch문으로 변경해 보자.
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
|
import java.util.Scanner;
class ExceptionHandleUseIf
{
public static void main(String[] args)
{
Scanner keyboard=new Scanner(System.in);
int[] arr=new int[100];
for(int i=0; i<3; i++)
{
try
{
System.out.print("피제수 입력: ");
int num1=keyboard.nextInt();
System.out.print("제수 입력: ");
int num2=keyboard.nextInt();
System.out.print("연산결과를 저장할 배열의 인덱스 입력: ");
int idx=keyboard.nextInt();
arr[idx]=num1/num2;
System.out.println("나눗셈 결과는 "+arr[idx]);
System.out.println("저장된 위치의 인덱스는 "+idx);
catch(ArithmeticException e)
{
System.out.println("제수는 0이 될 수 없습니다.");
i-=1;
}
catch(ArraysIndexOutOfBoudnsException e)
{
System.out.println("유효하지 않은 인덱스 값입니다.");
i-=1;
}
}
}
}
|
cs |
(4) 다음은 컴파일에러가 발생하는 소스코드이다. 무엇이 문제인가?
1
2
3
4
5
6
7
8
9
10
11
12
|
try
{
...
}
catch(Throwable e)
{
...
}
catch(ArithmeticException e)
{
...
}
|
cs |
☞
1
2
3
4
5
6
7
8
9
10
11
12
|
try
{
...
}
catch(ArithmeticException e)
{
...
}
catch(Throwable e)
{
...
}
|
cs |
(5) 예외 상황의 발생여부와 상관없이 항상 실행되는 영역에 쓰는 키워드는 무엇이고, 그것을 이용하여 소스코드를 작성해 보시오.
- finally
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
|
class FinallyTest
{
public static void main(String[] args)
{
boolean result = divisor(4, 2);
if(result)
System.out.println("연산 성공");
else
System.out.println("연산 실패");
result = divisor(4, 0);
if(result)
System.out.println("연산 성공");
else
System.out.println("연산 실패");
}
public static boolean divisor(int num1, int num2)
{
try
{
int result = num1/num2;
System.out.println("나눗셈 결과 : " + result);
return true;
}
catch(ArithmeticException e)
{
System.out.println(e.getMessage());
return false;
}
finally
{
System.out.println("finally 영역 실행");
}
}
}
|
cs |
(6) 나이를 입력 받았을 때 0보다 작은 값이 입력되면 "유효하지 않은 나이가 입력되었습니다."라고 출력하도록 예외처리를 만드시오.
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
|
import java.util.Scanner;
class AgeInsertException extends Exception
{
public AgeInsertException()
{
super("유효하지 않은 나이가 입력되었습니다.");
}
}
class ProgrammerDefineException
{
public static void main(String[] args)
{
System.out.print("나이를 입력하세요.");
try
{
int age = readAge();
System.out.println("당신은 " + age + "세입니다.");
}
catch(AgeInsertException e)
{
System.out.println(e.getMessage());
}
}
public static int readAge() throws AgeInsertException
{
Scanner sc = new Scanner(System.in);
int age = sc.nextInt();
if(age<0)
{
AgeInsertException exp = new AgeInsertException();
throw exp;
}
return age;
}
}
|
cs |
(7) 6번을 메소드를 쓰지 않고서 메인 메소드 내에서 처리해보자.
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
|
import java.util.Scanner;
class AgeInsertException extends Exception
{
public AgeInsertException()
{
super("유효하지 않은 나이가 입력되었습니다.");
}
}
class ProgrammerDefineException
{
public static void main(String[] args)
{
Scanner sc = new Scanner(System.in);
System.out.print("나이를 입력하세요.");
try
{
int age = readAge();
if(age<0)
{
AgeInsertException exp = new AgeInsertException();
throw exp;
}
System.out.println("당신은 " + age + "세입니다.");
}
catch(AgeInsertException e)
{
System.out.println(e.getMessage());
}
}
}
|
cs |
(8) 6번을 main메소드내에서도 예외상황을 처리하지 않는다면 어떻게 해야 되는가?
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
|
import java.util.Scanner;
class AgeInsertException extends Exception
{
public AgeInsertException()
{
super("유효하지 않은 나이가 입력되었습니다.");
}
}
class ProgrammerDefineException
{
public static void main(String[] args) throws AgeInsertException
{
System.out.print("나이를 입력하세요.");
int age = readAge();
System.out.println("당신은 " + age + "세입니다.");
}
public static int readAge() throws AgeInsertException
{
Scanner sc = new Scanner(System.in);
int age = sc.nextInt();
if(age<0)
{
AgeInsertException exp = new AgeInsertException();
throw exp;
}
return age;
}
}
|
cs |
'Java > Day21' 카테고리의 다른 글
[Java] 전화번호 관리 프로그램 - 전화번호 05단계 (0) | 2021.11.25 |
---|