Object Class

이주오·2021년 8월 5일
0

java

목록 보기
1/6
post-thumbnail

Object 클래스


  • 모든 클래스의 최고 조상이기 때문에 Object 클래스의 멤버들은 모든 클래스에서 바로 사용 가능
  • 멤버변수는 없고 오직 11개의 메서드만 가지고 있다.
    • 모든 클래스의 조상인 Object 클래스의 메서드들은 대부분 네이티브 메서드이다.

Native Method란

  • 네이티브 메서드는 JVM이 설치된 OS의 메서드를 말한다.
  • 네이티브 메서드는 보통 C언어로 작성되어 있는데, 자바에서는 메서드의 선언부만 정의하고 구현은 하지 않는다.
  • Java Method와 Native Function을 Mapping하는 역활은 JNI(java native interface)가 한다.

주요 메서드


  • protected Object clone() : 객체 자신의 복사본을 반환한다.
  • public boolean equals(Object obj) :객체 자신과 객체 obj가 같은 객체인지 알려준다.
  • protected void finalize() : 객체가 소멸될 때 가비지 컬렉터에 의해 자동적으로 호출된다.
    • 이 때 수행되어야하는 코드가 있을 때 오버라이딩 한다.
    • finalize 예측이 불가능하고, 위험하며 대부분 불필요하다
    • Deprecated
  • public Class getClass() : 객체 자신의 클래스 정보를 담고 있는 Class 인스턴스를 반환한다.
  • public int hashCode() : 객체 자신의 해시코드를 반환한다.
  • public String toString() : 객체 자신의 정보를 문자열로 반환한다.
  • public void notify() : 객체 자신을 사용하려고 기다리는 쓰레드를 하나만 깨운다.
  • public void notifyAll() : 객체 자신을 사용하려고 기다리는 모든 쓰레드를 깨운다.
  • public void wait(), wait(long timeout), wait(long timeout, int nanos)
    - 다른 쓰레드가 notify()나 notifyAll()을 호출할 때까지 현재 쓰레드를 무한히 또는 지정된 시간(timeout, nanos)동안 기다리게 한다.
    - timeout: 천 분의 1초, nanos는 10^9분의 1초

Clone()


  • 자신을 복제하여 새로운 인스턴스를 생성하는 일을 한다
  • clone() 사용법
    • 복제할 클래스가 Cloneable 인터페이스를 구현한 클래스에서만 clone() 호출 가능
    • 그리고 접근제어자를 protected에서 public으로 변경
    • 마지막으로 조상클래스의 clone()을 호출하는 코드가 포함된 try - catch문 작성
      class Poketmon implements Cloneable {
          String name;

          Poketmon(String name) {
              this.name = name;
          }

          @Override
          public Object clone() {
              Object obj = null;
              try {
                  obj = super.clone();  // clone()은 반드시 예외처리를 해주어야 한다.
              } catch(CloneNotSupportedException e) {}
              return obj;
          }
      }

      class CloneEx {
          public static void main(String[] args){
              Poketmon original = new Poketmon(3, 5);
              Poketmon copy = (Poketmon)original.clone(); // 복제(clone)해서 새로운 객체를 생성
              System.out.println(original);
              System.out.println(copy);
          }
      }
    Poketmon{name='메타몽'}
    Poketmon{name='메타몽'}

공변 반환타입(covariant return type)

  • jdk 1.5부터 추가된 기능
  • 오버라이딩할 때 조상 메서드의 반환타입을 자손 클래스의 타입으로 변경을 허용하는 것
  • 이처럼 조상의 타입이 아닌, 실제로 반환되는 자손 객체의 타입으로 반환할 수 있어서 번거로운 형변환이 줄어드는 장점이 존재.
    @Override
    public Poketmon clone() {
    		Object obj = null;
    		try {
    			obj = super.clone();
    		} catch(CloneNotSupportedException e) {}
    		return obj;
    }
    // Poketmon copy = (Poketmon)original.clone();
    Poketmon copy = original.clone();

