[Java]내 맘대로 자바 정리하기 1(예외처리)

이종호·2020년 12월 7일
0

자바

목록 보기
1/1

컴퓨터 하드웨어의 오동작 또는 고장으로 인해 응용프로그램 실행 오류가 발생하는 것을 자바에서는 에러라고 한다.
에러는 JVM실행에 문제가 생겼다는 것이므로 JVM위에서 실행되는 프로그램을 아무리 견고하게 만들어도 결국 실행 불능 이 된다.
갤발자는 이런 에러에 대처할 방법이 전혀 없다.

자바에서는 에러 이외에 예외라고 부르는 오루가 있다.
예외란 사용자 잘못된 조작 또는 개발자의 잘못되 코딩으로 인해 발생하는 프로그램 오류를 말한다.
ㅇ뢰가 발생되면 프로그램은 곶바로 종료된다는 점에서는 에러와 동일하다. 그러나 오ㅔ=ㅒ외는 처리(Exception Handling)를 통해 프로그램을 종료하지 않고 정상 실행 상태가 유지되돌고 할 수 있다.

예외는 두 가지 종류가 있다
하나는 일반 예외 이고 ㅎ다른 하나는 실행 예외 이다.

[일방 얘외는 컴파일러 체크 예외라고도 하는데 자바 소스를 컴파일 하는 과정에서 얘외처리 코드가 필요한지 검사하기 떄문이다.

만약 예외 처리 코드가 없다면 컴파일 오류가 발생한다. 실행 예외는 컴파일 하는 과정에서 예ㅊ외 처리 코드를 검하하지 않는 예ㄹ외를 말한다. 컴파일시 예외 처리르 확인하는 차이일 뿐 두 가지 예외는 모두 예외 처리가 필요하다.

자바에소눈 얭하룰 클래스로 관리한다.
JVM은 프로그램을 실행하는 도중에 예외가 발생하면 해당 예외 클래스로 객첼르 생성한다.
그리고 나서 예외 처리 코드에ㅓ ㅇ외 객체를 이용할 수 있도록 해준다. 모든 예외 클랫들은 다음과 같이 java.lang.Exception클래스를 상속받는다.

일반예외와 실생예외는 Runtime Exception을 상속 받냐 안 받냐로 나눈다.

실행 예외 == java.lang.RuntimeException을 상소 받은 예외들

실행 예외는 자바 컴파일러가 체그를 하지 않기 때문에 오로지 개발자의 경험에 의해서 예외 처리 코드를 삽입해야한다. 만약 개발자가 실행 예외에 대해 예외 처리 코드를 넣지 않았을 경우, 해당 예외가 발생하면 프로그램을 곧바로 종료된다. 자바 프로그램 개발 경력이 풍부하다면, 언제 어ㅓㄸㄴ 싱행예외가 발생하는지 쉽게 알 수 이지만, 이제 ㅅ시작하는 개발자렴ㄴ 긱즘부터 설명하느 몇 가지 실행 예외를 잘 익혀두기 라바란다.

10.2.1 NullPointException

자바 프로그램에서 가장 빈번하게 발생하는 실생 예외는java.lang.NullPointException일 것이다. 이것은 객체 참ㄷ조가 ㅇ벗는 상태, 즉 null값을 작는 참조 변수로 객체 접근 연산자인 도트(.)를 사용했을 때 발생한다. 객체가 없는 상태에서 객체를 사용하려 했으니 예외가 발생하는 것이다.

프로그램에서 예외가 살뱅하면 예외 메시지가 Console 뷰에 출력되면서 프로그램잉 종료된다.
Console뷰에 출력되는 내용에는 어떤 예외디가 어던 소스의 몇 번째 코드에서 발생했는지에 대한 정보가 들어이따.
위 예제의ㅣ 겨웅NullPointException.java소스이 5번재 코드에서 발생했음을 알려준다.

10.2.2 ArrayIndexOutOfBoundsException

배열에서 인덱스 범위를 초과하여 사용할 경우 실행 예외인 java.lang.ArrayIndexOutOfBoundsException이 발생한다.
예를 들어 길이가 3인 int[] arr = new int[3] 배열을 선언해따면 0~2까지ㅣ 인덱스를 사용할 수 잇다.
하지만 arr[3]을 사용하려맣면 해당 에러가 발생한다.

예제를 다음과 같이 수정하면 해당 에러가 발생하지 않는 좋은 프로그램이 된다.
배열값을 읽기 전ㅇ 배열의 길이를 먼저 조사하는 것이다.
실행 매개 값이 없거나 부족할 경우 조건문을 이용해서 사용자에게 실행 방법을 알려준다.

10.2.3 NumberFormatException

프로그램을 개발하다 보명 문자열로 되어 있는 데이터를 수샂로 변경하는 겨우우가 자주 발생하낟.
문자열을 숫잘 변환한ㄴ 방법으 ㄴ여러가지가 있지만 가장 많이 상요되는 코드는 다음과 같다.
int=> Integer.parseInt(String s): 주어진 문자열을 정수로 변환해서 리턴
double => Double.parseDouble(String s): 주어진 문자여릉 ㄹ실수로 변환해서 리턴

Integer와 Double은 포장 클래스라고 하는2제 11장에서 자세히 ㅓㄹ명된다. 이 클래스의 정적 메소드인 parseXXX메소드를 ㅣ용하면 문자열을 숫자로 변환할 수 있다. 이 메소드들은 매개값인 문자열이 숫자로 변환될 수 있따면 숫자를 리턴한지만 , 숫자로 변환ㄴ될수 ㅇㅂ서는 문ㅈ각 포함되어 있담ㄴ java.lang.NumberFromatException을 발생한다.

10.2.4 ClassCastException

타입 변환은 상위 클래스와 하위 클래스 간에 발생하고 구현 클래스와 인터페이스간에도 발생하낟.
이러한 관계가 아니라면 클래스는 다른 클래스로 타입 변환할 숭 ㅓㅄ다.ㄴ
억디로 타입 변환을 시도할 겨웅ClassCastException이 살뱅한다. 예를 들어 다음과 같이 상속 관계와 구현 관가가 있다고 가정해보자.

문제 없는 코드

Animal animal = new Dog()
Doig dog= (Dog) animal;

RemoteControll rc = new Television();
Television tv = (Television)rc;

그러나 다음과 같이 타입 변화능 ㄹ하면 ClassCastException이 발생한다. 대입된 객체가 아닌 다른 클래스 타임ㅂ으로 타입 변환해기 떄문이다.

Animal animal = new Dog();
Cat cat = (Cat) animal;

RemoteControl rc = new Television();
Audio audio = (Audio) rc;

ClassCastException을 발생시키지 않으려면 타입 변환 전에 타입 변환이 가능하지 instanceof연산자로 확인하는 것이 돈핟. instanceof연산의 결과가 true이면 좌항 객체를 우항 타입을 변환이 가능하ㅈ다는 것이다.

if(animal instanceof Dog)
	Dog dog = (Dog) animal;

10.3 예외 처리 코드

프로그램에서 예외가 발생했을 경우 프로그램의 갑작스러운 종룔를 막고, 정상 실행을유지할 수 있도록 처리하는 코드를 예외 처리 코드라고 한다. 자바 컴파일러는 소스 파일을 컴파일 할 떄 일반 예외가 발생할 가능성이 있는 코드를 발견하면 컴파일 오류를 발생시켜 개발자로 하여금 강ㅇ제적으로예외 처리 코드를 작성하돌고 역후하낟. 그러나 싫ㅇ 예외는 커파일러가 체그해주지 않기 ㅒ뚬ㄴ에예ㅊ외 처리 코드를 개발자의 경험을 바타응로 삭성해야하낟. 예외 처리코ㄷ는 try-catch-finally블록을 이용한다. 이 블록은 생성자 내부와 메소드 내부에서 작성되어 일반 예욍와 실행 예외가 발생할 경우ㅏ 예외 처리를 할 수 있도록 해준다,.

!! try, catch블록에서 return문을 사용ㅇ하더라고 finally블로근 항상 실행된다.

Class.forName()메소드는 매개값으로 주어진 클래스가 존재하면, Class객체를 리턴하지만, 존재하지 않으면 ClassnotFoundException을 발생시킨다.
ClasNotFoundEXceptoin예외는 일반 예외이므로 컴파일러는 개발자로 하여금 예외 처리 코드를 다음과같이 작성하도록 요구한다.

10.4 에외 종류에 따른 처리 코드

10.4.1 다중 catch

try블록 내부는다양한 종류으이 예외가 발생할 수 잇다.
이 겨웅, 발생되는 예외별로 예외 처리 코드를 다르게 하려면 어떻게 하면 될까/. 이것에 대한 해답은다중 catch블록을 삭성하는 것이다.
catch블로의 예외 클래스 타입은 try블로에서 발생된 예외의 종류르 말하는데, try블로에서 해당 타입의 예외가 발생함녀catch블록을실행하도록 되어 이싿.

catch 블록이 여어 개라 할지라도 단 하나의 catch블록말 실행ㅎ된다. 그 이유는 try블로에서 동시다발적으로 예외가 발생하지 ㅇ낳고, 하나의 예외가 발생하면 즉시 실행을 멈추고 해당 catch블록으로이동하기 ㄸ문이다.

10.4.2 catch 순서

다중 catch블록을 삭성할 떄 주의할 점은 상위 예외 클래스가 하ㄴ위 예외 클래스보다 아래쪽에 위치해야 한다. try브로에서 예외가 발생했을 때, 예외를처리해줄 catch블록은 위에서부터 차례대로 검색된다. 만약 상위 예외 클랫의catch블록이 위에 있다면, 하위 ㅇ예외 클랫의 catch블록은 실행되지 ㅇ낳는다. 왜냐하면 하위예외는 상위 예외를상속했기 떄문에 상위 예외 타입도 되기 때문이다.

10.4.3 멀티 catch

자바 7부터 하나의catch블록에서 여러개의 예외를 처리할 수 있도록 멀티(multi) catch기능을 추가했다. 다음은 멀티 catch블록을 작성하는 방법을 보여준다. catch괄호()안에 동일하게 처리하고 싶은 예외를 |로 연결하면 된다.

10.5 자동 리소스 닫기

자바 7에서 새로 수가된 try-with-resources를 사용하면 예외 삽생 여부와 상관없이 사용해섣ㄴ 리소스 객체(각종 입출력 스트림, 서버 소켓, 소켓, 각종 채널)의 close() 메소드를 호출해서 안전하게 리소스를 닫아준다. 이소스라는 말이 새호하게 들리맂 모르겟다. 여러의미가 있겠지만 여기서는 ㄴ데이터를 읽고 쓰는 객체라고 생각ㄱ해 두자. 예를 들어 파일의 데이터를 읽은FileInputSTream객체와 파일에 쓰는FileOutputStream은 리소스 객체라고 보면 된다.
다음은 자바6 이전까지 사용해썯 코드이다.

FileInputStream fis = null;
try{
	fis = new FileInputStream("file.txt");
    ...
} catch(IOException e){
	...	
} finally {
	if(fis != null) {
    	try{
        	fis.close();
        } catch(IOException e) { }
    }
}

finally블로에서 다시 try-catch를 사용해서 cloase메소드를 예외 처리해야하므로 다소 복잡하게 보인다.
자바 7에서 추가된 try-with-resoureces 를 사용하면 다음과 같이 간단해진다. 어디봐도 close를 명시적으로 호출한 곳이 없다.

코드

try블록이 정상적으로 실행을 완려했건, 도중에 얘외가 ㅊ바랭하게 되면 자동으로FileInputSTream의 close()메소드가 자동 호출된다.
try{}에서 예외가 발생하면 ㅑㅁ우선 close로 리소스를 닫고 catch블록을 실행한다. 만약 복수개의 리소스라면 다음과 같이 작성할 수 있다.

try-with-resources를 사용하기 위해서는 조건이 있는데, 리소스 객체는 java.lang.AutoCloseable인터페이슬ㄹ 구현하고 있어야 한다.
AutoCloseable에는 close메소드가 정의되어 있는데 try-with-resoureces는 바로 이 close메소드를 자동 호출한다. API도큐먼트에서ㅓ AutoCloasable인터페이스를 찾아 All Known Implementing Classes:"를 보면 try-with-resources와 함께 사용할 수 있는 리소스가 어떤 것이 잇는지 알 수 있다.
다음 예제는 직접 AutoCloseable을 구현해서FileinpuTSTream 클래스를 자것ㅇ해띠ㅏㄴ.

10.6 예외 떠넘기기

메소드 내부에서 예외가 발생할 수 있는 코드를 작성할 떄 try-catch블록으로 예외를 처리하느것이 기본이지만, 경우에 따라 메소드를 호출한 곳으로 예ㄹ외를 떠넘길 수도 있다. 이때 사용하는 키워드가 ㅇ throws이다. throws키워드는 메소드 선언부 끝에 작성되어 메소드에서 처리하지 않느 ㄴ예외를 호출한 곳을 떠넘기는 역할을 한다.
throws키워드 위에 떠넘길 예외 클래스를 쉼표로 구분해서 나열해줌녀 된다.

발생할 수 있는 예외의 종류별로 throws뒤에 나열하는 것이 일반적이지만, 다음과 같이 throws Exception만으로 모든 예외를 간단히 떠넘길 수도 잇다.

throws 키워드가 붙어있는 메소드는 반드시 try블록 내에서 호출되어야 한다.
그리고 catch 블록에서 떠넘겨 받은 예외를 처리해야한다. 다음 코드는 throws키워드가 이쓴ㄴ method2()를 method1()에서 호출하는 방버을 보여누단.

자바API도큐먼트를 보면 클래스 생성자와 메소드 선언부에 throws키워드가 붙어있는 것을 흐힌 볼 수 있다. 이러한 생성자와 메소드를 사용하고 싶다면, 반드시 try-catch블록으로예외처리를 해야하낟. 아니면 throws를 다시 상용해서 예외를호출한 곳으러 떠넘겨야 한다.
그렇지 않으며느 컴파일 오류가 발생한다. 얘를 드렁 Clas의 forName()메소드를 자바 API도큐먼트에서 보면 다으모가 같다.
... throws ClassNotFoundException
forName()메소드 선언부 뒤에 throws ClassNotFoundException이 붙어 있기 ㄸ문에 forName메소드를 호출할 떄 try-catch블록으로예외를 처리하거나, throws로 예뢰를 떠넘겨야 하낟.
다음 예제에서 Class.forName()메소드를 사용하는 findClas메소드는 얘와ㅣ를 떠넘겼고, findClass()를 호출하는 main메소든 ㄴtry-catch블록을 사용해 예외를 처리했다.

main에서 throws키워들 랏용해서 예외를 떠넘길 수 있는데ㅏ, 결국 JVM이 최종적으로 예외 처리를 하게 된다. JVM은 예외의 내용으 ㄹ콘솔에 출력하는 것으로 예외 처리를 한다.

main() 메소드에서throws Exception을 붙이는 것은 좋지 못한 ㅇ외 처리 방ㅂ버이다. 프로그램 사용자는 프로그램이 알 수 없는 예외 내용을 출력하고 종료되는 것을 좋아하지 않는다. 그렇기 때문에 main에서 try-catych블로그올 예외를 최종 처리하는것이 바람직하다.

10.7 사용자 정의 예외와 예외 발생

프로그램을 개발하다 보면 자바 표주API에서 제공하는 예외 클래스만으로나 다양한 종류의 예외를 표현할 수가 없다. 예를 들어 은행 업무를 처리하는 프로그램에서 잔고보다 더 ㅁ낳은 출금 요청이 들어왔을 경우 오류가 되며,프로그램은 잔고 부족 얘뢰를 잘생시킬 필요가 있다. 그러나 잔고 붖고 ㅇ?ㅖ외는 자바 표쥬 API에서 존재하지 않는다. 잔고 부족 ㅇ외와 같이 애플리켕션 서비스와 관련된 예외를 ㄹ애플리케이션 예외 라고하낟. 애플리케이션 예외는 개발자가 직접 정의해서 만들어야 하므로 사용자 정의 얘외라고도 한다.

10.7.1 사용자 정의 예외 클래스 선언

사용자 정의 예외 클래스는 컴파일러가 체크하는 일반 예외로 선언할 수도 이쏙, 컴파일러가 체그하지 앟는 실행 예외로 선언할 수도 있다. 일반 예외로 선언할 경우 Exception을 상속하면 되고, 실행 예외로 선언할 겨웅 RuntimeException을 상속하면 된다.

pubcli class XXXException extends [Exception | RuntimeException] {
    public XXXException() {};
    public XXXException(String message) { super(message); };
}

사용자 정의 예외 클래스 이름은 Exception으로 끝나는 것이 좋다. 사용자 정의 예외 클래스도 필드, 생서자, 메소드 선언들을 포함할 수 있지마느 대부분 생성자 선언만 포함한다. 생성자는 두개를 선언하는 것이 일반적인데, 하나는 매개변수가 없는 기본 생성자이고, 다른 하나는 예외 발생 원인 을 전달하기 위한 STrin 타입의 매개변수를 샂는 생성자이다. String타입의 매개변수를 갖는 생성자는 상위 클래스의 생성자를 호출하여 예외 메시지를 넘겨주낟. 예외 메시지ㄹ의 용도는 catch{} 블록의 예외 처리 코드에서 이용하기 위해서이다. 다음은 잔고 부족 예외를 사용자 정의 예욐를새ㅡ로 선언한 것이다.

10.7.2 예외 발생시키기

사용자 정의 얘외 또는 자바 표준 예외를 여러분의 코드에서 발생시키는 방법을 알아보자. 콛에서 예외를 발생시키는 방법은 다음과같다.

throw new XXXException();
throw new XXXException("메세지");

예외 객체를 생성할 떄는 기본 생성자 또는 예외 메시지를 갖는 생성자주 어떤 것을 사용해도 된다.
만약 catch블록세어 예외 메시지가 필요하다면 예외메시지를 ㅏㄱㅈ는 생성자을 이용해야 하낟.
예외 발생 코드릀가지고 이쓴ㄴ 메소드는 내부에서 try-catch블록으로 예외를처리할 수 있지만 대부분으 자신을 호출한 곳엣 예외를 처리하돌 ㄱ throws 키워드롤 예외를 넘긴다.

public void method() throws XXXException {
	throw new XXXException();
}

그렇기 때문에 throws 키워드를 포함하고 있는메소드느 호출한 곳에더다음과 같이 예외 처리르 해주여아한ㄷ.

10.8 예외 정보 얻기

try 블로에서 예외가 발생되면 예외 객체는 catch블록의 매개변수에서 참조하게 되므로 개배 변수를 이용하면 예외 객체의 정보를 알 수 있다. 모든 예외 객체는Exception클래스를 상속하기 때문ㅇ Exception이 가지고 있는 메소드들은 모든 예외 객체에서 호룿ㄹ 할 수 있다.
그중에서도 가장 많이 사용되는 메소드는 getMessage()와 printStrackTrace()이다. 예외를 발생시킬 떄 다음과 같이String타입의 메시지를 ㅏㄱㅈ는 생성자를 이용하였다면 메시지는 자동적으로 예외 객체 대문에 저장된다.

예외 메시지의 내용에는 왜 예외가 발생했는지에 대한 간단ㄴ한 설명이 포함된다. 좀 더 상세한 원인을 세분화하기 위ㅐ 예외 코들ㄹ 포함하기도하는데, 예를 들어 데이터베이스에서 발생한 오류들은 예외 코드가 예외 메시지로 전달된다. 이왁 ㅏㅌ이 예외 메시지는 다음과 같이 catch블로에서 getMessage메소드의 리턴값으로 얻을 수 잇다.

printStackTrace()는 메소드 이름에서 알 수 있듯 예외 발생 코드를 추적해서 모두 콘솔에 출력한ㄷ.ㅏ 어떤 예외가 어디에서 발생했는지 상세하게 출력해주기 떄문에 프로그램을 테ㅡ트하면서 오류를 찾을 떄 활용된다.

ps.
신용권 선생님의 이것이 자바 책을 보면서 알게된 내용들을 써적여 나갈 것이다.
Java란 언어, 많이 접했지만, 여전히 애매하거나 헷갈리거나 어려운 내용들이 산재해있다.
이를테면 spring에서 어떤 변수는 static final이고 어떤 변수는 static만쓰고 어떨 떄는 final만 쓰는지? 보통 final만 쓰는 경우는 없고 static final을 쓰는데,

뭐 그런 것들이나, 휼륭한 디자인 패턴?(시스템 패턴? 정확한 명칭은 모르겠다.)들에도 관심이 많다.

보통 Server라 하면 Controller, Service, DAO, DTO, VO등등이 있는데,
위에서 부터 설계하는건지, 아니면 아래서 부터 설계하는건지

목표하는 프로젝트 개념은 어느정도 오나성되어야 플젝 진행을 하는지?()
이는 워터폴인지 애자일인지를 생ㄱ가하면 되는건지 모르겠다.

원칙주의적이고 융통성이 부족한 나로선 워터폴이 맘이 편하지만
처음부터 설계가 완벽할 수 없는 왕초보 개발자여서 무조건 부족한 기능 부분이 생겨버렸다.(물론 내가 더 열심히 설계했다면 달라졌을 지도.)
애자일 방법(그중에서 스크럼)을 사용해본 결과, 관리만 좀 제대로 되고 툴에 더 익숙해진다면 정말 괜찮은 방법일지도 모른다는 생각이 들었다.

내가 썻던 툴은 Azure Board였는데, github주소까지 연동 되어 사용가능했기에 꽤나 훌륭했다.

profile
열심히 사는 사람

0개의 댓글