ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java 자바] 11. 기본 API 클래스 ② Object 클래스
    Programming/Java 2022. 8. 9. 23:52
    반응형

    11-3. Object 클래스

    - 클래스 선언시 extends 키워드로 다른 클래스를 상속하지 않으면 암시적으로 java.lang.Object 클래스를 상속
    - 최상위 부모 클래스로 필드가 없고 11개의 메소드로만 구성되어 있음

    11-3-1. 객체 비교 (equals())

    public boolean equals(Object obj) {...}

    - 매개 타입이 Object이기 때문에 모든 객체를 매개값으로 대입 가능
    - 두 객체를 비교하여 논리적으로 동등하면 true, 그렇지 않으면 false 리턴
       (논리적으로 동등하다 == 객체가 저장하고 있는 데이터가 동일하다는 것을 의미)
    - Object의 equals() 메소드는 직접 사용되지 않고 하위 클래스에서 재정의하여 논리적으로 동등비교할 때 사용
      (ex. String객체에선 equals()를 번지 비교가 아닌 문자열 비교가 가능하도록 오버라이딩하여 사용)

    [ equals() 메소드 재정의 예시 ]

    객체 간 동일 필드를 가지고 있다면 논리적으로 동등하다고 가정하고 싶을 때

    ① 매개값(비교 객체)이 기준 객체와 동일한 타입의 객체인지 먼저 확인 필요(instanceof)
    ② 비교 객체가 동일 타입이라면 기준 객체 타입으로 강제 타입 변환하여 필드값이 동일한지 검사

    // Member.java
    public class Member {
      public String id;
    	
      public Member(String id) {
        this.id = id;
      }
    	
      @Override
      public boolean equals(Object obj) {
        if(obj instanceof Member) {
          Member member = (Member) obj;
          if(id.equals(member.id)) {
            return true;
          }
        } 
        return false;
      }
    }
    
    //MemberTest.java
    public class MemberTest {
    
      public static void main(String[] args) {
        Member obj1 = new Member("blue");
        Member obj2 = new Member("blue");
        Member obj3 = new Member("red");
    		
        if(obj1.equals(obj2)) {
          System.out.println("동등합니다.");
        } else {
          System.out.println("동등하지 않습니다.");
        }
    		
        if(obj1.equals(obj3)) {
          System.out.println("동등합니다.");
        } else {
          System.out.println("동등하지 않습니다.");
        }
      }
    }
    
    // 출력값
    // 동등합니다.
    // 동등하지 않습니다.

     

    11-3-2. 객체 해시코드 (hashCode())

    - 객체 해시코드: 객체를 식별할 하나의 정수값
    - Object의 hashCode() 메소드는 객체의 메모리 번지를 이용해 해시코드를 만들어 리턴하기 때문에 객체마다 다른 값을 가짐

    - 논리적으로 동등한 객체는 hashcode 규약에 따라 반드시 같은 해시코드 값을 가져야 함
    - 따라서, equals() 재정의를 통해 서로 다르게 생성한 객체를 같다고 정의할 경우, hashcode()도 이에 맞게 재정의 필요


    If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

    - HashSet, HashMap, Hashtable은 동등비교시 아래와 같은 방법으로 두 객체를 비교하기 때문에, 재정의하지 않으면 오류 발생

    [ 해시코드를 재정의하지 않고 equals만 재정의 했을 때 ]

    - number 필드가 같더라도, hashCode()에서 리턴하는 해시코드가 다르기 때문에 다른 식별키로 인식됨
    - 즉, 해시맵에 값을 넣을 때 사용된 해시값과 조회할 때 사용되는 해시값이 다르기 때문에 null 값이 출력됨

    //Key.java
    public class Key {
      public int number;
    
      public Key(int number) {
        this.number = number;
      }
    	
      @Override
      public boolean equals(Object obj) {
        if(obj instanceof Key) {
          Key compareKey = (Key) obj;
          if(this.number == compareKey.number) {
            return true;
          }
        }
        return false;
      }
    }
    
    //KeyTest.java
    public class KeyTest {
      public static void main(String[] args) {
        HashMap<Key, String> hashMap = new HashMap<Key, String>();
    		
        hashMap.put(new Key(1), "Erin");
    		
        String value = hashMap.get(new Key(1));
        System.out.println(value);	// null
      }
    }

    [ 해시코드까지 재정의 ]

    - 리턴하는 해시코드의 값을 number로 바꿔줌으로써, 같은 number 값을 가진 객체는 동일한 해시코드 값을 갖도록 변경

    //Key.java
    public class Key {
      ...
      
      @Override
      public int hashCode() {
        return number;
      }
    }

    11-3-3. 객체 문자 정보 (toString())

    - 객체의 문자 정보(객체를 문자열로 표현한 값)를 리턴
    - 기본적으로 Object 클래스의 toString() 메소드는 "클래스명@16진수해시코드"로 구성된 문자 정보를 리턴
    - Object 하위 클래스는 toString() 메소드를 재정의하여 간결하고 유익한 정보를 리턴하도록 되어 있음

    ex. Date 클래스는 toString() 메소드를 재정의하여 현재 시스템의 날짜와 시간 정보 리턴
          String 클래스는 저장하고 있는 문자열을 리턴
    - 사용자가 만드는 클래스 역시 toString() 메소드를 재정의하여 유용한 정보를 리턴하게 할 수 있음

    - System.out.println() 메소드는 매개값이 기본 타입일 경우 해당 값을 그대로 출력, 객체일 경우 객체의 toString() 메소드를 호출해서 리턴값을 받아 출력하도록 되어 있음

    11-3-4. 객체 복제 (clone())

    protected native Object clone() throws CloneNotSupportedException;

    - 객체 복제: 원본 객체의 필드값과 동일한 값을 가지는 새로운 객체를 생성하는 것
    - 원본 객체를 안전하게 보호하기 위해 복제된 객체를 만들어 신뢰하지 않은 영역으로 넘겨 작업을 진행할 수 있음

    1) 얕은 복제 (thin clone, shallow copy)

    - 단순 필드값을 복사해서 객체를 복제하는 것을 의미
    - 필드가 기본 타입일 경우 값 복사가 일어나고 참조 타입일 경우 객체의 번지가 복사되어 같은 객체를 참조하게 됨
    - 이 경우, 복제 객체를 변경하면 원본 객체 역시 영향을 받게 됨 (기본 타입일 경우에는 원본 값에 영향 없음)

    - Object의 clone() 메소드는 자신과 동일한 필드값을 가진 얕은 복제된 객체를 리턴

    ❗️ clone() 메소드로 객체를 복사하려면 원본 객체는 반드시 Cloneable 인터페이스를 구현하고 있어야 함
    - 클래스 설계자가 복제를 허용한다는 의도적 표시
    - 해당 인터페이스가 없는 클래스 복사 시도시에 CloneNotSupportedException 예외 발생
    - clone()은 Clonable 인터페이스의 추상메소드로 작동하기 때문에 사용하는 곳에서 오버라이딩 필요
    //Member.java
    public class Member implements Cloneable{
      public String id;
      public String password;
    	
      public Member(String id, String password) {
        this.id = id;
        this.password = password;
      }
    	
      @Override
      public Member clone() {
        try {
          return (Member) super.clone();
        } catch (CloneNotSupportedException e) {
          return null;
        }
      }
    }
    
    //MemberTest.java
    public class MemberTest {
      public static void main(String[] args) {
        Member member1 = new Member("Erin", 1234);
    		
        Member cloned = member1.clone();
        cloned.password = 2345;
    		
        System.out.println(member1.password);	// 1234
        System.out.println(cloned.password);	// 2345
    		
      }
    }

    2) 깊은 복제 (deep clone, deep copy)

    - 참조하고 있는 객체도 복제
    - 깊은 복제를 위해서는 Object의 clone() 메소드를 재정의하여 참조 객체를 복제하는 코드를 직접 작성해야 함

    package object3_clone;
    
    import java.util.Arrays;
    
    public class Member implements Cloneable{
      public String id;
      public int password;
      public int[] scores;
      public Car car;
    	
      public Member(String id, int password, int[] scores, Car car) {
        this.id = id;
        this.password = password;
        this.scores = scores;
        this.car = car;
      }
    	
      @Override
      public Member clone() {
        try {
          // 기본 필드 얕은 복제
          Member cloned = (Member) super.clone();
          // 배열 깊은 복제
          cloned.scores = Arrays.copyOf(this.scores, this.scores.length);
          // 객체 깊은 복제
          cloned.car = new Car();
          return cloned;
    			
        } catch (CloneNotSupportedException e) {
          return null;
        }
      }
    }

    11-3-5. 객체 소멸자 (finalize())

    - GC는 참조하지 않는 배열이나 객체를 힙 영역에서 자동으로 삭제
    - 객체 소멸 직전에 마지막으로 객체의 소멸자(finalize())를 실행시킴
    - 객체가 소멸되기 전에 마지막으로 사용했던 자원(데이터 연결, 파일 등)을 닫고 싶거나 중요 데이터를 저장하고 싶으면
       Object의 finalize()를 재정의하여 사용 가능
    - finalize()의 경우, JVM에 의해 자동 실행되는 메소드이기 때문에 호출 시점이 명확하지 않음
    - 따라서, 일반 메소드로 명시적으로 호출하는 것이 좋은 방법 

    반응형

    댓글

Designed by Tistory.