[Book Review] JAVA의 신 (12)

[Book Review] JAVA의 신 (12)

CH12. 모든 클래스의 부모클래스 Object

Object

모든 클래스에는 부모 클래스가 있다. (java.lang.object)

package c.inheritance;
ㅤ
public class InheritanceObject {
  public static void main(String[] args) throws Exception {
    inheritanceObject object = new InheritanceObject();
    System.out.println(object.toString());
  }
}

아무런 상속을 받지 않으면 java.lang.Object 클래스 (이하 Object클래스)를 확장함
이 클래스가 어떻게 Object클래스를 확장한다는 것을 알 수 있을까?
-> Object 클래스에 있는 메소드 활용

위 예제에서 toString()이라는 메소드 호출 (main메소드 이외에는 메소드 없는데도) 하지만 클래스를 컴파일하는데 실행에 문제가 없음

그렇다면 만약 extends를 사용하여 부모클래스를 상속받을때에는 어떻게 될까?
자바는 한번에 이중상속을 받을 수 없지만 여러단계로 상속 받을 수 있다. 앞 장에서 예제로 사용한 Parent 클래스의 자식인 Child클래스가 있을 때 Parent클래스는 아무런 상속을 받지 않았지만 실제로는 Object클래스의 상속을 받은 것
(Child 클래스는 Object클래스의 자식의 자식)

왜 모든 클래스는 Object 클래스의 상속을 받는가?
이유 : Object클래스에 있는 메소드들을 통해서 클래스의 기본적인 행동을 정의할 수 있기 때문 /클래스라면 이정도의 메소드는 정의되어있어야하고 처리해야한다라는 것을 정의하는 작업이 필요하기 때문

Object 클래스에서 제공하는 메소드 종류
1)두 가지: 객체 처리 위한 메소드 & 2) 쓰레드를 위한 메소드

쓰레드란?
프로그램이 실행되는 작은 단위 중 하나 (어플리케이션 서버처럼 사용자의 요청들을 동시에 받기 위해선 여러 개의 쓰레드가 동작하여야만 정상적으로 동작할 수 있음)

1) 객체 처리를 위한 메소드
순서 : 접근제어자 / 리턴타입 / 메소드이름(매개변수)

  • protected Object clone() : 객체의 복사본을 만들어 리턴
  • public boolean equals(Object obj) : 현재 객체와 매개변수로 넘겨받은 객체가 같은지 확인 / 같으면 true 다르면 false
  • protected void finalize() : 현재 객체가 더 이상 쓸모가 없어졌을 때 가비지 컬랙터(garbage collector)에 의해서 이 메소드가 호출됨, / 신경쓸필요 없음
  • public Class<?> getClass() : 현재 객체의 Class 클래스의 객체를 리턴
  • public int hashCode() : 객체에 대한 해시코드(hash code) 값을 리턴 / 해시코드 = 16진수로 제공되는 객체의 메모리주소
  • public String toString() : 객체를 문자열로 표현하는 값을 리턴

Garbage Collector
자바의 메모리에 있는 쓰레기 청소 (객체 생성하고 그 객체가 쓰인 후 필요어지면 청소)

2) 쓰레드 처리를 위한 메소드

  • public void notify() : 이 객체의 모니터에 대기하고 있는 단일 쓰레드를 깨움
  • public void notifyAll() : 이 객체의 모니터에 대기하고 있는 모든 쓰레드를 꺠움
  • public void wait() : 다른 쓰레드가 현재 객체에 대한 notify() 메소드나 notifyAll() 메소드를 호출할 때까지 현재 쓰레드가 대기하고 있도록함.
  • public void wait(long timeout) : wait() 메소드와 동일한 기능 제공 / 매개변수에 지정한 시간만큼 대기 = 매개변수 시간 넘어섰을 때 현재 쓰레드는 다시 깨어남 / 여기서의 시간은 밀리초 1/1,000초 단위 / 만약 1초간 기다리게 할 경우 1000을 매개변수로 넘겨줌
  • public void wait(long timeout, int nanos) : wait() 메소드와 동일한 기능 제공 / 하지만 wait(time out)에서 밀리초 단위의 대기시간을 기다린다면 이 메소드는 자세한 밀리초 + 나ㅗ초 (1/1,000,000,000초) 만큼만 대기 / 뒤에 있는 나노초의 값은 0~999,999 사이의 값만 지정가능

사용 빈도수 높은 순으로 객체 처리 메소드
toString()
equals()
hashCode()
getClass()
clone()
finalize()

1) toString()
가장 많이 사용 / 해당 클래스가 어떤 객체인지 쉽게 나타낼 수 있는 메소드

