Object Class 8개 메서드


boolean equals(Object obj) 
String toString() 
int hashCode() 
protected void finalize() 
protected Object clone() 
final void notify(), final void notifyAll() 
final void wait(), final void wait(long timeout), final void wait(long timeout, int nanos) 
final Class getClass()


자바의 모든 클래스들은 Object 클래스를 상속받고 있습니다. 보이진 않지만, 디폴트(Default)로 상속을 한 것입니다. 그렇게 되면 상속을 받은 하위 클래스(Child Class)는 상위 클래스(Parent Class)의 메소드(Method)도 같이 상속을 받게 되는 것입니다. 따라서, 모든 클래스는 Object클래스를 상속받으므로 Object클래스의 8개 메소드를 모두 사용할 수 있습니다. 이 메소드들은 기능이 엄청납니다. 잘 사용할줄 알면 많이 편하고 코딩의 생산성도 높을 수 있을 것이고 프로그램 코딩의 효율성도 높일 수 있습니다.


#1 String toString()
 : 객체의 가장 중요한 정보(객체 타입, 참조값)를 리턴하는 메서드

toString메서드는 기본적으로 객체의 정보를 리턴하지만, 오른쪽 예제처럼 원하는 정보를 얻도록 재정의할수도 있습니다. 자바의 클래스들 중에는 이미 편의상 재정의된 String, Date..등이 있습니다.(아래소스는 이해만되도록 대충 적었습니다.)

클래스를 만든 후 그대로 toString메서드를 사용하면 클래스의 정보를 반환합니다.

class Toy extends Object{ ... }
main(){
   Toy t = new Toy();
   System.out.println(t.toString());
}






----------------출력
Toy @12498b5
클래스를 만든후 toString메서드를 재정의 해서 원하는 정보를 가져올 수 있습니다.

class Toy { 
   private int id;
   private String name;
   public String toString() {
      return name + " " + id;
   }
}
main() {
   Toy e = new EBook(1000, "노인과 바다");
   System.out.println(e.toString());
}

----------------출력
노인과 바다 1000




#2 boolean equals(Object obj)

: 객체를 비교하는 메서드(==)

equals메서드는 사용하는 방법을 제대로 알아야 합니다. equals = "==" 와 같은 말입니다. 하지만, == 와 같은 기능을 하도록 사용자가 재정의 해주어야 합니다. 물론, 이미 재정의된 String, Wrapper(Character, Integer..), Date, File..등의 클래스가 있습니다.
equals메서드를 그대로 사용하면 객체의 참조값만 비교해서 결국, 서로다른 주소를 비교하는 것이다.

class Toy extends Object{
   public static void main(String[] args){
      Toy t1 = new Toy();
      Toy t2 = new Toy();
      System.out.println(a1 == a2);
      System.out.println(a1.equals(a2));
   }
}








----------------출력
false
false
equals메서드를 재정의 해서 사용하면, 같은 객체를 구별할 수 있는 원하는 방식으로 만들수 있습니다.

class Toy extends Object{
   private int id; 
   private String name; 
   public boolean equals(Object obj) {
      if(obj instanceof Toy) {      //instanceof는 객체 확인
         Toy eo =(Toy)obj;            //downcasting
         return (this.id == eo.id);
      }
      return false;
   }

   public static void main(String[] args){
      Toy a1 = new Toy(10, "wow");
      Toy a2 = new Toy(10, "wow2"); 
      System.out.println(a1.equals(a2)); 
   }
}

----------------출력
true




#3 int hashCode()
: 객체가 보유한 유일한 참조값을 10진수로 리턴합니다. new를 사용해 생성했다면 중복 참조값은 없습니다. 해싱코드는 가상머신이 동적으로 부여합니다.

위에서 구현한 Toy클래스의 toString()을하면 아래와 같습니다.
코드: System.out.println(a1.toString)          =>        출력: Toy @12498b5

toString()의 원형은 다음과 같습니다. hashCode()를 16진수로 변환해서 출력하고 있네요..
                                  public String toString() {
                                     return getClass().getName() + "@" + Integer.toHexString(hashCode()); 
                                  } 




#4 protected void finalize()
: 해당 객체의 메모리가 수거되기 직전에 가비지 콜렉터에 의해서 자동으로 호출되는 메서드입니다.

사실 Object클래스의 finalize메서드는 하는일이 하나도 없답니다. 그런데 이 finalize를 하는 이유는 최상위부터 최하위까지 상속받은 모든 메모리를 정리하려고 사용합니다.
(만약에 class Object / class father extends Object / class Toy extends father 이라면, 아래와 코드가 같을 때, 상속받은 father, Objcet클래스의 finalize까지 모드 호출됩니다.) 


