네로개발일기

개발자 네로의 개발 일기, 자바를 좋아합니다 !

'전체 글'에 해당되는 글 194건


반응형

# toString을 항상 재정의하라

- Object.toString() 메서드: [클래스이름]@[16진수로 표시한 해시코드] 포맷을 갖는다.

- toString의 일반 규약에 따르면, 간결하면서 사람이 읽기 쉬운 형태의 유익한 정보를 반환해야 한다.

- toString의 규약은 모든 하위 클래스에서 이 메서드를 재정의하라 라고 하고 있다.

 

## 1. toString을 재정의해야 하는 이유

- toString을 잘 구현한 클래스는 사용하기 편하고, 그 클래스에 대해 디버깅하기 쉽다.

- map 객체를 출력하는 경우 {Jenny=PhoneNumber@addbb}보다는 {Jenny=707-867-5308}이라는 메시지가 가독성이 좋다.

- 실전에서는 toString 메서드를 재작성할 때, 그 객체가 가진 주요 정보를 모두 반환하는 것이 좋다.

- toString을 구현할 때면 반환값의 포맷을 문서화할 지 정해야 한다.

- 값 클래스는 문서화를 권한다.

- 포맷을 명시하면, 그 객체는 표준적이고, 명확하고, 사람이 읽을 수 있게 된다.

- 포맷을 명시하기로 했으면, 명시한 포맷에 맞는 문자열과 객체를 상호전환할 수 있는 정적 팩터리나 생성자를 함께 제공하면 좋다.

- 단, 포맷을 한번 명시하면, 평생 그 포맷에 얽매이게 된다.

- 포맷을 명시하지 않는다면 다음 릴리즈에 포맷을 변경할 수 있는 유연성을 더 가져갈 수 있다.

- 포맷을 명시하든 아니든, 개발자의 의도는 명확히 밝혀야 한다.

- toString이 반환한 값에 대해 포함한 정보를 얻어올 수 있는 API를 제공하자

- toString에 있는 getter를 제공하지 않는다면, 클라이언트에서 toString을 파싱하여 사용할지도 모른다.

 

포맷을 명시하기로 했으면, 명시한 포맷에 맞는 문자열과 객체를 상호전환할 수 있는 정적 팩터리나 생성자를 함께 제공하면 좋다.

 

## 2. toString을 따로 재정의하지 않아도 되는 경우

- 정적 Utils 클래스는 따로 재정의하지 않아도 된다. (객체의 상태(state)를 가지는 클래스가 아니기 때문이다.)

- enum 타입 또한 이미 완벽한 toString을 제공한다.

- 대다수의 컬렉션 구현체는 추상 컬렉션 클래스(AbstractMap, AbstractSet 등)의 toString 메서드를 상속하여 쓴다.

- 라이브러리를 통해 자동생성

- Google의 @Autovalue

- Lombok의 @ToString

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

# equals를 재정의하려거든 hashCode도 재정의하라.

- equals를 재정의한 클래스 모두에서 hasCode도 재정의해야 한다.

일반 규약을 어기게 되어 HashMap이나 HashSet 같은 컬렉션의 원소로 사용할 때 문제가 발생할 수 있다.

 

## 1. Object 명세의 3가지 규약

1. equals 비교에 사용되는 정보가 변경되지 않았다면 객체의 hashCode를 몇 번을 호출해도 항상 같은 값을 반환해야 한다.

2. equals(object)를 통해 두 객체를 비교했을 때 객체가 같다고 판단했다면(true를 반환) 두 객체의 hashCode는 같다.

3. equals(object)가 두 객체를 다르게 판단했다 하더라도 (false를 반환) hashCode가 다를 필요는 없다. (Hash Collision) 단, 다른 객체에 대해서는 다른 값을 반환해야 해시 테이블의 성능이 좋아진다.

 

논리적으로 같은 객체는 같은 hasCode를 반환해야 한다.

 

## 2. hashcode의 동작방법

- 좋은 해쉬 함수라면 서로 다른 인스턴스에 다른 해쉬 코드를 반환한다.

- 주어진 인스턴스들을 균일하게 분배해야 한다. (32비트 정수 범위 내에서)

- 만약 같은 값을 반환한다면, 객체가 해시 테이블 버킷 하나에 담기고, 그 객체들이 연결 리스트처럼 동작한다.

 

