네로개발일기

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

'dev book/Effective Java'에 해당되는 글 27건


반응형

# 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

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

반응형

# 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

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

반응형

finalizer와 cleaner 사용을 피해라

## 1. 자바의 객체 소멸자

- finalizer: 예측할 수 없고, 상황에 따라 위험할 수 있어 일반적으로 불필요하다. 오동작, 낮은 성능, 이식성 문제의 원인이다. **"쓰지 말자"** 자바 9에서는 사용 자제(deprecated) API로 지정

- cleaner: finalizer보단 덜 위험하지만, 여전히 예측할 수 없고, 느리고, 일반적으로 불필요하다.

 

* C++의 파괴자(destructor): 특정 객체와 관련된 자원을 회수하는 보편적인 방법 (Java의 try-with-resources, try-finally)

* Java의 가비지 컬렉터: 접근할 수 없게된 객체를 회수

 

## 2. finalizer와 cleaner 사용을 피해야 하는 이유

### 1) 즉시 수행된다는 보장이 없다.

객체에 접근할 수 없게 된 이후부터 실행되기까지 얼마나 걸릴지 알 수 없다. 제때 실행되어야 하는 작업은 절대 할 수 없다.   

수행 시점이 전적으로 GC 알고리즘에 달렸으며, 구현 방식에 따라 천차만별이다.     

finalizer 쓰레드는 다른 애플리케이션보다 우선순위가 낮다. 

 

### 2) 수행 여부도 보장하지 않는다.

접근할 수 없는 객체에 딸린 종료작업을 수행하지 못한 채 프로그램이 중단될 수도 있다. 

상태를 영구적으로 수정하는 작업에서 절대 finalizer나 cleaner에 의존해서는 안된다.   

System.gc, System.runFinalization 메서드에 현혹되지 말자. 실행 가능성은 높여주나 보장해주진 않는다.     

 

### 3) finalizer 동작 중 발생한 예외가 무시되며, 처리할 작업이 남아있어도 그 순간 종료된다.

잡지 못한 예외로 객체가 덜 마무리된 상태로 남아있을 수 있다. 훼손된 객체를 사용하려 할 때 예측할 수 없다.   

 

### 4) finalizer와 cleaner은 심각한 성능문제도 동반한다.

AutoCloseable 객체를 생성해 GC 수거시간: 12ns   

finalizer 수거시간: 550ns -> GC의 효율을 떨어뜨린다.    

 

### 5) finalizer 공격에 노출되어 심각한 보안 문제를 일으킬 수도 있다.

생성자나 직렬화 과정에서 예외가 발생하면 생성되다 만 객체에서 악의적인 하위 클래스의 finalizer가 수행될 수 있다.    

객체 생성을 막으려면 생성자에서 예외를 던지면 되는데, finalizer가 있으면 그렇지도 않다.     

final이 아닌 클래스를 finalizer 공격으로부터 방어하려면 아무 일도 하지 않는 finalize 메서드를 만들고 final로 선언하자.

 

## 3. finalizer, cleaner의 대안

AutoClosable을 구현하고, 클라이언트에서 인스턴스를 다 쓰면 close 메서드를 호출한다. 예외가 발생해도 잘 종료되도록 try-with-resources를 사용한다.

 

## 4. finalizer, cleaner의 쓰임새

### 1) 자원의 소유자가 close메서드를 호출하지 않는 것에 대비한 안전망 역할

안전망 역할의 finalizer를 작성할 때는 그럴만한 값어치가 있는지 심사숙고하자.    

자바 라이브러리의 일부 클래스는 안전망 역할의 finalizer를 제공한다: FileInputStream, FileOutputStream, ThreadPoolExecutor   

 

### 2) 네이티브 피어와 연결

네이티브 피어(native peer): 일반 자바 객체가 네이티브 메서드를 통해 기능을 위임한 네이티브 객체. 자바 객체가 아니라 가비지 컬렉터는 그 존재를 알지 못한다.      

성능 저하를 감당할 수 있고 네이티브 피어가 심각한 자원을 가지고 있지 않을 때에만 해당된다. 성능 저하를 감당할 수 없거나 네이티브 피어가 사용하는 자원을 즉시 회수해야 한다면 close 메서드를 사용하자.   

728x90
반응형
blog image

Written by ner.o

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