얕은 복사와 깊은 복사

  • clone()은 단순히 객체에 저장된 값을 그대로 복제할 뿐, 객체가 참조하고 있는 객체까지 복제하지는 않는다.
  • 기본형 배열인 경우에는 아무런 문제가 없지만, 객체배열을 clone()으로 복제하는 경우에는 원본과 복제본이 같은 객체를 공유하므로 완전한 복제라고 보기 어렵다.
    • 이러한 복제를 얕은 복사라고 한다, shallow copy
    • 얕은 복사는 원본을 변경하면 복사본도 영향을 받는다.
  • 반면에 원본이 참조하고 있는 객체까지 복제하는 것을 깊은 복사, deep copy
    - 깊은 복사에서는 원본과 복사본이 서로 다른 객체를 참조하기 때문에 원본의 변경이 복사본에 영향을 미치지 않는다.

getClass()


  • 자신이 속한 클래스의 Class 객체를 반환하는 메서드
  • Class 객체는 이름이 'Class'인 클래스 객체이다.
  • 다음과 같이 정의
    public final class Class implements ... { ... }
  • 클래스 객체는 클래스의 모든 정보를 담고 있으며, 클래스 당 1개만 존재
  • 클래스 파일이 '클래스 로더(ClassLoader)'에 의해 메모리에 올라갈 때, 자동으로 생성
    • 먼저 기존에 생성된 클래스 객체가 메모리에 존재하는지 확인하고
    • 있으면 클래스 객체의 참조를 반환
    • 없으면 클래스 패스에 지정된 경로를 따라서 클래스 파일을 찾는다.
      • 못찾으면 ClassNotFoundException 발생
      • 찾으면 해당 클래스 파일을 읽어서 Class 객체로 변환
  • 즉 파일 형태로 저장되어 있는 클래스를 읽어서 Class 클래스에 정의된 형식으로 변환하는 것
  • 따라서 클래스 파일을 읽어서 사용하기 편한 형태로 저장해 놓은 것이 클래스 객체이다.

Class 객체를 얻는 방법

  • 클래스의 정보가 필요할 때, 먼저 Class 객체에 대한 참조를 얻어 와야 하는데, 해당 Class 객체에 대한 참조를 얻는 방법으로 여러 가지가 있다.
    // case1: 생성된 객체로부터 얻는 방법
    Class cObj = new Poketmon().getClass(); 

    // case2: 클래스 리터럴(*.class)로 부터 얻는 방법
    Class cObj = Poketmon.class;
     
    // case3: 클래스 이름으로 부터 얻는 방법
    // 특히 클래스 파일, 예를 들어 데이터베이스 드라이버를 메모리에 올릴 때 주로 사용
    Class cObj = Class.forName("Poketmon");
  • 클래스 객체를 이용하면 클래스에 정의된 멤버의 이름이나 개수 등, 클래스에 대한 모든 정보를 얻을 수 있기 때문에 Class 객체를 통해서 객체를 생성하고 메서드를 호출하는 등 보다 동적인 코드 작성 가능
    Poketmon p = new Poketmon();                  // new 연산자를 이용한 객체 생성
    Poketmon p = Poketmon.class.newInstance();    // Class객체를 이용한 객체 생성

toString()


목적 : 인스턴스에 대한 정보를 문자열로 제공하기 위해

  • 일반적으로, toString 메소드는 this 객체를 "텍스트로 표현"한 문자열을 리턴합니다. 결과는 간결하면서도 충분한 정보를 담고 있어야 하며, 사람이 읽기 쉬운 형태여야 합니다.
  • 대부분의 경우 인스턴스 변수에 저장된 값들을 문자열로 표현
  • Object class의 toString()
    public String toString() {
    	return getClass().getName()+"@"+Integer.toHexString(hashCode());
    }
  • 가능하다면 toString 메소드는 객체 내의 중요 정보를 전부 담아 반환해야 한다.
  • toString이 반환하는 문자열의 형식을 명시하건 그렇지 않건 간에, 어떤 의도인지는 문서에 분명하게 남겨야 한다.
  • toString이 반환하는 문자열에 포함되는 정보들은 전부 프로그래밍을 통해서 가져올 수 있도록 하라.
    • toString 반환값을 파싱해서 쓰는 일이 없도록 한다. toString 구현이 바뀌면 그런 코드는 다 못 쓰게 된다

equals(Object obj)