## 3. 좋은 hashCode를 작성하는 방법

@Override
public int hashCode() {
	int c = 31;
    
	// 1. int 변수 result를 선언한 후 첫번째 핵심 필드에 대한 hashCode로 초기화한다.
	int result = Integer.hashCode(firstNumber);

	// 2. 기본타입 필드라면 Type.hashCode()를 실핸한다.
	// Type은 기본타입의 Boxing 클래스이다.
	result = c * result + Integer.hashCode(secondNumber);

	// 3. 참조타입이라면 참조타입에 대한 hashCode함수를 호출한다.
	// 4. 값이 null이면 0을 더해준다.
	result = c * result + address == null ? 0 : address.hashCode();

	// 5. 필드가 배열이라면 핵심 원소를 각각 필드처럼 다룬다.
	for(String elem : arr) {
		result += c * result + elem == null ? 0 : elem.hashCode();
	}

	// 6. 배열의 모든 원소가 핵심필드면 Arrays.hashCode를 이용한다.
	result = c * result + Arrays.hashCode(arr);

	// 7. result = 31 * result + c 형태로 초기화하여 result 를 리턴한다.
	return result;
}

- hashCode를 구현했다면 이 메서드가 동치인 인스턴스에 대해 똑같은 해시 코드를 반환할지 Testcase를 작성하자.

-파생 필드는 hashCode 계산에서 제외해도 된다.

- equals 비교에 사용되지 않은 필드는 반드시 제외한다.

- 31 * result를 곱하는 순서에 따라 result 값이 달라진다.

- 곱하는 숫자 31인 이뉴는 31이 홀수이면서 소수(prime)이기 때문이다.

- 31을 이용하면 (i << 5) - i와 같이 최적화할 수 있다.

 

* hashCode를 편하게 만들어주는 모듈

- Objects.hash()

- 내부적으로 auto boxing이 일어나 성능이 떨어진다.

- 입력 인수를 담기 위한 배열 생성으로 속도가 더 느리다.

- Lombok의 @EqualsAndHashCode

- Google의 @AutoValue

 

## 4. hashCode를 재정의할 때 주의할 점 !

- 불변 객체에 대해서는 hashCode 생성 비용이 많이 든다면, hashCode를 캐싱하는 것도 고려하자.

- 스레드 안전성까지 고려해야 한다.

- 성능을 높이고자 hashCode를 계산할 때 핵심 필드를 생략해서는 안된다.

- 속도는 빨라지겠지만, hash 품질이 나빠져서 해시 테이블의 성능을 떨어뜨릴 수 있다. (Hashing Collision)

- hashCode 생성 규칙을 API 사용자에게 공표하지 말자.

- 그래야 클라이언트가 hashCode 값에 의지한 코드를 짜지 않는다.

- 다음 릴리즈 시, 생성을 개선할 여지가 있다.

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

# equals는 일반 규약을 지켜 재정의하라.

## 1. equals를 재정의하면 안 되는 경우

- equals는 재정의하기 쉬워보이지만 곳곳에 함정이 있다. 문제를 회피하는 가장 쉬운 길은 아예 재정의하지 않는 것

 

### a. 각 인스턴스가 본질적으로 고유할 때

- 값 표현 객체가 없을 때(값이 아닌 동작을 표현하는 클래스, Thread가 좋은 예)

- Bean에 등록해두는 객체 repository, controller, service 등이 이에 해당

- DTO, Domain 객체는 값 검증이 필요할 수 있으니 equals를 재정의해야 할 수도 있다. 

 

### b. 인스턴스의 논리적 동치성(logical equality)을 검사할 일이 없을 때

- 논리적 동치성 검사의 예시: java.utils.regex.Pattern의 equals는 내부의 정규표현식이 같은지를 검사하는 메서드

 

### c. 상위 클래스에서 재정의한 equals가 하위 클래스에도 들어맞을 때

- 같은 특징을 가지기 때문에 equals를 상속받아 사용하는 걸 권장

- Set, Map, List의 경우 Abstract(Type)의 equals를 쓴다.

 

### d. 클래스가 private거나 package-private여서 equals 메서드를 호출할 일이 없을 때

- equals가 실수로 호출되는 것을 막고 싶다면

@Override
public boolean equals(Object o) {
    throws new AssertionError(); // equals 호출시 error();
}

 

 

