네트워크연결 - 데이터베이스연결 - 사용 - 에러 - 데이터베이스닫기 - 종료
데이터베이스를 사용하고 나서 닫는게 매우 중요하다.
예외처리를 통해 프로그램 종료 이전 데이터베이스를 닫을 수 있게 해야한다.
예외
사용자의 잘못된 조작 또는 개발자의 잘못된 코딩으로 인한 오류로 예방할 수 있음
예외처리를 통해 정상 실행 상태가 유지되도록 할 수 있다.
cf.에러:하드웨어의 잘못된 동작 또는 고장으로 인한 오류, 정상 실행 상태로 돌아갈 수 없음
예외처리
에러를 수정하는 게 아니라 에러가 나면 처리하지 못하는 부분에 대해서 프로그램하는 것으로, 선택이 아닌 필수이다.
예외의 종류

자바에서는 예외를 클래스로 관리한다.
JVM은 프로그램 실행중 예외가 발생하면 해당 예외 클래스로 객체를 생성한다.
그리고나서 예외처리 코드에서 예외 객체를 이용할 수 있도록 해준다.
모든 예외 클래스들은 java.lang.Exception 클래스를 상속받는다.
일반예외 : 컴파일러 체크 예외, RuntimeException 상속X
실행예외 : 컴파일러 예외처리코드를 검사X, RuntimeException상속O
NullPointerException : 객체 참조가 없음. null값을 갖는 참조변수 사용
ArrayIndexOutOfBoundsException : 배열에서 인덱스 범위 초과하여 사용
NumberFormatException : 숫자로 변환될 수 없는 문자를 변환하려 할 때
ClassCastException : 대입된 객체가 아닌 다른 타입으로 타입변환
(상속 관계의 클래스,인터페이스와 구현클래스)
NullPointerException 예시
public class NullPointerExceptionExample {
//null값을 가지는 참조 변수로 객체 접근연산자인 도트(.)를 사용할 때 발생
public static void main(String[] args) {
String data = null;
System.out.println(data.toString());
}
}
ArrayIndexOutOfBoundsException 예시
배열값을 읽기 전에 배열의 길이를 먼저 조사해야 한다. 조건문을 이용해 실행 방법을 알려줄 수 있다.
public class ArrayIndexOutOfBoundsException {
public static void main(String[] args) {
String data1 = args[0];
//args 배열의 크기가 정해져있지 않아 ArrayIndexOutOfBounds 발생
String data2 = args[1];
System.out.println("args[0]: " + data1);
System.out.println("args[1]: " + data2);
}
}
//예외방지 – 배열값을 읽기 전에 배열의 길이를 먼저 조사
package bookExample;
public class ArrayIndexOutOfBoundsException {
public static void main(String[] args) {
if(args.length == 2) {
String data1 = args[0];
//args 배열의 크기가 정해져있지 않아 ArrayIndexOutOfBounds 발생
String data2 = args[1];
System.out.println("args[0]: " + data1);
System.out.println("args[1]: " + data2);
} else {
System.out.println("[실행 방법]");
System.out.println("java ArrayIndexOutOfBoundsExceptionExample");
System.out.println("값1 값2");
}
}
}
NumberFormatException 예시
Integer, Double은 포장클래스(Ch11)로 정적 메소드 parseXXX()를 가지고 있는데, 이를 이용하면 문자열을 숫자로 변환한다.
public class NumberFormatExceptionExample {
public static void main(String[] args) {
String data1 = "100";
String data2 = "a100";
int value1 = Integer.parseInt(data1);
int value2 = Integer.parseInt(data2); //예외발생
int result = value1 + value2;
System.out.println(data1 + "+" + data2 + "=" + result);
}
}
ClassCastException 예시
package bookExample;
public class ClassCastExceptionExample {
public static void main(String[] args) {
Dog dog = new Dog();
changDog(dog);
Cat cat = new Cat();
changDog(cat); //대입된 객체 Cat이 아닌 다른 클래스타입 Dog으로 타입변환 불가능하다.
}
public static void changDog(Animal animal) {
if(animal instanceof Dog) { //animal이 참조하는 객체가 Dog타입의 객체로 처음 생성되었다면 true반환
Dog dog = (Dog) animal; //ClassCastException 발생가능
}
}
}
class Animal {}
class Dog extends Animal{}
class Cat extends Animal{}
예외처리 코드
예외 발생시에도 정상 실행을 유지할 수 있도록 처리하는 코드
try-catch-finally 블록
생성자 내부와 메소드 내부에서 작성되어 일반 예외와 실행 예외가 발생할 경우 예외 처리를 할 수 있도록 해준다.
try {
예외 발생코드
} catch(예외클래스 e) {
예외처리
} finally { //옵션으로 생략 가능하다.
예외와 무관하게 항상 실행; // try블록과 catch블록에서 return문을 사용해도 항상 실행된다.
}
Class.forName() 메소드를 통한 일반예외발생과 처리
Class.forName() : 매개값으로 주어진 클래스가 존재하면 Class 객체를 리턴, 아니면 예외 발생
package bookExample;
public class TryCatchFinallyExample {
public static void main(String[] args) {
try {
//Class.forName() : 매개값으로 주어진 클래스가 존재하면 Class 객체를 리턴, 아니면 예외 발생
Class clazz = Class.forName("java.lang.String2");
} catch (ClassNotFoundException e) {
System.out.println("클래스가 존재하지 않습니다.");
}
}
}
실행예외발생과 처리(ArrayIndexOutOfBoundsException, NumberFormatException)
package bookExample;
public class TryCatchFinallyRuntimeExceptionExample {
public static void main(String[] args) {
String data1 = null;
String data2 = null;
try {
data1 = args[0]; //Run configurations Arguments에 실행 매개값을 주지 않으면 예외발생
data2 = args[1];
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("실행 매개값의 수가 부족합니다.");
System.out.println("[실행 방법]");
System.out.println("java TryCatchFinallyRuntimeExceptionExample num1 num2");
return;
}
try {
int value1 = Integer.parseInt(data1); //실행 매개값이 숫자로 변환될 수 없을 때 예외 발생
int value2 = Integer.parseInt(data2);
int result = value1 + value2;
System.out.println(data1 + "+" + data2 + "=" + result);
} catch(NumberFormatException e) {
System.out.println("숫자로 변환할 수 없습니다.");
} finally {
System.out.println("다시 실행하세요.");
}
}
}
package p1;
public class ExceptionEx {
public static void main(String[] args) {
try {
//예외날 수 있는 문장을 넣어주는 곳
System.out.println("네트워크 연결하기");
System.out.println("데이터베이스 열기");
String data = null; // 예외발생
System.out.println(data.toString()); // 여기서 에러 null값을 참조(.)할 수 없음. 에러나면 catch행으로 넘어감
// 예외명과 변수명. 예외 발생시 힙메모리에 Null~이 올라가서 주소를 스택메모리 e에 넣어준다.
//예외에 대한 처리할 내용 넣는 곳
} catch (NullPointerException e) {
// e.printStackTrace(); //e에 가면 Null~의 메소드 중 printStackTrace가 에러를 잡아준다.
// System.out.println(e.getMessage());
System.out.println("데이터가 입력되지 않았습니다.");
} finally { //예외와 상관없이 무조건 처리할 내용을 넣는 곳
System.out.println("데이터베이스 닫기");
System.out.println("네트워크 로그아웃하기");
}
}
}
다중 catch 블록 작성
단 하나의 catch블록만 실행된다.
작은타입부터 더 큰 타입의 예외처리로 코드
앞에서 작은 타입의 예외처리코드가 잡지 못하면 더 큰 타입으로 잡을 수 있게 코드한다.
상위 예외 클래스가 하위 예외 클래스보다 아래쪽에 위치해야 한다.
catch 1 : ArrayIndexOutOfBoundsException
catch 2 : Exception
package bookExample;
public class CatchByExceptionKindExample {
public static void main(String[] args) {
try {
String data1 = args[0];
String data2 = args[1];
int value1 = Integer.parseInt(data1);
int value2 = Integer.parseInt(data2);
int result = value1 + value2;
System.out.println(data1 + "+" + data2 + "=" + result);
} catch(ArrayIndexOutOfBoundsException e) { //다중처리 catch1 : ArrayIndexOutOfBoundsException 처리
System.out.println("실행 매개값의 수가 부족합니다.");
System.out.println("[실행 방법]");
System.out.println("java CatchByExceptionKindExample num1 num2");
} catch(NumberFormatException e) { //다중처리 catch2 : NumberFormatException 처리
System.out.println("숫자로 변환할 수 없습니다.");
} finally {
System.out.println("다시 실행하세요.");
}
}
}
package p2;
public class ClassCastException {
public static void main(String[] args) {
Dog dog = new Dog();
changeDog(dog);
Cat cat = new Cat();
changeDog(cat); // cat을 Dog로 형변환 할 수 없음. 예외 발생 막기위해서 instancof로 검증해야.
Animal animal = new Animal();
changeDog(animal);
}
public static void changeDog(Animal animal) {
try { //catch문을 쓸때는 갈수록 크게 잡는 게 좋음
Dog dog = (Dog) animal;
} catch (ArrayIndexOutOfBoundsException e) { //ClassCastException
System.out.println("강아지만 다운캐스팅하셔야 합니다. 다시 확인해주세요.");
} catch(RuntimeException e1) { //해당하는 Exception이 아니면 밑에 큰 타입 예외가 잡음
System.out.println("위에 catch예외보다 큰 타입 예외");
} catch (Exception e) {
System.out.println("제일 큰 타입의 catch");
}
/*
* if(animal instanceof Dog) { Dog dog = (Dog) animal; System.out.println(dog);
* //참조변수명만 쓰면 "참조변수.toSring()" 호출 } else if(animal instanceof Cat) { Cat cat =
* (Cat) animal; System.out.println(cat); } else { System.out.println(animal); }
*/
}
}
class Animal {
@Override
public String toString() {
return "동물";
}
}
class Dog extends Animal {
@Override
public String toString() {
return "강아지";
}
}
class Cat extends Animal {
@Override
public String toString() {
return "고양이";
}
}
멀티 catch
catch 괄호() 안에 동일하게 처리하고 싶은 예외를 |로 연결한다.
catch(ArrayIndexOutOfBounds | NumberFormatException e){ }
package bookExample;
public class MultiCatchExample {
public static void main(String[] args) {
try {
String data1 = args[0];
String data2 = args[1];
int value1 = Integer.parseInt(data1);
int value2 = Integer.parseInt(data2);
int result = value1 + value2;
System.out.println(data1 + "+" + data2 + "=" + result);
} catch(ArrayIndexOutOfBoundsException | NumberFormatException e) {
System.out.println("실행 매개값의 수가 부족하거나 숫자로 변환할 수 없습니다.");
} catch(Exception e) {
System.out.println("알수 없는 예외 발생");
} finally {
System.out.println("다시 실행하세요.");
}
}
}
자동리소스 닫기
try-with-resources
예외 발생여부와 상관없이
사용했던 리소스 객체의 close() 메소드를 호출해서 안전하게 리소스를 닫아준다
가장 일반적인 패턴의 파일보기 : 파일 지정 new --> 파일 읽기/쓰기 --> close()
리소스 객체 : 각종 입출력 스트림, 서버 소켓, 소켓, 각종 채널 등이 있다.
ex. 데이터를 읽고 쓰는 객체로, 파일의 데이터를 읽는 FileInputStream객체와 파일에 쓰는 FileOutputStream
1. try-catch로 잡기 (try-with-resources 이전)
자바 6 이전까지 사용해왔던 코드
파일을 닫아주기 위해 finally 안에서 try-catch문을 한번 더 쓰고 그 안에서 close()를 호출해야 했다.
import java.io.IOException;
public class TryWithResourceExample {
public static void main(String[] args) {
FileInputStream fis = null;
try { //파일처리할 때 에러가 많이 나기 때문에 거의 항상 예외처리해줌.
fis = new FileInputStream("file.txt"); //읽고 싶은 파일 이름
} catch(IOException e) {
//예외처리
} finally {
if(fis != null) {//파일이 열려서 힙메모리에 올라갔다면
try{
fis.close(); //파일을 닫아줘야함
} catch(IOException e) {}
}
}
2. try ~ with ~ resource
try ~ with ~ resource 구문 자체가 존재하는 건 아니지만, 특징 및 사용방법은 다음과 같다.
close()를 명시적으로 호출하지 않는다.
try블록을 실행 완료했거나 예외가 발생하면 자동으로 FileInputStream의 close() 메소드가 호출되고, catch블록을 실행한다.
① 클래스객체화 : 단 이클래스는 implements AuotoCloseable
AutoCloseable 인터페이스를 구현함으로써 그 안의 close()메소드를 자동 호출한다.
② 예외발생시키기
③ 예외처리 : close()메소드가 자동호출되므로 반드시 처리하는 finally에 쓸 내용의 일부분은 close()메소드에 구현할 수 있음
메인클래스
try( FileInputStream fis = new FileInputStrean("file.txt") ) { //①클래스객체화
throw new 예외명; //②예외발생시키기 : 강제로 예외를 발생시켜 close()
} catch(예외명 e) {
③예외처리
}
구현클래스
class FileInputStream implements AutoCloseable { //AutoCloesable interface구현
// 필드
// 명시적 생성자
public void read(String file){ //필드 초기화}
@Override
public void close() throws Exception{ } //Override close(), throws Exception 예외 호출한 곳으로 넘겨주기
package p3;
import java.io.IOException;
//440 파일에 대한 코드 예제. 파일 지정 new --> 파일 읽기/쓰기 --> close()
public class TryWithResourceExample {
public static void main(String[] args) {
try //파일처리할 때 에러가 많이 나기 때문에 거의 항상 예외처리해줌.
(FileInputStream fis = new FileInputStream("file.txt")) //읽고 싶은 파일 이름으로
{fis.read();
throw new Exception(); //이 줄을 추가함으로써 finally 하지 않아도 close 메소드가 알아서 호출됨
} catch(Exception e) {
//예외처리
System.out.println("예외 처리 코드가 실행되었습니다");
}
}
}
----------------------------------------------
package p3;
public class FileInputStream implements AutoCloseable{
private String file;
public FileInputStream(String file) {
this.file = file;
}
public void read() {
System.out.println(file + "을 읽습니다");
}
@Override
//void close() throws Exception : 접근제한은 늘어나고 예외는 줄어듦
public void close() throws Exception { //인터페이스의 추상메소드를 실체화함. 예외발생하면 던져준다.
System.out.println(file + "을 닫습니다.");
}
}
예외 떠넘기기
throws
메소드 선언부 끝에 작성되어 메소드에서 처리하지 않은 예외를 호출한 곳으로 떠넘긴다.
throws 키워드 뒤에는 떠넘길 예외 클래스를 쉼표로 구분해서 나열한다.
리턴타입 메소드명(매개변수, ...) throws 예외클래스1, 예외클래스2, ...{ }
리턴타입 메소드명(매개변수, ...) throws Exception{ }
throws 키워드가 붙어있는 메소드는 반드시 try 블록 내에서 호출되어야한다.
그리고 catch 블록에서 떠넘겨 받은 예외를 처리한다.
main() 메소드에 throws Exception을 붙이는 건 좋지 않다.
일일이 예외가 발생할만한 곳에 try catch를 쓰면 복잡하기 때문에 throws를 적절히 활용해야한다.
cf.
throw : 예외를 발생시킴
throws : 예외를 호출한 곳으로 떠넘김
public class ThrowsExample {
public static void main(String[] args) { //throws ClassNotFoundException > 단 이렇게 되면 콘솔창을 통해 예외의 내용을 출력하기 떄문에 좋지않음.
try {
findClass(); //예외를 떠넘겨받아, catch블록에서 처리한다.
} catch (ClassNotFoundException e) { //try-catch블록을 작성하는 대신 떠넘길 수 있다
System.out.println("클래스가 존재하지 않습니다.");
}
}
public static void findClass() throws ClassNotFoundException { //없기 때문에 예외가 발생되고, 메소드가 호출된 곳으로 떠넘긴다.
Class clazz = Class.forName("java.lang.String2"); //매개값을 이름으로 가지는 파일이 있으면 객체로 생성해서 clazz에 담는다.
}
}
package p4;
public class ThrowsEx {
public static void main(String[] args) {
A a = new A();
try {
a.mA();
} catch(ArithmeticException | ArrayIndexOutOfBoundsException e) {
//에러가 많다보니 그냥 더 큰 타입의 예외처리코드 RuntimeException으로 퉁치기도 한다.
System.out.println(e.getMessage());
System.out.println("예외 떠 맡아서 처리함");
}
}
}
class A{ //mA() --> mB() --> mC() --> throws 에러 to mB() --> throws 에러 to mA() --> throws 에러 to main -->에러메세지 출력
void mA() throws ArithmeticException,ArrayIndexOutOfBoundsException {
mB();
System.out.println("이곳은 mA메소드");
}
void mB() throws ArithmeticException, ArrayIndexOutOfBoundsException {
mC();
int[] number = new int[3];
number[3] = 10;
System.out.println("이곳은 mB메소드" + number[3]);
}
void mC() throws ArithmeticException {
System.out.println("이곳은 mC메소드");
System.out.println(15/0);
}
}
사용자 정의 예외처리
애플리케이션 서비스와 관련된 예외를 애플리케이션 예외라고 한다.
개발자가 직접 정의해서 만든다는 점에서 사용자 정의 예외라고도 부른다.
1.사용자 정의 예외클래스 선언
사용자정의 예외클래스
public class XXXException extends [ Exception | RuntimeException] { //일반예외면 Exception, 실행예외면 RuntimeException 상속
public XXXException() { } //기본생성자
public XXXException(String message) { super(message) } //상위 생성자 호출하여 예외 메세지를 넘겨주고, catch{} 블록이 이용한다.
}
사용자 정의 예외 클래스의 이름은 Exception으로 끝나는 것이 좋다.
일반예외면 extends Exception, 실행예외면 extends RuntimeException
필드, 생성자, 메소드 선언들을 포함할 수 있지만 대부분 생성자 선언만을 포함한다.
생성자는 두 개를 선언하는 것이 일반적인데,
하나는 기본생성자이고 다른 하나는 예외 발생 원인을 전달하기 위해 String 타입의 매개변수를 가지는 생성자이다.
이를 통해 상위 클래스의 생성자를 호출하여 예외 메시지를 넘겨준다.
2.예외 발생시키기
실행클래스
try{
method();
} catch (XXXException e) {
//예외처리
}
public void method() throws XXXException{
throw new XXXException(); //예외 클래스 객체화, 예외 메세지 필요X
throw new XXXException("메시지"); //예외 클래스 객체화, 예외 메세지 필요O
}
if문을 통해 발생해야 할 예외 조건을 설정하고, throw를 통해 예외를 발생시킨다.
예외 객체를 생성할 때는 기본 생성자 또는
catch블록에서 예외메세지가 필요하다면 예외메세지를 갖는 명시적 생성자를 사용한다.
예외 발생 코드를 가지고 있는 메소드는 내부에서 try-catch 블록으로 예외를 처리할 수 있지만,
대부분은 자신을 호출한 곳에서 예외를 처리하도록 throws 키워드로 예외를 떠넘긴다.
3. 예외 정보 얻기
실행클래스
try{
method();
} catch (예외클래스명 e) {
String message = e.getMessage(); //예외가 가지고 있는 Message얻기
System.out.println(message);
e.printStackTrace(); //예외의 발생 경로를 추적
}
try 블록에서 예외가 발생하면 예외 객체는 catch 블록의 매개 변수에서 참조하게 되므로 매개 변수를 이용하면 예외 객체의 정보를 알 수 있다.
모든 예외 객체는 Exception 클래스를 상속하기 때문에 Exception이 가지고 있는 메소드들을 모든 예외 객체에서 호출 가능하다.
그중 가장 많이 쓰이는 메소드가 getMessage()와 printStackTrace()이다.
getMessage() : String 타입의 메시지를 갖는 생성자를 이용하여 예외를 발생시키면, 메시지는 자동적으로 예외 객체 내부에 저장된다.
예외 메시지의 내용에는 왜 예외가 발생했는지에 대한 간단한 설명이 포함되며, 좀 더 상세한 원인을 세분화하기 위해 예외 코드를 포함하기도 하는데, catch 블록에서 getMessage() 메소드의 리턴값으로 예외메시지를 얻을 수 있다.
printStackTrace(): 예외 발생 코드를 추적해서 모두 콘솔에 출력한다. 어떤 예외가 어디에서 발생했는지 상세하게 출력해주기 때문에 프로그램을 테스트하면서 오류를 찾을 때 활용된다.
//사용자 정의 예외클래스
public class BalanceInsufficientException extends Exception {
public BalanceInsufficientException() {}
public BalanceInsufficientException(String message) {super(message);}
//message를 Exception의 필드에 저장한다.
}
//사용자 정의 예외 발생시키기 (부품 클래스)
public class Account {
private long balance;
public Account() {}
public long getBalance() {
return balance;
}
public void deposit(int money) {
balance += money;
}
public void withdraw(int money) throws BalanceInsufficientException {
if(balance < money) {
throw new BalanceInsufficientException("잔고부족:" + (money-balance)+" 모자람"); //소괄호 안의 메세지를 매개값으로 >Exception에 돌려줌
}
balance -= money;
}
}
//실행 클래스- 예외 정보 얻기
public class AccountExample {
public static void main(String[] args) {
Account account = new Account();
//예금하기
account.deposit(10000);
System.out.println("예금액: " + account.getBalance());
//출금하기
try {
account.withdraw(30000);
} catch(BalanceInsufficientException e) { //BalanceInsufficientException을 e가 참조한다.
String message = e.getMessage(); //부모클래스 Exception의 getMessage를 이용해서 저장해두었던 message를 꺼내온다.
System.out.println(message);
System.out.println();
e.printStackTrace(); //콘솔에 어떤 위치에서 어떤 예외가 발생했는지에 대해 말해준다.
}
}
}

package p7;
//사용자가 만든 예외
public class JumsuRangeException extends Exception{
public JumsuRangeException() {
}
public JumsuRangeException(String message) {
super(message);
}
}
----------------------------------
import java.util.Scanner;
public class JumsuBusiness {
int jumsu;
void inputJumsu() throws JumsuRangeException { //여기서 던져준다
Scanner sc = new Scanner(System.in);
int jumsu = sc.nextInt();
if(jumsu<0 || jumsu > 100) {
throw new JumsuRangeException(); //예외가 발생하면
}
this.jumsu = jumsu;
}
void outputJumsu() {
System.out.println("당신의 점수는 " + jumsu);
}
}
---------------------------
public class JumsuMain {
public static void main(String[] args) {
JumsuBusiness jb = new JumsuBusiness();
try {
jb.inputJumsu();
} catch(JumsuRangeException e) {
String message = e.getMessage();
//Throwable의 자식 Exception의 자식 JumsuBusiness는 Throwable의 메소드 getMessage 쓸 수 있음
System.out.println(message);
}
jb.outputJumsu();
}
}
'Java > Study' 카테고리의 다른 글
| Ch11 기본 API클래스 - 해시코드 (0) | 2021.03.25 |
|---|---|
| Ch11 java.lang & java.util 패키지(Object, System, Class 클래스) (0) | 2021.03.23 |
| Ch7 상속 - 다형성 및 추상클래스 (0) | 2021.03.11 |
| Ch7 상속 (0) | 2021.03.09 |
| Ch6 - 패키지, 접근제한자 (0) | 2021.03.05 |