목적 : 물리적으로 다른 메모리에 위치하는 객체여도 논리적으로 동일함을 구현하기 위해

  • 두 객체의 같고 다름을 참조변수의 값으로 판단
  • 즉 두 참조변수에 저장된 주소값이 같은지를 판단하는 기능
    public boolean equals(Object obj) {
    	return (this == obj)
    }
  • 클래스의 인스턴스변수 값으로 객체의 같고 다름을 비교하게 하고 싶다면 오버라이딩을 하면된다
    • String class의 equals 메서드도 오버라이딩을 통해 String 인스턴스가 갖는 문자열 값을 비교하도록 되어 있다.
    • String, Date, File, wrapper 클래스(Integer, Double 등)
  • equals 메소드는 null이 아닌 객체 참조들에 대한 동치 관계를 구현하며, 동치 관계의 조건은 null이 아닌 참조 x, y, z에 대하여,
    - 반사관계: x.equals(x)는 true여야 한다.
    - 대칭관계: y.equals(x)가 true이면, x.equals(y)도 true여야 한다.
    - 추이관계: x.equals(y)가 true이고, y.equals(z)도 true이면, x.equals(z) 또한 true여야 한다.
    - 일관성: equals 비교에 필요한 정보가 수정되지 않았다면, x.equals(y)를 여러 차례 실행한 결과는 일관성 있게 true만 리턴하거나 false만 리턴해야 한다.
    - x.equals(null)은 false여야 한다.

실제 위반되는 경우

  • 실제 Timestamp 객체와 Date 객체를 같은 컬렉션에 보관하거나 섞어 쓰면 문제가 생길 수 있다고 한다.
  • 대칭관계 위반
    // Date Class
    public boolean equals(Object obj) {
        return obj instanceof Date && getTime() == ((Date) obj).getTime();
    }
    // Timestamp class
    public boolean equals(Timestamp ts) {
        if (super.equals(ts)) {         // java.util.Date.equals
            if  (nanos == ts.nanos) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    public boolean equals(java.lang.Object ts) {
      if (ts instanceof Timestamp) {
        return this.equals((Timestamp)ts);
      } else {
        return false;
      }
    }
  • date.equals(timestamp) : date 기준으로 시간이 같은지만 검사하므로 true가 된다.
  • timestamp.equals(date) : date는 Timestamp의 인스턴스가 아니므로 false가 된다. 인스턴스 검사를 하지 않고 date가 timestamp로 형변환이 가능하다 쳐도, nanos 검사에서 false가 나올 수 밖에 없다.

hashCode()


목적: 객체의 동일성을 위해 구현

  • 해싱기법에 사용되는 해시함수를 구현한 것이다.
    • 인스턴스가 저장된 가상머신의 주소를 10진수로 반환
  • 오버라이딩 된 hashCode()는 세가지 조건을 만족해야 한다.
  1. 변경되지 않은 한 객체의 hashCode 메소드를 호출한 결과는 항상 똑같은 integer 값이어야 한다.
    - 객체가 변경됐더라도 equals 메소드가 참고하는 정보가 변경되지 않았다면 hashCode 값은 달라지지 않는다.
  2. equals 메서드를 이용한 비교에 의해서 true를 얻은 두 객체에 대해 각각 hashCode()호출해서 얻은 결과는 반드시 같아야 한다.
    - f1 f2의 equals 메서드의 결과가 true라면 hashCode1, 2의 값은 같아야 한다
          Fruit f1 = new Fruit("apple", 10);
          Fruit f2 = new Fruit("apple", 10);

          boolean b = f1.equals(f2);

          int hashCode1 = f1.hashCode();
          int hashCode2 = f2.hashCode();

  1. 그러나 equals() 메소드가 다르다고 판별한 두 객체의 hashCode 값이 반드시 달라야 하는 것은 아니다.
    • 해시 테이블 성능이 향상을 위해 같지 않은 객체들이 각기 다른 hashCode 값을 가지면 좋다.
    • 서로 다른 객체에 대해서 해시코드값이 중복되는 경우가 많아질수록 해싱을 사용하는 컬렉션의 검색속도가 떨어진다.
  • 따라서 hashCode()메서드는 equals() 메서드와 밀접한 관계가 있다.
    • 2번 조건에 위배되지 않기 위해

참고 및 출처

https://atoz-develop.tistory.com/
https://yoonemong.tistory.com/193
https://github.com/castello/javajungsuk3
https://docs.oracle.com/en/java/javase/11/docs/api/index.html

profile
동료들이 같이 일하고 싶어하는 백엔드 개발자가 되고자 합니다!

0개의 댓글