자바의 모든 에러와 예외는 Throwable 클래스를 상속받는다. 예외 발생 시 예외 클래스로 부터 객체를 생성한다.
checked exception에 대해 try-catch블록으로 exception발생시 로그를 statck trace를 남기고 다음 순서 로직을 수행하거나 throws 키워드를 통해 해당 메서드를 호출한 메서드로 exception에 대한 내용을 전달 할 수있다.
public class Test {
public static void readFile() throws IOException {
BufferedReader bw = new BufferedReader(new FileReader("myFile.txt"));
int read = bw.read();
System.out.println("read = " + read);
bw.close();
}
public static void main(String[] args) {
try {
readFile();
System.out.println("exception 이후1");
} catch (IOException ioe) {
ioe.printStackTrace();
}
System.out.println("exception 이후2");
}
}
java.io.FileNotFoundException: myFile.txt (지정된 파일을 찾을 수 없습니다)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:216)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:111)
at java.base/java.io.FileReader.<init>(FileReader.java:60)
at Test.readFile(Test.java:6)
at Test.main(Test.java:14)
exception 이후2
위의 코드를 실행했을때 checked exception으로 인해 compile이 실패했다.
public class Test {
public static void readFile1() {
try {
readFile2();
} catch (IOException e) {
System.out.println("readFile2에서 발생 !!");
throw new IOException(e);
}
}
public static void readFile2() throws IOException {
boolean checked = true;
if(checked) {
throw new IOException();
}
}
public static void main(String[] args) throws IOException {
readFile1();
System.out.println("exception 이후2");
}
}
readFile2에서 발생 !!
Exception in thread "main" java.lang.RuntimeException: java.io.IOException
at Test.readFile1(Test.java:18)
at Test.main(Test.java:28)
Caused by: java.io.IOException
at Test.readFile2(Test.java:24)
at Test.readFile1(Test.java:15)
... 1 more
throws로 상위 메서드로 전달
readFile2에서 발생한 exception은 readFile1로 전달되고 따로 exception처리가 되지않아 최상위 main메소드까지 전달된다.
throw new IllegalArgumentException();public class Test {
public static void readFile1() throws IOException {
readFile2();
}
public static void readFile2() throws IOException {
boolean checked = true;
if(checked) {
throw new IOException();
}
}
public static void main(String[] args) throws IOException {
readFile1();
System.out.println("exception 이후2");
}
}
Exception in thread "main" java.io.IOException
at Test.readFile2(Test.java:20)
at Test.readFile1(Test.java:6)
at Test.main(Test.java:24)
try-with-resources는 try블록이 끝나면 자원을 반납하는 기능을 가진다. try구문안에 전달할 수 있는 자원은 AutoCloseable인터페이스 구현체로 제한된다. 이로써 finally 구문에서 자원을 반납하지 않아도 된다.
try-finally를 이용하여 exception 발생 순서를 확인하고자 한다.
static String firstLineOfFile(String path) throws IOException {
BadBufferedReader br = new BadBufferedReader(new FileReader(path));
try{
return br.readLine();
} finally {
br.close();
}
}
public static void main(String[] args) throws IOException {
String path = "application.properties";
System.out.println("firstLineOfFiel(path) = " + firstLineOfFile(path));
}
public class BadBufferedReader extends BufferedReader {
public BadBufferedReader(Reader in, int sz) {
super(in, sz);
}
public BadBufferedReader(Reader in) {
super(in);
}
@Override
public String readLine() throws IOException {
throw new CharConversionException();
}
@Override
public void close() throws IOException {
throw new StreamCorruptedException();
}
}
Exception in thread "main" java.io.StreamCorruptedException
at BadBufferedReader.close(BadBufferedReader.java:28)
at Item9.firstLineOfFile(Item9.java:41)
at Item9.main(Item9.java:47)
디버깅 시 제일 처음 발생한 exception을 확인해야하므로 중요한데, 맨 처음 발생한 exception이 찍히는것이 아닌 가장 마지막에 발생한 Exception이 stack trace에 찍히게 된다.
위의 문제는 java7에서 등장한 try-with-resource를 통해 해결된다.
static String firstLineOfFile(String path) throws IOException {
try(BufferedReader br = new BufferedReader(new FileReader(path))) {//resource block setting
return br.readLine();
}
}
public static void main(String[] args) throws IOException {
String path = args[0];
System.out.println("firstLineOfFiel(path) = " + firstLineOfFile(path));
}
try-with-resource 구문을 통해 exception발생 순서를 확인할 수 있다.
Exception in thread "main" java.io.CharConversionException
at BadBufferedReader.readLine(BadBufferedReader.java:23)
at Item9.firstLineOfFile(Item9.java:38)
at Item9.main(Item9.java:44)
Suppressed: java.io.StreamCorruptedException
at BadBufferedReader.close(BadBufferedReader.java:28)
at Item9.firstLineOfFile(Item9.java:37)
... 1 more
static void firstLineOfFile(String path, String path2) throws IOException {
try(InputStream in = new FileInputStream(path);
OutputStream out = new FileOutputStream(path2)) {//resource block setting
byte[] buf = new byte[8 * 1024];
int n;
while((n = in.read(buf)) >= 0)
out.write(buf, 0,n);
}
}
public static void main(String[] args) throws IOException {
String path = args[0];
String path2 = args[1];
firstLineOfFile(path, path2);
}
resource가 여러개일 경우 try-with-resource구문을 사용하면 try구문안에 사용할 자원을 넣어주면 코드도 깔금해지고 자원이 닫히는것이 보장된다.
try-catch와 함께 예외 발생 상관없이 실행되어야 할 코드를 적을때 사용한다.
try {
//예외 발생 가능성 코드를 적는다.
} catch {
//예외 처리를 위한 문장
} finally {
//예외 발생 여부 상관없이 실핼될 코드
}
finally 블록은 catch에 return문이 있어도 실행된다.
❓ 왜 예외를 두가지로 나눠서 처리하는가?
Unchecked Exceptions — The Controversy (The Java™ Tutorials >
Essential Java Classes > Exceptions)
runntime exception은 프로그래머의 잘못으로 발생하는 예외이다. 호출하는쪽(클라이언트)에서 대처하기에는 힘든 문제이다. runtime exception은 checked exception에 비해 발생 빈도가 높아 runtime exception에대해 예외처리를 한다면 코드의 가시성을 해칠 수 있다.
[참고]
https://www.baeldung.com/java-checked-unchecked-exceptions
https://wisdom-and-record.tistory.com/46