티스토리 뷰

궁금증의 시작

Java Exception에 대해 구글링을 하다가 우연히 아래와 같은 예제 코드를 접하게 되었습니다.

public class TryCatchFinallyTrick {
    public static void main(String[] args) {
        System.out.println("The output is: " getName());
    }

    static int getName(){
        int a = 3;
        try{
            System.out.println("I am try");
            a = 4;
            return a;
        } catch(Exception e){
            System.out.println("I am catch");
            a = 5;
            return a;
        } finally {
            System.out.println("I am finally.");
            a = 6;
        }
    }
}
public class TryCatchFinallyTrick2 {
    public static void main(String[] args) {
        System.out.println("The output is: " getName());
    }

    static String getName(){
        String  name = "a";
        try{
            System.out.println("I am try");
            name = "try";
            return name;
        } catch(Exception e){
            System.out.println("I am catch");
            name = "catch";
            return name;
        } finally {
            System.out.println("I am finally.");
            name = "finally";
            return name;
        }
    }
}
[실행결과]
I am try
I am finally.
The output is: 4
[실행결과]
I am try
I am finally.
The output is: finally

좌측 예제 코드에서 getName()의 결과는 6이라고 생각했습니다.
결과는 4.

finally 블록은 try~catch문이 종료되기 전에 항상 실행된다고 배웠습니다.
그렇다면 a = 6 구문이 실행되어 getName()의 결과는 6이 되지 않을까 생각했습니다.(우측 코드와 유사하게)

그래서 양쪽 코드를 비교해 보니 2가지 차이점을 보였습니다.
  • Primitive Type(int) VS Object Type(String)
  • finally 블록에 return문 여부

먼저 Primitive Type이라서 그런지 검증을 해 보았습니다.
우측 예제 코드에서 finally 블록 안에 있는 return문을 제거하고 실행해 보았습니다.

public class TryCatchFinallyTrick2 {
    public static void main(String[] args) {
        System.out.println("The output is: " getName());
    }

    static String getName(){
        String  name = "a";
        try{
            System.out.println("I am try");
            name = "try";
            return name;
        } catch(Exception e){
            System.out.println("I am catch");
            name = "catch";
            return name;
        } finally {
            System.out.println("I am finally.");
            name = "finally";
            //return name;
        }
    }
}
I am try
I am finally.
The output is: try

좌측 예제 코드와 유사하게 name 값은 “finally”가 될 것 같았으나, “try”가 되었습니다.
즉, Primitive Type과 Object Type의 차이 때문은 아닌 것으로 확인 되었습니다.
그렇다면 finally 블록 안에 return문이 있어 발생하는 차이일까요?

TryCatchFinallyTrcik2 클래스를 컴파일 후 디컴파일을 해 보았습니다.
아래 코드에서 좌측 코드는 java 파일 코드이고, 우측 코드는 디컴파일한 class 코드입니다.

public class TryCatchFinallyTrick2 {
    public static void main(String[] args) {
        System.out.println("The output is: " getName());
    }

    static String getName(){
        String  name = "a";
        try{
            System.out.println("I am try");
            name = "try";
            return name;
        } catch(Exception e){
            System.out.println("I am catch");
            name = "catch";
            return name;
        } finally {
            System.out.println("I am finally.");
            name = "finally";
            //return name;
        }
    }
}
public class TryCatchFinallyTrick2 {
    public TryCatchFinallyTrick2() {
    }

    public static void main(String[] args) {
        System.out.println("The output is: " + getName());
    }

    static String getName() {
        String name = "a";

        String var2;
        try {
            System.out.println("I am try");
            name = "try";
            String e = name;
            return e;
        } catch (Exception var6) {
            System.out.println("I am catch");
            name = "catch";
            var2 = name;
        } finally {
            System.out.println("I am finally.");
            name = "finally";
        }

        return var2;
    }
}