class Toy extends Object{ 
    protected void finalize() throws Throwable { 
    //작업생략
    super.finalize(); 
    } 
}


finalize는 자바의 가비지 콜렉터에 의해 자동으로 호출되지만, System.gc를 사용해 가비지 콜렉터를 수동으로 불러서 쓸수도 있습니다. 메모리를 중간에 정리해줄수 있습니다.
public class FinalManMain{ 
    //작업생략
    protected void finalize() throws Throwable, IOException{ 
       if (this.fw != null)
          this.fw.close();  
       super.finalize(); 

    } 
   public static void main(String[] args) throws Exception{
   {                                                   //단순히 블록만 지정start
      FinalMan fm = new FinalMan("finalize.dat");
      fm.writeData("안녕하세요");
      fm.writeData("Hello");
      fm = null;                                    //참조값을 쓰지 않겠다고 보장한다.(이게 수집대상으로 만드는 방법이다)
   }                                                  //단순히 블록만 지정end (일부러 main이 끝날때까지 fm.close를 하지 않았다)
   System.gc();                                 //가비지 콜렉터 구동 : 객체의 참조값이 더이상 없을 경우! 자동으로 구동!
   System.out.println("프로그램 종료");
   }
}

아.. 그리고 가비지 콜렉터(Finalizer)는 스레드인데, 이 스레드가 finalize() 메서드를 호출하는 것입니다.
이 구문을 사용하면 실행하는 메서드가 속해있는 스레드의 참조값을 출력합니다.
   Thread t = Thread.currentThread();
   System.out.println("여기 메서드의 스레드: " + t );         
출력값을 예를 들면,
   [메서드]에서...   Thread[스레드이름, 우선권, 스레드그룹명] 
   main에서는.....   Thread[main,5,main]
   Thread에서는..   Thread[Finalizer,8,system]
으로 출력됩니다.
 



#5 protected native Object clone()
: 메모리를 복사하는 메서드 ( JAVA는 복사할 때 참조값만 복사해서.. )
: "native" 는 자바로 구현하기 어려운 부분을 C나 C++로 구현하려할 때 사용하는 키워드

▨ 방법1 Cloneable 인터페이스(얕은 복사)
class Mimic extends Object implements Cloneable
       public Object clone() throws CloneNotSupportedException{    //protected이기 때문에 상속받아 사용한다.
       return super.clone();    
    } 
}
Mimic n = (Mimic)m.clone();

▨ 방법2 Cloneable 인터페이스를 직접 구현
방법1처럼 복사를 하는 방법이 일반적으로 메모리를 복사하긴 하지만, 미리 구현된 객체들의 clone함수 중에는 얕은 복사를 하는 경우가 있습니다.
예를 들면,
   Vector v = new Vector(); 
   v.addElement(new String("testmsg"));
   v.addElement(new String("testmsg2"));
   Vector s = (Vector)v.clone(); 
를 하면, 벡터 클래스는 clone복사를 하면, 그 안의 String객체는 참조값만 복사가 됩니다. 결국 속에 있는 원소들까지 복사하려면 직접 구현해주어야 합니다.
그럴때는, clone 메서드와, super.clone메서드를 사용해서 직접 구현해야 합니다.




#6,7 final void wait(), final void notify()
 앞에서 봤던 Thread(스레드)와 synchronized(동기화)는 아주 밀접한 관계입니다. 공유자원을 동기화했을 때, 하나의 메서드가 공유자원을 쓰는동안 lock을 걸고, lock이 풀리면, 그때 다른 메서드가 그 공유자원에 lock을 걸어 사용합니다.

 앞에서는 sleep()을 사용해서 스레드의 순서를 조절했었습니다. 그보다 더 효율적으로 Thread를 synchronized할수 있는 방법이 바로 wait()와 notify()입니다.

 sleep은 지정된 시간동안 공유자원을 lock ! =>   시간동안 스레드를 NotRunnable로 !
 wait를 불르면 공유자원을 바로 lock !         =>   스레드를 NotRunnable로 !
 notify를 불르면 공유자원을 바로 unlock !   =>    스레드를 Runnable로 !

 wait과 notify는 공유자원을 사용할 때 한 스레드를 기다리게 만들고, 그 스레드가 풀리면, 다음 순서의 스레드에게 알립니다. 
 notifyAll()은 공유자원을 사용할 때 한 스레드가 사용중이고, 그 스레드가 풀리면, 다른 모든 스레드에게 알립니다.
 효율적으로 프로그램을 만들기 위해서 둘을 제대로 알아야 합니다.^_^

