본문 바로가기

(2)

아이템9 - try-finally보다는 try-with-resources를 사용하라

자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다. InputStream, OutputStream, java.sql.Connection 등이 좋은 예다. 자원 닫기는 클라이언트가 놓치기 쉬워서 예측할 수 없는 성능 문제로 이어질 수도 있다. 이런 자원 중 수 상당수가 안전망으로 finalizer를 활용하고는 있지만 finalizer는 그리 믿을만하지 못하다. 전통적으로 자원이 제대로 닫힘을 보장하는 수단으로 try-finally가 쓰였다. 예외가 발생하거나 메서드에서 반환되는 경우를 포함해서 말이다. static String firstLineOfFile(String path) throws IOException { BufferedReader br = new BufferedRea..

Java - extends, implements, abstract 차이

상속이라는 개념을 이론적으로 분명 학습했는데, 잘 안쓰다보니(직접 만들어 쓴적은 없다는 얘기... JpaRepository 인터페이스 같은 것은 자주 사용...) 그 개념을 자꾸 잊어먹어서 implements, extends 심지어 추상 클래스 ,메서드인(명칭만 기억하지 기능은 기억도 안나는 것 같다.. 반성한다) abstract 등 한번 기회 잡아서 다시 정리해야지하며 미뤘는데, 어쩌다보니 스터디원분이 상속 얘기를 꺼내면서, 매우 진땀을 흘렸다. 민망해서 밤에 이불킥 몇 번 하다가 내 선생님인 '구글'을 찾아봤다. 마침 간단하게 잘 요약해둔 블로그가있어서, 옮겨적어본다. 참고: https://velog.io/@hkoo9329/%EC%9E%90%EB%B0%94-extends-implements-%EC%B..

개발서적/이펙티브 자바

아이템9 - try-finally보다는 try-with-resources를 사용하라

728x90

자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다. InputStream, OutputStream, java.sql.Connection 등이 좋은 예다. 자원 닫기는 클라이언트가 놓치기 쉬워서 예측할 수 없는 성능 문제로 이어질 수도 있다. 이런 자원 중 수 상당수가 안전망으로 finalizer를 활용하고는 있지만 finalizer는 그리 믿을만하지 못하다. 전통적으로 자원이 제대로 닫힘을 보장하는 수단으로 try-finally가 쓰였다. 예외가 발생하거나 메서드에서 반환되는 경우를 포함해서 말이다. 

static String firstLineOfFile(String path) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        br.close();
    }
}

위 방식도 나쁘지 않다, 자원을 하나 더 추가해보자! 

static void copy(String src, String dst) throws IOException {
    InputStream in = new FileInputStream(src);
    try {
        OutputStream out = new FileOutputStream(dst);
        try {
            byte[] buf = new byte[BUFFER_SIZE];
            int n;
            while ((n = in.read(buf)) >= 0) {
                out.write(buf, 0, n);
            }
        } finally {
            out.close();
        }
    } finally {
        in.close();
    }
}

try-finally 문을 제대로 사용한 앞의 두 코드 예제에조차 미묘한 결점이 있다. 예외는 try 블록과 finally블록 모두에서 발생할 수 있는데, 예컨데 기기에 물리적인 문제가 생긴다면 firstLineOfFile 메서드 안의 readLine 메서드가 예외를 던지고, 같은 이유로 close 메서드도 실패할 것이다. 이런 상황이라면 두번째 예외가 첫 번째 예외를 완전히 집어삼켜 버린다. 그러면 스택 추적 내역에 첫 번째 예외에 관한 정보는 남지 않게 되어, 실제 시스템에서의 디버깅을 몹시 어렵게 한다. 물론 두 번째 예외 대신 첫 번째 예외를 기록하도록 코드를 수정할 수는 있지만, 코드가 너무 지저분해져서 실제로 그렇게까지 하는 경우는 거의 없다. 

이러한 문제들은 자바 7이 투척한 try-with-resources 덕에 모두 해결되었다. 이 구조를 사용하려면 해당 자원이 AutoCloseable 인터페이스를 구현해야 한다. 단순히 void를 반환하는 close 메서드 하나만 덩그러니 정의한 인터페이스다. 자바 라이브러리와 서드파티 라이브러리들의 수많은 클래스와 인터페이스가 이미 AutoCloseable을 구현하거나 확장해뒀다. 우리들도 닫아야 하는 자원을 뜻하는 클래스를 작성한다면 AutoCloseable을 반드시 구현하도록 하자.

다음은 try-with-resources를 사용해 위의 코드들을 재작성한 예다.

static String firstLineOfFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(
            new FileReader(path))) {
        return br.readLine();
    }
}
static void copy(String src, String dst) throws IOException {
    try (InputStream in = new FileInputStream(src);
            OutputStream out = new FileOutputStream(dst)) {
        byte[] buf = new byte[BUFFER_SIZE];
        int n;
        while ((n = in.read(buf)) >= 0) {
            out.write(buf, 0, n);
        }
    }
}