### e. 싱글턴을 보장하는 클래스(인스턴스 통제 클래스, Enum(열거타입)) 인 경우

- 객체간 동등성, 동일성이 보장된다.

 

## 2. equals를 재정의해야 하는 경우

- 객체 식별성(object identity): X - 두 객체가 물리적으로 같은가

- 논리적 동치성(logical equality): O

 

- 객체 식별성이 아니라 논리적 동치성을 확인해야 하는데, 상위 클래스의 equals가 논리적 동치성을 비교하도록 재정의하지 않았을 때 (주로 값 클래스)

public class Fruit {
    private String name; // name이 같을 경우 두 객체는 같다(논리적 동치성)
}

### 2-1. 값 클래스의 equals를 재정의할 때 기대 효과

- 값을 비교

- Map, Set의 원소로 사용가능

 

### 2-2. 값 클래스여도 equals를 재정의할 필요가 없을 때

- 인스턴스 통제 클래스: 값이 같은 인스턴스가 2개 이상 만들어지지 않음 (예. Static Factory Method Pattern, Enum)

 

## 3. Equals 메서드의 규약 - 동치 관계

- 동치 클래스(equivalence class): 집합을 서로 같은 원소들로 이루어진 부분집합으로 나누는 연산

- equals 메서드가 쓸모 있으려면 모든 원소가 같은 동치류에 속한 어떤 원소와도 서로 교환이 가능해야 한다.

 

### 3-1. 반사성(reflexivity)

- null이 아닌 모든 참조 값 x에 대해 x.equals(x)는 true이다.

- 단순히 말하면 객체는 자기 자신과 비교했을 때 같아야 한다는 뜻

- 만약 x.equals(x)가 성립하지 않는 객체라면, 컬렉션에서 contain 메서드를 사용하는 경우 방금 넣은 객체도 찾을 수 없을 것이다.

 

### 3-2. 대칭성(symmetry)

- null이 아닌 모든 참조 값 x, y에 대해 x.equals(y)가 true이면, y.equals(x)가 true를 만족해야 한다.

public final class CaseInsensitiveString {

  private final String s;

  public CaseInsensitiveString(String s) {
    this.s = Objects.requireNonNull(s);
  }

  @Override
  public boolean equals(Object o) {

    if(o instanceof CaseInsensitiveString) {
      return s.equalsIgnoreCase(((CaseInsensitiveString) o).s);
    }
    
    if(o instanceof String) { //한 방향으로만 작동!!
      return s.equalsIgnoreCase((String) o);
    }
    return false;
  }
}
CaseInsensitiveString caseInsensitiveString = new CaseInsensitiveString("Test");
String test = "test";
System.out.println(caseInsensitiveString.equals(test)); //true
System.out.println(test.equals(caseInsensitiveString)); //false

 

- String 클래스에서는 CaseInsensitiveString의 존재를 모르기 때문에 false가 날 수밖에 없는 상황

 

List<CaseInsensitiveString> list = new ArrayList<>();
list.add(new CaseInsensitiveString("Test"));
System.out.println(list.contain("test")); //false or true

 

- 위의 내용을 수정한다면, String과의 비교는 포기해야 한다.

같은 CaseInsensitiveString 타입인 경우에만 비교하도록 한다.

@Override
public boolean equals(Object o) {
    return o instanceof CaseInsensitiveString 
        && ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}

 

### 3-3. 추이성(transitivity) 

- null이 아닌 모든 참조 값 x, y, z에 대해 x.equals(y)가 true이고, y.equals(z)가 true이면 x.equals(z)도 true가 되어야 한다는 조건이다.

- 상위 클래스에서 equals를 재정의했을 경우 equals를 재정의하면 안 된다.

 

#### 문제 1. 대칭성 위배 문제에 빠질 수 있다.

// ColorPoint.java 의 equals
@Override 
public boolean equals(Object o){
    if(!(o instanceof ColorPoint))
        return false;
    return super.equals(o) && ((ColorPoint) o).color == color;
}



public static void main(){
    Point p = new Point(1,2);
    ColorPoint cp = new ColorPoint(1,2, Color.RED);
    p.equals(cp);   // true (Point의 equals로 계산)
    cp.equals(p);   // false (ColorPoint의 equals로 계산: color 필드 부분에서 false)
}

 

 

