본문 바로가기
☕ Java 웹 프로그래밍/Java

[프로그래머스] Java(자바) 중급 | Part 1. Object 클래스

by 일단연 2023. 5. 10.

 Object와 오버라이딩 

 

java.lang 패키지

  • Java에서 가장 기본적인 동작을 수행하는 클래스들의 집합
  • java.lang 패키지의 클래스들은 import 문을 사용하지 않아도 클래스 이름만으로 바로 사용할 수 있음

 

java.lang.Object 클래스

  • 모든 클래스의 최상위 클래스
  • 아무것도 상속받지 않으면 자동으로 Object를 상속
  • Object가 가지고 있는 메소드는 Java의 모든 클래스에서 다 사용할 수 있음
  • Object 클래스는 필드를 가지지 않으며, 총 11개의 메소드만으로 구성되어 있음

 

Object 클래스의 메소드 11개

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

 

Object 클래스의 메소드 중 자주 사용하는 메소드 3개

  • String toString( ) 메소드
    • 해당 인스턴스에 대한 정보를 문자열로 리턴
    • 이때 반환되는 문자열은 클래스 이름과 함께 구분자로 '@'가 사용되며, 그 뒤로 16진수 해시 코드(hash code)가 추가됨
    • 16진수 해시 코드 값은 인스턴스의 주소를 가리키는 값으로, 인스턴스마다 모두 다르게 리턴됨
    • 해당 메소드를 오버라이딩하면 인스턴스 주소를 가리키는 값이 아니라, 객체가 가지고 있는 필드 값을 받을 수 있음
  • boolean equals(Object obj) 메소드
    • 해당 인스턴스를 매개변수로 전달받는 참조 변수와 비교(같은 값인지 아닌지)해, 그 결과를 리턴
    • 이때 참조 변수가 가리키는 값을 비교하므로, 서로 다른 두 객체는 언제나 false를 리턴
    • 객체 간에 비교를 하려면 기준이 필요. Object 클래스가 equals 메소드만 제공하고 기준은 제공하지 않으니, 메소드를 오버라이딩해서 기준을 정하는 건 프로그래머의 역할.
    • equals( ) 메소드는 Object 클래스가 가진 메소드를 오버라이딩한 것. String 클래스가 가진 equals( ) 메소드를 사용했을 때 안에 들어있는 문자열들이 다 같으면 같다고 리턴.
  • int hashCode( ) 메소드
    • 해당 객체의 해시코드 값을 리턴
    • 해시코드 값은 객체별로 다른 게 좋음
    • 특히 자료구조에서 많이 사용됨
  • 자주 쓰는 equals( ) 메소드와 hashCode( ) 메소드 예시
public class Student {
  String name;
  String number;
  int birthYear;
	
  public static void main(String[] args) {
    Student s1 = new Student();
    s1.name = "홍길동";
    s1.number = "1234";
    s1.birthYear = 1995;
		
    Student s2 = new Student();
    s2.name = "홍길동";
    s2.number = "1234";
    s2.birthYear = 1995;
		
    System.out.println(s1);

    if(s1.equals(s2)) {
      System.out.println("s1 == s2");
    } else {
      System.out.println("s1 != s2");
    }
		
    System.out.println(s1.hashCode());
    System.out.println(s2.hashCode());
  }
}
  • 결과
    • javaStudy2.part1.Student@170861
      s1 != s2
      366712642
      1829164700

 

Object 클래스의 메소드를 오버라이딩하기

  • toString( ) 메소드, equals( ) 메소드, hashCode( ) 메소드 오버라이딩하기
    • Object 클래스의 메소드는 사용자가 알맞게 오버라이딩해서 사용해야 함
    • toString( ) 메소드 오버라이딩
      • 인스턴스 주소를 가리키는 값이 아니라, 객체가 가지고 있는 필드 값을 받을 수 있음
        • s1과 s1.toString( )의 값이 같음
          • 객체를 출력하면 내부적으로 toString( ) 메소드를 자동으로 호출해 출력함
    • equals( ) 메소드 오버라이딩
      • 객체 안에 들어있는 문자열들이 다 같으면 true로 리턴
    • hashCode( ) 메소드 오버라이딩
      • 객체 안에 든 문자열들이 다 같을 때 똑같은 해시코드 값을 리턴
public class Student {
  String name;
  String number;
  int birthYear;

  @Override
  public String toString() {
    return "Student [name=" + name + ", number=" + number + ", birthYear=" + birthYear + "]";
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((number == null) ? 0 : number.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    //메소드에 매개변수로 들어온 obj와 자기 자신을 비교
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    //getClass()는 oObject가 갖고 있는 메소드
    //클래스의 정보를 갖고 있으며, 클래스라는 객체를 반환
    //자기 자신의 클래스 정보와 obj의 클래스 정보를 비교
    //서로 다른 클래스들끼리 값을 비교할 필요가 없어짐
    if (getClass() != obj.getClass())
      return false;
    //obj를 Student로 형변환
    Student other = (Student) obj;
    if (number == null) {
      if (other.number != null)
        return false;
    //자신의 number와 obj의 number를 비교
    } else if (!number.equals(other.number))
      return false;
    return true;
  }
	
  public static void main(String[] args) {
    Student s1 = new Student();
    s1.name = "홍길동";
    s1.number = "1234";
    s1.birthYear = 1995;
		
    Student s2 = new Student();
    s2.name = "홍길동";
    s2.number = "1234";
    s2.birthYear = 1995;
		
    System.out.println(s1);

    if(s1.equals(s2)) {
      System.out.println("s1 == s2");
    } else {
      System.out.println("s1 != s2");
    }
		
    System.out.println(s1.hashCode());
    System.out.println(s2.hashCode());
  }
}
  • 결과
    • Student [name=홍길동, number=1234, birthYear=1995]
      Student [name=홍길동, number=1234, birthYear=1995]
      s1 == s2
      1509473
      1509473

 

메소드 오버라이딩하는 방법