디컴파일한 코드의 try 블록을 보면 e라는 임시변수에 내용을 담아 두었다가 return 하고 있으며, catch 블록 안에 return문이 메서드의 마지막 부분으로 이동하였습니다.
실행 결과가 “finally”가 아니라 “try”가 된 이유는 임시변수 e에 결과를 미리 담아두고 return하기 때문인 것을 확인 할 수 있습니다.

그렇다면 try, catch, finally 블록 안에서 return문은 다른 역할을 할까요?

위 포스트를 확인해 보면 아래와 같이 이야기 하고 있습니다.

  • try 블록 안에 return
    return문은 정상 종료를 의미하므로 finally 블록을 실행 후 정상 종료
  • catch 블록 안에 return
    catch 블록 안에서 return문은 권장하지 않으나, try 블록 안에 return과 동일하게 finally 블록을 실행 후 정상 종료
    catch 블록 안에서 return문을 권장하지 않는 이유는 아래 포스트에서 상세히 확인할 수 있습니다.
    http://lazydeveloper.net/1560573
  • finally 블록 안에 return
    return문은 정상 종료를 의미하기 때문에 finally 블록 안에서 return 될 경우 try 블록 안에서 발생한 Exception은 무시하고 정상 종료
    즉, Exception이 발생하였으나 발생하지 않은 것처럼 종료됨을 의미합니다.

그래서 finally 블록 안에서 return문을 사용하게 되면 try 블록 안에 return문이 실행되어야 하나 finally 블록 안의 return문이 실행되는 것입니다.
정말 그런지 아래와 같이 코드를 작성해 보았습니다.

public class TryCatchFinallyTrick3 {
    public static void main(String[] args) {
        System.out.println("The output is: " getName());
    }

    static String getName(){
        String  name = "a";
        try{
            System.out.println("I am try");
            name = "try";
            return name;
        } catch(Exception e){
            System.out.println("I am catch");
            name = "catch";
            return name;
        } finally {
            System.out.println("I am finally.");
            name = "finally";
            return name;
        }
    }

    static String tryReturn(String result) {
        System.out.println("try return");
        return result;
    }

    static String catchReturn(String result) {
        System.out.println("catch return");
        return result;
    }

    static String finallyReturn(String result) {
        System.out.println("finally return");
        return result;
    }
}
I am try
try return
I am finally.
finally return
The output is: finally

예제를 통해서 finally 블록의 return문이 실행됨을 확인할 수 있습니다.
try 블록에서 Exception이 발생한 경우도 Exception 로그가 출력되지 않을까요?
테스트 코드를 만들어서 테스트 해 보니 정상적으로 출력되었습니다.
(샘플 코드를 잘못 만들어서 그런지 모르겠으나, Exception 로그가 출력되지 않거나 무시 되진 않았습니다.)

public class TryCatchFinallyTrick4 {
    public static void main(String[] args) {
        System.out.println("The output is: " getName());
    }

    static String getName(){
        Exception exception = null;
        String  name = "a";
        try{
            System.out.println("I am try");
            name = "try";
            throw new Exception("Try Exception");
        } catch(Exception e){
            System.out.println("I am catch");
            e.printStackTrace();
            name = "catch";
            return name;
        } finally {
            System.out.println("I am finally.");
            name = "finally";
            return name;
        }
    }
}
I am try
java.lang.Exception: Try Exception
    at tomining.exception.TryCatchFinallyTrick4.getName(TryCatchFinallyTrick4.java:17)
    at tomining.exception.TryCatchFinallyTrick4.main(TryCatchFinallyTrick4.java:8)
I am catch
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
I am finally.
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
The output is: finally
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)


결론

try 블록 안에 return문은 임시 변수에 결과 정보를 담아 두었다가 finally 블록을 실행한 뒤 임시 변수 정보를 반환합니다.
만약 finally 블록 안에서 return을 실행한 경우 try 블록 안에 return문이 무시될 수 있습니다.
따라서 catch나 finally 블록 안에서는 return문을 사용하지 않고 메서드 마지막 부분에서 return문을 사용하도록 합니다.



참고
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/03   »
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
글 보관함