예,

비디오 방에서 비디오를 빌리려고 합니다.
다음 사람이 비디오를 빌리려고하는데 없으면, 그 사람(그 스레드)은 wait 상태가 됩니다.
비디오가 모두 나가고, 다른 스레드가 다시 반납하면, 반납을 알리고, 다음 사람(다음 스레드) 다시 빌려갑니다.
import java.util.*;
class VideoShop{ 
   private Vector buffer = new Vector();
   public VideoShop(){                     //비디오 방에는 비디오 2개뿐입니다.
      buffer.addElement("비디오1");
      buffer.addElement("비디오2");
   }
   public synchronized String lendVideo() throws InterruptedException{
      Thread t = Thread.currentThread();
      if(buffer.size()==0){
         System.out.println(t.getName() + ": 대기 상태 진입");
         this.wait();                                                    //비디오 없음 : 이때 메서드는 기다림..
         System.out.println(t.getName() + ": 대기 상태 해제");
      }
      String v = (String)this.buffer.remove(buffer.size()-1);
      return v;
   } 
   public synchronized void returnVideo(String video){ //비디오 반납 : 반납 알림
      this.buffer.addElement(video);
      this.notify();
   }
}

class Person extends Thread{
   public void run(){
      try{ 
         String v = GoodVideoStoreKeeperMain.vShop.lendVideo(); 
         System.out.println("대여");
         System.out.println("보는중");
         this.sleep(1000);                   //비디오를 1초동안 보고 반납합니다.(이때, 다음 사람이 와서 비디오를 빌리려고
                                                      하면, 없으면 그 Thread는 wait이 되고, 있으면 바로 빌립니다.

         System.out.println("반납");     //비디오를 반납하면 그 메서드 Thread는 sleep을 그만하고,  다음 메서드에게
                                                      알립니다.

         GoodVideoStoreKeeperMain.vShop.returnVideo(v);
      }catch(InterruptedException e){e.printStackTrace();}
   }
}

public class GoodVideoStoreKeeperMain{
   public static VideoShop vShop = new VideoShop();
   public static void main(String[] args){ 
      Person p1 = new Person();
      Person p2 = new Person();
      Person p3 = new Person();
      Person p4 = new Person(); 
      p1.start();                         //메서드 스레드1
      p2.start();                         //메서드 스레드2      
      p3.start();                         //메서드 스레드3
      p4.start();                         //메서드 스레드4  
   }
}



#8 final Class getClass() 
: 가상머신이 할 작업을 직접 할 수 있도록 만드는.. 클래스 정보를 가져오는.. 메서드?

class Data { ... } 

 만약, 위에서 만든 Data 클래스를 사용하려고하면, 가상머신이 Data.class를 로딩해야 합니다. 그리고 그 후에, 그 객체를 생성하거나 메서드를 호출할수 있습니다. 하지만!, Data.class를 내가 가지고 있다면 어떨까요?

                       가상머신의 객체 생성              Class 클래스를 이용한 객체 생성
public class DataClass{
   public static void main(String[] args){
      Data d = new Data();
      System.out.println(d);
   }
}

public class UserClass{
   public static void main(String[] args) throws InstantiationException, IllegalAccessException{ 
      Class c = Data.class; 
      Object obj = c.newInstance();

      System.out.println(obj);
   }
}


public class ReflectClass{
   public static void main(String[] args) throws InstantiationException, IllegalAccessException{
      Data d = new Data();
      Class c = d.getClass();
      Object obj = c.newInstance();

      System.out.println(obj);
   }
}

 Data d = new Data()라고 했을 때 가상머신이 알아서 자동으로 .class 파일 로딩과 객체 생성을 해주지만, Class 클래스를 이용하면 가상머신에게 일을 맡길 필요가 없습니다. 가상머신이 하는 작업을 프로그래머가 직접 할 수 있도록 해주는 것이 바로 Class 클래스입니다.

 위 테이블에서 ②번과 같은 프로그램 기법을 리플렉션(Reflection)기법이라고 합니다.
-일반적인 프로그램 기법 : 클래스를 이용해서 객체를 생성 
-리플렉션(Reflection)을 이용한 프로그램 기법 : 객체를 이용해서 클래스를 얻는다. 


이렇게 해서 만약 프로그래머 자신이 가상머신이 하는 작업을 대부분 흉내낼 수 있다면 가상머신의 로직과 프로그램의 밑바닥을 아는 것과 마찬가지입니다.