  • 이클립스의 Source 탭 > Generate 메소드명 클릭 (도구가 자동으로 만들어줌)

  • 비교할 기준과 어디에 오버라이딩할지 선택
    • Insertion point는 Source탭으로 들어오기 전에 마우스 커서로 미리 지정할 수 있음

  •  

 

Object 클래스의 메소드 나머지 8개

  • protected Object clone( ) 메소드
    • 해당 인스턴스를 복제하여, 새로운 인스턴스를 생성해 리턴
    • 하지만 Object 클래스의 clone() 메소드는 단지 필드의 값만을 복사하므로, 필드의 값이 배열이나 인스턴스면 제대로 복제할 수 없음
    • 따라서 이러한 경우에는 해당 클래스에서 clone() 메소드를 오버라이딩하여, 복제가 제대로 이루어지도록 재정의해야 함
    • 이러한 clone() 메소드는 데이터의 보호를 이유로 Cloneable 인터페이스를 구현한 클래스의 인스턴스만이 사용할 수 있음
    import java.util.*;
    
    class Car implements Cloneable {
      private String modelName;
    ① private ArrayList<String> owners = new ArrayList<String>();
    
      // modelName의 값을 리턴
      public String getModelName() { return this.modelName; }
       // modelName의 값을 설정              
      public void setModelName(String modelName) { this.modelName = modelName; }
    
    // owners의 값을 리턴
      public ArrayList getOwners() { return this.owners; }
    // owners의 값을 추가
      public void setOwners(String ownerName) { this.owners.add(ownerName); }
    
      public Object clone() {
        try {
    ②     Car clonedCar = (Car)super.clone();
    ③     // clonedCar.owners = (ArrayList)owners.clone();
          return clonedCar;
    ④   } catch (CloneNotSupportedException ex) {
          ex.printStackTrace();
          return null;
        }
      }
    }
    
    public class Object03 {
      public static void main(String[] args) {
    ⑤   Car car01 = new Car();
        car01.setModelName("아반떼");
        car01.setOwners("홍길동");
    ⑥   System.out.println("Car01 : " + car01.getModelName() + ", " + car01.getOwners() + "\\n");
    
    ⑦   Car car02 = (Car)car01.clone();
    ⑧   car02.setOwners("이순신");
    ⑨   System.out.println("Car01 : " + car01.getModelName() + ", " + car01.getOwners());
    ⑩   System.out.println("Car02 : " + car02.getModelName() + ", " + car02.getOwners());
      }
    }
    
  • protected void finalize( ) 메소드
    • 해당 객체를 더는 아무도 참조하지 않아 가비지 컬렉터가 객체의 리소스를 정리하기 위해 호출
  • Class<T> getClass( ) 메소드
    • 해당 객체의 클래스 타입을 리턴
  • void notify( ) 메소드
    • 해당 객체의 대기(wait)하고 있는 하나의 스레드를 다시 실행할 때 호출
  • void notifyAll( ) 메소드
    • 해당 객체의 대기(wait)하고 있는 모든 스레드를 다시 실행할 때 호출
  • void wait( ) 메소드
    • 해당 객체의 다른 스레드가 notify( )나 notifyAll( ) 메소드를 실행할 때까지 현재 스레드를 일시적으로 대기(wait)시킬 때 호출
  • void wait(long timeout) 메소드
    • 해당 객체의 다른 스레드가 notify( )나 notifyAll( ) 메소드를 실행하거나 전달받은 시간이 지날 때까지 현재 스레드를 일시적으로 대기(wait)시킬 때 호출
  • void wait(long timeout, int nanos) 메소드
    • 해당 객체의 다른 스레드가 notify()나 notifyAll() 메소드를 실행하거나 전달받은 시간이 지나거나 다른 스레드가 현재 스레드를 인터럽트(interrupt) 할 때까지 현재 스레드를 일시적으로 대기(wait)시킬 때 호출

 

실습

  • 문제 설명
    • Car클래스는 name과 number라는 필드를 가집니다. Car클래스를 문자열로 바꾸면 다음과 같은 형식이 되도록 `toString`메소드를 오버라이드 해 보세요.
          
       `name: [이름], number: [번호]`
          
      예를 들어 name의 값이 "Car"이고, number가 1234이라면, 해당 오브젝트의 문자열 값은 `"name: Car, number: 1234"`와 같이 되어야 합니다.
  • Car 클래스
public class Car{
  String name;
  int number;
    
  //toString을 오버라이드 해 보세요.
  //return 형식은 아래줄을 참고하세요: 
  //"name: " + name + ", number: " + number;
  public String toString(){
    return "name: " + name + ", number: " + number;
  } 
}
  • CarExam 클래스
//아래는 실행을 위한 코드입니다. 수정하지 마세요.
public class CarExam{
  public static void main(String[]args){
    Car ex = new Car();
  }
}