#### 문제 2. 추이성 위배 문제에 빠질 수 있다.

//ColorPoint.java의 equals
@Override 
public boolean equals(Obejct o){
    if(!(o instanceof Point))
        return false;
    if(!(o instanceof ColorPoint))
        return o.equals(this);
    return super.equals(o) && ((ColorPoint) o).color == color;
}

public static void main(){
    ColorPoint p1 = new ColorPoint(1,2, Color.RED);
    Point p2 = new Point(1,2);
    ColorPoint p3 = new ColorPoint(1,2, Color.BLUE);
    p1.equals(p2);  // true (ColorPoint의 equals 비교 //2번째 if문에서 Point의 equals로 변환)
    p2.equals(p3);  // true (Point의 equals 비교 // x,y 같으니 true)
    p1.equals(p3);  // false (ColorPoint의 equals 비교)
}

 

 

#### 문제 3. 무한 재귀에 빠질 수 있다.

//SmellPoint.java의 equals
@Override 
public boolean equals(Obejct o){
    if(!(o instanceof Point))
        return false;
    if(!(o instanceof SmellPoint))
        return o.equals(this);
    return super.equals(o) && ((SmellPoint) o).color == color;
}

public static void main(){
    ColorPoint p1 = new ColorPoint(1,2, Color.RED);
    SmellPoint p2 = new SmellPoint(1,2); 
    p1.equals(p2);
    // 처음에 ColorPoint의 equals로 비교 : 2번째 if문 때문에 SmellPoint의 equals로 비교
    // 이후 SmellPoint의 equals로 비교 : 2번째 if문 때문에 ColorPoint의 equals로 비교
    // 무한 재귀의 상태!
}

 

- 구체 클래스를 확장해 새로운 값을 추가하면서 equals 규약을 만족시킬 방법은 존재하지 않는다.

- 그렇다고 instanceof 검사 대신 getClass 검사를 하라는 것이 아니다.

 

@Override
public boolean equals(Object o) {
    if (o == null || o.getClass() != getClass())
        return false;
    Point p = (Point) o;
    return x == p.x && y == p.y;
}

 

- 리스코프 치환원칙을 위배한다: Point의 하위 클래스는 정의상 여전히 Point이기 때문에 어디서든 Point로 활용되어야 한다.

- 리스코프 치환 원칙(Liskov Substitution Principle): 어떤 타입에 있어 중요한 속성이라면 그 하위 타입에서도 마찬가지로 중요하다. 따라서 그 타입의 모든 메서드가 하위 타입에서도 똑같이 잘 작동해야 한다.

 

#### 우회 방법 1. 상속 대신 컴포지션을 활용하라

public class ColorPoint {

    private final Point point;
    private final Color color;

    public Point asPoint() {
        return point;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof ColorPoint))
            return false;
        ColorPoint cp = (ColorPoint) o;
        return cp.point.equals(point) && cp.color.equals(color);
    }
}

 

#### 우회 방법 2. 추상 클래스의 하위 클래스 사용하기

- 추상 클래스의 하위 클래스에서는 equals 규약을 지키면서도 값을 추가할 수 있다.

- 상위 클래스를 직접 인스턴스로 만드는 게 불가능하기 때문에 하위 클래스끼리 비교가 가능해진다.

 

### 3-4. 일관성(consistency)

- null이 아닌 모든 참조 값 x, y에 대해 x.equals(y)를 반복해서 호출하면 항상 같은 값을 반환한다.

- 두 객체가 같다면(어느 하나 혹은 두 객체 모두가 수정되지 않는 한) 앞으로도 영원히 같아야 한다.

- 가변 객체 = 비교 시점에 따라 서로 다를 수 있다.

- 불변 객체 = 한 번 다르면 끝까지 달라야 한다.

- equals는 항시 메모리에 존재하는 객체만을 사용한 결정적 계산만 수행해야 한다.

- 클래스가 불변이든 가변이든 equals의 판단에 신뢰할 수 없는 자원이 끼어들면 안 된다. 예) URL과 매핑된 호스트의 IP 주소

 

### 3-5. null 아님

- null이 아닌 모든 참조값 x에 대해, x.equals(null)은 false이다.

- 모든 객체가 null과 같지 않아야 한다.

1. 명시적 null 검사

@Override
public boolean equals(Object o) {
    if (o == null) return false;
    ...
}

 