이 메소드가 자동으로 호출되는 경우
a) System.out.println() 메소드에 매개변수로 들어가는 경우
b) 객체에 대하여 더하기 연산을 하는 경우

package c.inheritance;
ㅤ
public class ToString {
  public static void main(String args[]) {
    ToString thisObject = new ToString();
    thisObject.toStringMethod(thisObject);
  }
ㅤ
  public void toStringMethod(Object obj) {
    System.out.println(obj)
    System.out.println(obj.toString());
    System.out.println(“plus „+obj); 
  }
}
  • toStringMethod() 메소드의 가장 첫줄 = 객체 그대로 출력
  • toStringMethod() 메소드의 두번째 줄 = toString이 클래스에 선언되어있지 않지만 ToString클래스가 Object 클래스에 상속되어있기 떄문에 toString()메소드 호출 가능
  • toStringMethod() 메소드의 마지막줄 = 객체의 더하기 연산 수행 (참조 자료형 더하기 연산은 String만 가능하다고 했는데? -> String을 제외한 참조자료형에 더하기연산하면 자동으로 toString()메소드 호출되어 객체의 위치에는 String값 놓임

toStringMethod() 좀 더 깔끔하게

public void toStringMethod() {
  System.out.println(this);
  System.out.println(toString());
  System.out.println(„plus“ + this);
}

이렇게 toString()으로 출력된 결과는 무엇인가?

실제 Object클래스에 구현되어있는 toString() 메소드

getClass().getName() + ‘@’ + Integer.toHexString(hashCode())
  • Object 클래스에있는 getClass()의 결과에 getName() 메소드를 부르면 현재 클래스의 패키지 이름과 클래스 이름이 나옴
  • 여기서는 c.inheritance.InheritanceObject가 여기에 속함
  • 그 다음에 at(@)가 붙는다. 그냥 앞의 결과 뒷 결과 구분
  • 마지막 부분에 객체의 해시코드값을 출력 / Object 클래스에 선언되어있는 hashCode() 나중에 / hashCode() 메소드에서 int 타입의 값을 리턴 / 그 값을 클래스에서 제공하는 toHexString()이라는 메소드를 활용하여 16진수로 변환
    (Integer클래스 : int값을 보다 쉽게 처리할 수 있도록 도움주는 클래스)

클래스 이름 & 해시 코드 값 알필요 ?
클래스 만들 떄 toString() 메소드는 직접 구현해야한다 (= Overriding을 적용해야한다)
*Overriding 제대로 구현하기 위해서 : 접근 제어자 / 리턴 타입 / 메소드 이름 / 매개변수타입과 개수 = 모두 동일해야함

ToString클래스의 toString() 메소드를 다음과 같이 Overriding

package c.inheritance;
ㅤ
public class ToString {
  public static void main(String args[]) {
    ToString thisObject = new ToString();
    thisObject.toStringMethod(thisObject);
  }
ㅤ
  public void toStringMethod() {
    System.out.println(this);
    System.out.println(toString());
    System.out.println(„plus“ + this);
  }
ㅤ
  public String toString() {
    return “ToString class”;
  }
}

이 toString()메소드의 선언부를 보면 접근 제어자는 public이고 리턴 타입은 String인 것을 알 수 있다. 이렇게 toString() 메소드를 변경한 후 ToString 클래스를 컴파일하고 다시실행
-> 결과

ToString class
ToString class
plus ToString class

패키지를 포함한 클래스이름과 골뱅이 / hashCode()메소드 수행결과 나오지 않음

마찬가지로 MemberDTO에서도

toString() 없다면

MemberDTO dto = new MemberDTO(“sangmin”, “01011111111”, [email protected]);
System.out.println(“Name=“+dto.name+“ Phone =”+dto.phone+” eMail=”+dto.email);

toString() 추가

public class MemberDTO {
 // 중간생략
  public String toString() {
    return “Name=”+name+” Phone=”+ phone+” eMail=”+email;
  }
}

이렇게 toString()을 Overriding해놓으면 다음과 같이 간단하게 사용가능

MemberDTO dto = new MemberDTO(“sangmin”, “01011111111”, [email protected]);
System.out.println(dto);

이클립스에서는 파일을 저장하면 바로 컴파일 완료 / 버튼 하나 누르면 실행
or Intelli J (인텔리 제이) 유.무료 / 안드로이드 개발에 사용되는 안드로이드 스튜디오(인텔리 제이 기반)




2) equals()
객체는 ==만으로 같은지 확인이 안되기 때문에 equals() 사용
비교 연산자 (==, !=)들은 주소값을 비교한다

package c.inheritance;
ㅤ
public class MemberDTO {
  public String name;
  public String phone;
  public String email;
  // 이하 생략
}