try-with-resources 버전이 짧고 읽기 수월할 뿐 아니라 문제를 진단하기도 훨씬 좋다. firstLineOfFile 메서드를 생각해보자. readLine 과 close 호출 양쪽에서 예외가 발생하면, close에서 발생한 예외는 숨겨지고 readLine에서 발생한 예외가 기록된다. 이처럼 실전에서는 프로그래머에게 보여줄 예외 하나만 보존되고 여러 개의 다른 예외가 숨겨질 수도 있다. 이렇게 숨겨진 예외들도 그냥 버려지지는 않고, 스택 추적 내역에 '숨겨졌다(suppressed)'는 꼬리표를 달고 출력된다. 또한, 자바7에서 Throwable에 추가된 getSuppressed 메서드를 이용하면 프로그램 코드에서 가져올 수도 있다. 보통의 try-finally에서처럼 try-with-resources에서도 catch 절을 쓸 수 있다. catch 절 덕분에 try문을 더 중첩하지 않고도 다수의 예외를 처리할 수 있다. 아래의 코드에서는 firstLineOfFile 메서드를 살짝 수정하여 파일을 열거나 데이터를 읽지 못했을 때 예외를 던지는 대신 기본값을 반환하도록 해봤다.

static String firstLineOfFile(String path, String defaultVal) {
    try (BufferedReader br = new BufferedReader(
            new FileReader(path))) {
        return br.readLine();
    } catch (IOException e) {
        return defaultVal;
    }
}
더보기
핵심 정리
꼭 회수해야 하는 자원을 다룰 때는 try-finally 말고, try-with-resources를 사용하자, 예외는 없다. 코드는 더 짧고 분명해지고, 만들어지는 예외 정보도 훨씬 유용하다. try-finally로 작성하면 실용적이지 못할 만큼 코드가 지저분해지는 경우라도, try-with-resource로는 정확하고 쉽게 자원을 회수할 수 있다. 

 

728x90
프로그래밍 공부/Java

Java - extends, implements, abstract 차이

728x90

상속이라는 개념을 이론적으로 분명 학습했는데, 잘 안쓰다보니(직접 만들어 쓴적은 없다는 얘기... JpaRepository 인터페이스 같은 것은 자주 사용...) 그 개념을 자꾸 잊어먹어서 implements, extends 심지어 추상 클래스 ,메서드인(명칭만 기억하지 기능은 기억도 안나는 것 같다.. 반성한다) abstract 등 한번 기회 잡아서 다시 정리해야지하며 미뤘는데, 어쩌다보니 스터디원분이 상속 얘기를 꺼내면서, 매우 진땀을 흘렸다. 민망해서 밤에 이불킥 몇 번 하다가 내 선생님인 '구글'을 찾아봤다. 마침 간단하게 잘 요약해둔 블로그가있어서, 옮겨적어본다.  

참고: https://velog.io/@hkoo9329/%EC%9E%90%EB%B0%94-extends-implements-%EC%B0%A8%EC%9D%B4

 

자바 extends, implements 차이

상속이란 (Inheritance) 상속을 말하기 전에 먼저 OOP가 무엇인지 알면 좋을거 같다.OOP(Object-Oriented Programming, 객체 지향 프로그래밍) 이란? OOP의 특징으로 1. 상속과 인터페이스 (계층성) 2. 다형성, 사용편의성 (모듈화) 3. 캡슐화, 정보은닉 4. 자료 추상화 (모델링) 5. 동적 바인딩 위에 링크에는...

velog.io

 

상속(Inheritance)이란 

상속을 말하기전에 먼저 객체 지향 프로그래밍(OOP)의 특징을 알면 좋다.

OOP의 특징은 아래의 5가지이다.

  1. 상속과 인터페이스(계층성)

  2. 다형성, 사용편의성(모듈화)

  3. 캡슐화, 정보은닉

  4. 자료 추상화(모델링)

  5. 동적 바인딩

상속은 아래의 그림을 통해서 이해를 도울 수 있다.

각 상자는 모두 객체(Object)이고, 자바 용어로는 클래스이다.
이러한 계층을 표현하기 위해서 만들어진 것이 상속이다.
하위 객체는 상위 객체(부모)의 특징을 물려받는다.

또 다른 예로 변수또한 객체에 선언되어 있다면 물려받게 된다.
이 메소드 또는 변수를 '구현'하는가 그대로 '사용'하는가에 따라서 상속의 형태가 갈리게 된다.

  1. extends 
    사실상 extends가 상속의 대표적인 형태다.
    부모에서 선언 / 정의를 모두하여 자식은 메소드 / 변수를 그대로 사용할 수 있다.
    '다중상속'을 지원하지 않는다.

  2. Implements (interface 구현)
    부모 객체는 선언만 하며 정의(내용)은 자식에서 오버라이딩(재정의)해서 사용한다.
    (extends가 못하는) 다중상속을 대신해준다.

  3. abstract
    extends와 implements의 혼합, extends하되 몇 개는 추상 메서드로 구현되어 있다.

정리

  1. extends는 일반 클래스와 abstract 클래스 상속에 사용되고, implements는 interface 상속에 사용된다.
  2. class to class, interface to interface 상속은 extends를 사용한다.
  3. class to interface 를 사용할 땐 implements를 써야하고
  4. interface to class 를 사용할 땐 implements를 사용할 수 없다.
  5. extends는 1 개의 상속만 받을 수 있다.
  6. extends 자식 클래스는 부모 클래스의 기능을 사용한다. 
  7. implements는 여러개 사용 가능하다.
  8. implements는 설계 목적으로 구현 가능하다.
  9. implements한 클래스는 implements의 내용을 다 사용해야한다.

 

 

728x90