2. 묵시적 null 검사

@Override
public boolean equals(Object o) {
    if (!(o instanceof MyType)) return false;

    MyType mt = (MyType) o;
    ...
}

 

 

## 4. equals 메서드를 구현하는 4단계

### 4-1. == 연산자를 사용해 입력이 자기 자신의 참조인지 확인

- object identity를 검사한다.

 

### 4-2. instanceof 연산자로 입력이 올바른 타입인지 확인

- 올바른 타입인 경우: equals가 정의된 클래스로 리턴이 되는가

- 올바른 타입이 아닌 경우: 구현한 서로 다른 클래스 간 비교가 가능하게 해야 함.

 

### 4-3. 입력을 올바른 타입으로 형 변환

- 4-2번에서 instanceof 연산자를 사용하였기 때문에 형 변환이 가능함.

 

### 4-4. 입력 객체와 자기 자신의 대응되는 '핵심'필드들이 모두 일치하는지 하나씩 검사

 

## 5. equals 구현할 때 주의사항

### 5-1. 기본 타입과 참조 타입

- 기본 타입: == 연산자 비교

- 참조 타입: equals() 메서드 비교

- 배열 필드: 원소 각각을 지침대로 비교한다. 모두가 핵심 필드라면 Arrays.equals()를 사용한다.

- float, double 필드: Float.compare(), Double.compare() 비교 (부동 소수 값) / Float.equals()나 Double.equals()은 오토 박싱을 수반할 수 있어 성능상 좋지 않다.

 

### 5-2. null 정상 값 취급 방지

- Object.equals()로 비교하여 NullPointException 발생을 예방하자

 

### 5-3. 필드의 표준형을 저장하자.

- 비교하기 복잡한 필드는 필드의 표준형을 저장한 후 비교: 불변 클래스에 제격

 

### 5-4. 필드 비교 순서는 equals 성능을 좌우한다.

- 다를 가능성이 높은 필드 우선

- 비교 비용이 싼 필드 우선

- 핵심 필드 / 파생 필드 구분

 

### 5-5. equals 재정의할 땐 hashCode도 반드시 재정의하자.

 

### 5-6. 너무 복잡하게 해결하려하지 말자.

 

### 5-7. Object 외의 타입을 매개변수로 받는 equals 메서드는 선언하지 말자.

public boolean equals(MyType o) // @Override가 되지 않는다 !

 

### 5-8. AutoValue 프레임워크



 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

💻 OS: mac m1 os

❓Error: bundle install 또는 rails db:create 사용할 때 발생하는 에러

 

 

macOS (m1)에서 bundle install을 할 때 생기는 에러이다. (while bundle install for mysql2 gem ruby on mac)

둘 중에 한 에러가 뜰 수 있다. (나는 둘 다 번갈아서 에러가 발생했다.)

ld: library not found for -lzstd (zstd 라이브러리를 찾지 못함)

ld: library not found for -lssl (ssl 라이브러리를 찾지 못함)

$ bundle install
(생략)
Installing mysql2 0.5.3 with native extensions

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.
current directory:
/Users/jyjeon/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/mysql2-0.5.3/ext/mysql2
/Users/jyjeon/.rbenv/versions/2.6.5/bin/ruby -I
/Users/jyjeon/.rbenv/versions/2.6.5/lib/ruby/2.6.0 -r
./siteconf20210928-15542-ky3uai.rb extconf.rb
--with-ldflags\=-L/opt/homebrew/Cellar/zstd/1.5.0/lib
checking for rb_absint_size()... yes
checking for rb_absint_singlebit_p()... yes
checking for rb_wait_for_single_fd()... yes
-----
Using mysql_config at /opt/homebrew/bin/mysql_config
-----
checking for mysql.h... yes
checking for errmsg.h... yes
checking for SSL_MODE_DISABLED in mysql.h... yes
checking for SSL_MODE_PREFERRED in mysql.h... yes
checking for SSL_MODE_REQUIRED in mysql.h... yes
checking for SSL_MODE_VERIFY_CA in mysql.h... yes
checking for SSL_MODE_VERIFY_IDENTITY in mysql.h... yes
checking for MYSQL.net.vio in mysql.h... yes
checking for MYSQL.net.pvio in mysql.h... no
checking for MYSQL_ENABLE_CLEARTEXT_PLUGIN in mysql.h... yes
checking for SERVER_QUERY_NO_GOOD_INDEX_USED in mysql.h... yes
checking for SERVER_QUERY_NO_INDEX_USED in mysql.h... yes
checking for SERVER_QUERY_WAS_SLOW in mysql.h... yes
checking for MYSQL_OPTION_MULTI_STATEMENTS_ON in mysql.h... yes
checking for MYSQL_OPTION_MULTI_STATEMENTS_OFF in mysql.h... yes
checking for my_bool in mysql.h... no