Equals패키지

package c.inheritance;
ㅤ
public class Equals {
  public static void main(String args[]) {
    Equals thisObject = new Equals();
    thisObject.equalMethod();
  }
  public void equalMethod() {
    memberDTO obj1 = new MemberDTO(“sangmin”);
    memberDTO obj2 = new MemberDTO(“sangmin”)’
    if (obj1 == obj2) {
      System.out.println(“obj1 and obj2 is same”);
    } else
      System.out.println(“obj1 and obj2 is different”);
    }
  }
}
  • obj1 obj2 주솟값다름
    속성값 (name “sangmin”, phone = email = null) 은 동일
    그래서 equals()라는 메소드를 통해 두 객체 비교

Overriding하지 않으면 equals()메소드에서는 hashCode()값을 비교

equals Overriding하면

public class MemberDTO {
  public boolean equals(Object obj) {
    if (this == obj) return true; // 주소값 같으므로 true
    if (obj == null) return false; // obj가 null이므로 false
    if (getClass() != obj.getClass()) return false; // 클래스의 종류가 다르므로 false
    ㅤ
    MemberDTO other = (MemberDTO) obj; // 같은 클래스이므로 형변환 실행
ㅤ
---각 인스턴스 변수가 같은지 비교---
ㅤ  
    if (name == null) { 
      // name이 null일 때
      if (other.name != null) return false; 
      // 비교대상의 name이 null이 아니면 false
      } else if (!name.equals(other.name)) return false; 
      // 두 개의 email값 다르면 false
ㅤ
    if (email == null) { 
      if (other.name != null) return false;
      }  else if (!email.equals(other.email)) return false;
ㅤ
    if (phone == null) {
      if (other.phone != null) return false;
      }  else if (!name.equals(other.phone)) return false;
    return true;
  }
}

이클립스에서 자동으로 생성한 equals() 메소드
equals() 메소드를 Overriding 할 때에는 반드시 다섯 조건 만족 (from API)
(1) 재귀(reflexive) : null이 아닌 x라는 객체의 x.equals(x)결과는 항상 true
(2) 대칭(symmetric) : null이 아닌 x와 y객체가 있을 때 y.equals(x)가 true를 리턴했다면, x.equals(y)도 반드시 true를 리턴
(3) 타동적(transitive) : null이 아닌 x,y,z가 있을 때 x.equals(y)가 true를 리턴하고 y.equals(z)가 true를 리턴하면 x.equals(z)는 반드시 true를 리턴
(4) 일관(consistent) : null이 아닌 x와 y가 있을 떄 객첵가 변경되지 않은 상황에서 몇 번 더 호출하더라도 x.equals(y)의 결과는 항상 true이거나 항상 false이어야한다.
(5) null과의 비교 : null이 아닌 x라는 객체의 x.equals(null)결과는 항상 false여야만한다.

  • 주의 : equals()메소드를 Overriding할 땐 hashcode()메소드도 같이 Overriding해야함
  • 이유: equals()메소드를 Overriding해서 객체가 서로 같다고 이야기할 수 있겠지만, 그 값이 같다고 해서 그 객체의 주소값이 같지는 않음
    = equals()메소드의 결과가 true임에도 hascode()메소드의 값은 다르기 때문
public class MemberDTO {
// 생략
ㅤ
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((email == null) ? 0 : email.hashcode());
    result = prime * result + ((name == null) ? 0 : email.hashcode());
    result = prime * result + ((phone == null) ? 0 : email.hashcode());
    return result;
  }
}




3) hashcode()
기본적으로 객체의 메모리 주소를 16진수로 리턴
만약 어떤 두 개의 객체가 서로 동일하다면 hashcode() 값은 무조건 동일해야함
따라서 equals() 메소드를 override하면, hashCode()메소드도 override해서 동일한 결과가 나오도록 해야함

hashcode Overriding 조건 (from API)
(1) 자바 어플리케이션이 수행되는 동안에 어떤 객체에 대해서 이 메소드가 호출될 땐 항상 동일한 int값 리턴 (하지만 자바 실행할때마다 같은 값이어야할 필요 X)
(2) 어떤 두개의 객체에 대하여 equals()메소드를 사용하여 비교한 결과가 true일 경우 두 객체의 hashCode()메소드를 호출하면 동일한 int값 리턴해야함
(3) 두 객체를 equals()메소드를 사용하여 비교한 결과false 리턴했다고 hashCode()메소드를 호출한 int값이 무조건 달라야할 필요 X ( 이 경우 서로 다른 int값 제공하면 hashtable 성능 향상에 도움)

직접 equals() hashCode() 메소드 작성 권장 X

좋은 웹페이지 즐겨찾기