-----
Don't know how to set rpath on your system, if MySQL libraries are not in path
mysql2 may not load
-----
-----
Setting libpath to /opt/homebrew/Cellar/mysql/8.0.26/lib
-----
(생략)
make "DESTDIR=" clean
(생략)
ld: library not found for -lssl
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [mysql2.bundle] Error 1

make failed, exit code 2
(생략)
An error occurred while installing mysql2 (0.5.3), and Bundler cannot

continue.

In Gemfile:
  mysql2

 

$ rails db:create
linking shared-object mysql2/mysql2.bundle
ld: library not found for -lzstd
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [mysql2.bundle] Error 1

make failed, exit code 2

이런 에러가 발생한다.

해결책은 bundle clean을 해주면 안 되는 경우가 있다. 그럴 경우 --force를 통해 해 준다.

bundle clean --force
brew uninstall mysql
brew install mysql

mysql이 깔려있을 경우 brew를 사용해서 삭제 후 재설치

 

1) ld: library not found for -lssl (ssl 라이브러리를 찾지 못함)

bundle config --local build.mysql2 "--with-cppflags=-I/usr/local/opt/openssl/include"

2) ld: library not found for -lzstd (zstd 라이브러리를 찾지 못함)

bundle config --local build.mysql2 "--with-ldflags=-L/opt/homebrew/Cellar/zstd/1.5.0/lib"

두 명령어 중 하나를 실행해주면 된다.

되지 않을 때는  sudo명령어를 사용해서 실행함.

gem install mysql2 -v '0.5.3' -- --with-opt-dir=$(brew --prefix openssl) --with-ldflags=-L/opt/homebrew/Cellar/zstd/1.5.0/lib

 

라이브러리 위치를 찾지 못해서 생기는 문제 같다.

 

출처

https://stackoverflow.com/questions/67840691/ld-library-not-found-for-lzstd-while-bundle-install-for-mysql2-gem-ruby-on-mac

 

ld: library not found for -lzstd while bundle install for mysql2 gem Ruby on macOS Big Sur 11.4

while running bundle install An error occurred while installing mysql2 (0.5.3), and Bundler cannot continue. Make sure that `gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'` succeeds

stackoverflow.com

 

728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !

반응형

# try-finally 보다는 try-with-resources를 사용하라

## 1. 자원이 닫힘을 보장하는 수단, try-finally의 단점

- 코드 가독성에 있어 지저분하다.

- 두번째 예외가 첫번째 예외를 집어삼켜버러 실제 시스템에서 디버깅을 어렵게한다.

static void copy(String src, String dst) throws IOException {
    InputStream in = new FileInputStream(src);
    try {
        OutStream out = new FileOutStream(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();
    }
}

 

 

## 2. 자원 회수의 최선책 try-with-resources

- AutoCloseable 인터페이스 구현: close() 메서드 하나만 정의

public interface AutoCloseable {
    void close() throws Exception;
}
static void copy(String src, String dst) throws IOException {
    try (InputStream in = new FileInputStream(src);
        OutStream out = new FileOutStream(dst)) {
            byte[] buf = new byte[BUFFER_SIZE];
            int n;
            while((n = in.read(buf)) >= 0) 
                out.write(buf, 0, n);
    } catch (IOException e) {
        return defaultValue;
    }
}

- 읽기 쉽고 문제 진단에 유리하다.

- catch를 이용해 try문을 중첩하지 않고도 다수의 예외 처리가 가능하다.

- 숨겨진 예외도 버려지지 않고, suppressed 꼬리표를 달고 출력된다.

 

readLine()과 close() 호출 양쪽에서 예외가 발생하면, close() 예외는 숨겨지고 readLine()에서 발생한 예외가 기록된다.  



728x90
반응형
blog image

Written by ner.o

개발자 네로의 개발 일기, 자바를 좋아합니다 !