ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • java.lang 패키지 - Object 클래스
    백엔드/자바 2020. 8. 11. 18:19

    Object 클래스

    모든 클래스의 최고 조상 클래스로 Object 클래스의 모든 멤버는 모든 클래스에서 바로 사용이 가능하다.

     

    Object 클래스는 멤버 변수는 존재하지 않고 11개의 메서드만 가지고 있으며

     

    Object의 모든 메서드는 앞서 말했듯 모든 클래스에서 바로 사용이 가능하다.

     

    해당 포스트에선 11개의 메서드 중 가장 많이 쓰이는 3개의 메서드에 대해 다룰 것이다.

    hashCode()

    객체의 해시코드를 반환하는 메서드다.

     

    해시코드란 해싱 함수를 사용한 결과값으로 반환되는 정수값을 의미한다.

     

    hashCode() 메서드는 객체가 가진 메모리상의 주소값을 int로 변환한 해시코드 형태로 반환한다.

     

    즉 풀어서 이야기하자면 객체는 각자 다른 해시코드 값을 가진다.

     

    그리고 hashCode메서드는 그 해시코드 값을 반환하는 역할을 하는 것이다.

     

    코드로 살펴보도록 하자.

     

     

    public class Object_test {
    
        public static void main(String[] args) {
    
            Member p1 = new Member("철수", "학생"); // Member 객체 생성
            Member p2 = new Member("영희", "학생"); // Member 객체 생성
    
            int h1 = p1.hashCode(); // hashCode() 메서드를 이용해서 p1 객체의 해시코드 값 저장
            int h2 = p2.hashCode(); // hashCode() 메서드를 이용해서 p2 객체의 해시코드 값 저장
    
            System.out.println(h1);
            System.out.println(h2);
    
        }
    }
    
    class Member{
    
       String name;
       String job;
    
       Member(String name, String job){
           this.name = name;
           this.job = job;
       }
    }

     

     

    멤버 클래스를 만들고 해당 클래스로 만든 객체 2개의 해시코드값을 출력한 결과물이다.

     

    위와 같이 자바 내에서 각각의 객체는 해시코드라는 고유값을 갖게 된다.

     

    그리고 이 해시코드를 통해 각각의 객체가 같은지 다른지 여부를 판단할 수 있게 된다.

    equals( Object obj )

    객체와 객체끼리 비교할 때 사용하는 메서드다.

     

    예를 들어 A와 B라는 객체를 비교해본다고 가정하자.

     

    equals 메서드를 사용하면 A객체와 B객체의 해시코드값을 비교하여 true/false를 반환한다.

     

    코드로 보면 더 이해하기 쉬울 것이다.

     

     

    public class Object_test {
    
        public static void main(String[] args) {
    
            Member p1 = new Member("철수", "학생"); // Member 객체 생성
            Member p2 = new Member("영희", "학생"); // Member 객체 생성
    
            boolean result = p1.equals(p2); // p1과 p2를 비교한결과를 boolean에 담음
    
            System.out.println(result); // 결과물을 출력함
    
        }
    }
    
    class Member{
    
       String name;
       String job;
    
       Member(String name, String job){
           this.name = name;
           this.job = job;
       }
    }

     

     

    코드와 그 결과물에서 볼 수 있듯이 equals()는 두 객체가 같은지 다른지 판별해서 알려준다.

     

    단, equals() 메서드는 그 한계가 존재한다.

     

    바로 같은 멤버변수 값을 가진 객체라도 다른 객체로 판별한다는 것이다.

     

    앞서 말했듯 equals()는 객체의 해시코드값을 기준으로 두 객체를 판별한다.

     

    그렇기 때문에 객체가 가진 값(멤버변수의 값)이 동일하더라도 해시코드가 다르면 다른 객체로 인식한다.

     

     

     

    예를 들어 위의 코드를 조금 수정해서 두 객체 모두 멤버 변수의 값이 같은 객체를 만들어보자.

     

    그럼 이 두 객체는 과연 같을까? 그림에서 볼 수 있듯이 결과는 false다. 즉 두 객체를 다르게 본다는 것이다.

     

    그럼 이 문제를 어떻게 해결할 수 있을까? equals()메서드를 오버라이딩해서 메서드 내용을 수정하면 된다.

     

     

     

    class Member{
    
       String name;
       String job;
    
       Member(String name, String job){
           this.name = name;
           this.job = job;
       }
    
       @Override
       public boolean equals(Object obj){
    
           // 우선 파라미터로 전달된 객체가 Member 클래스의 객체인지 확인
           if(!(obj instanceof Member)){
               return false;
           }
    
           // 만약 Member클래스의 객체라면 해당 객체를 멤버 클래스 객체로 형변환
           Member m = (Member) obj;
    
           // 만약 해당 메서드를 불러온 객체의 name, job 값과
           // 파라미터로 전달된 객체의 name, job값이 같으면 true 반환
           if(this.name == m.name && m.job == m.job){
               return true;
           } else{
               return false;
           }
       }
    }

     

    위 코드는 Member 클래스 안에 equals를 오버라이딩해서 내용을 수정해준 코드다.

     

    전달된 객체가 Member 클래스 값인지 확인 후 name, job값이 동일하면 true를 반환하게 수정했다.

     

     

     

    그 결과는 위와 같다.

     

    이전에 equals() 메서드를 쓸 때와는 달리 true값을 반환했다.

     

    이렇게 수정하고 나면 해시코드값이 아닌 멤버 변수 값을 비교하게 되므로 true가 출력됨을 알 수 있다.

     

    마지막으로 한 가지 더 짚고 넘어갈 것이 있다.

     

    지금까지 설명한 것처럼 equals()는 기본적으로 두 객체의 해시코드를 비교해서 true/false를 판별한다.

     

    만약 두 객체의 의 값을 비교하고 싶다면 오버라이딩을 통해 메서드를 변경해야 했다.

     

    하지만 이 오버라이딩이 기본적으로 적용된 클래스가 있는데 바로 문자열 String클래스다.

     

     

     

    예를 들어 위와 같은 코드가 있다고 가정해보자. 결과값이 어떻게 나올까?

     

     

     

    바로 true값이 나온다. 그 이유는 앞서 언급했다.

     

    String 클래스는 기본적으로 이미 두 값의 문자열을 비교하도록 오버라이딩되어있기 때문이다.

     

    그래서 String을 사용한 클래스는 해시코드와는 상관없이 문자열을 비교해서 결과를 반환한다.

     

    그 외에 Date, File, wrapper클래스(Integer, Double) 등도 주소값이 아닌 내용을 비교하도록 되있다.

    equals()와 hashCode()

    앞서 equals()와 hashCode()의 의미와 사용법에 대해 설명한 바 있다.

     

    간단히 요약하면 그 내용은 다음과 같다.

     

    - 모든 객체는 고유의 해시코드 값을 가진다.

    - equals() 메서드는 두 객체의 해시코드 값을 비교해서 true/false를 판별한다.

    - 만약 두 객체의 해시코드값이 같다면, 두 객체는 같은 객체로 판별해 true를 반환한다.

    - 즉 equals()의 결과값이 true라면 두 객체는 같다고 할 수 있다.

     

    - 만약 equals() 메서드로 객체별 해시코드가 아닌 객체별 변수값을 비교하려면 오버라이딩을 해야한다.

    - 오버라이딩을 한 equals()는 두 객체의 변수값이 같은 경우 true를 반환한다.

    - 즉 이때도 equals()의 결과값이 true라면 두 객체는 같다고 할 수 있다.

     

    여기에 근거하면 equals() 메서드의 결과가 true라면 두 객체의 해시코드도 같아야 한다는 결론이 나온다.

     

    위에서 만들었던 코드를 다시 한번 보도록 하자.

     

     

    equals() 파트에서 우리가 처음 만든 코드의 p1과 p2 객체의 equals() 메서드 결과값은 false였다.

     

    하지만 우리는 두 객체의 해시코드가 아닌 멤버 변수값을 비교하고 싶어서 equals()를 오버라이딩했다.

     

    그리고 그 결과값은 위 그림과 같이 true였다.

     

    그럼 이번 파트에서 위에 언급한 내용을 다시 한번 살펴보자.

     

     

    과연 우리가 equlas()메서드를 오버라이딩해서 true를 얻어낸 두 객체의 해시값은 같을까?

     

     

     

    당연한 이야기지만 두 객체의 해시코드값을 다르다.

     

    equlas() 메서드를 오버라이딩해서 비교해준건 두 객체의 멤버변수값이지 해시코드값이 아니기 때문이다.

     

    그럼 어떻게 해야할까?

     

    바로 hashCode() 메서드를 오버라이딩해서 내용을 변경해줘야 한다.

     

     

     

    위 코드는 hashCode가 객체 전체의 해시코드를 비교하는 것이 아닌

     

    객체의 멤버변수별로 해시코드를 구해서 둘이 합쳐서 반환하도록 오버라이딩했다.

     

    이제 오버라이딩을 끝냈으니 결과물을 보도록 하자.

     

     

     

    아까와는 달리 두 객체의 해시코드도 동일하고 equals()메서드의 결과도 true로 나왔다.

     

    여기서 얻을 수 있는 결론은 이거다.

     

    만약 특정 클래스 내에서 equals()를 오버라이딩해줬다면, hashCode()도 오버라이딩을 해야한다.

     

    이러한 정의를 가장 잘 보여주는 예가 바로 String 클래스다.

     

     

     

    앞서 String 클래스의 equlas()는 기본적으로 객체의 해시코드가 아닌 문자열을 비교한도록

     

    오버라이딩되있다고 이야기한 바 있다.

     

    그래서 String클래스를 구현한 객체들은 문자열이 같으면 equals()의 값으로 true가 반환된다.

     

    그렇다면 위의 4개의 객체의 해시코드는 어떨까?

     

     

     

    당연한 위에서 이야기한대로 equal()의 값이 true이므로 해시코드도 동일하다.

     

    이는 String클래스의 기본 기능으로서 이미 만들어졌을 때부터 이렇게 구현하도록 오버라이딩된 것이다.

     

     

     

    String클래스가 오버라이드됐다는 사실은 해당 객체들의 실제 해시코드를 확인해보면 알 수 있다.

     

    System.identityHashCode()는 온전히 메모리 주소값을 참조한 해시코드를 반환하는 메서드다.

     

    즉 오버라이드된 hashCode()를 거치지 않은 메모리를 참조한 실제 해시코드 주소를 보여주는데,

     

    확인해보면 알겠지만 모든 객체의 해시코드 값이 같지 않은 것을 볼 수 있다.

     

    (참고로 s1, s2는 해시코드가 같은건 리터럴에 관한 것으로 이후 다룰 String 클래스에서 다룬다.)

     

    내용이 길고 긴데 마지막으로 하나 간단히 정리하자면

     

    equlas 메서드드의 결과값이 true라면 두 객체의 해시코드는 동일해야 한다.

     

    단, equals 메서드의 결과값이 false라도 두 객체의 해시코드는 동일할 수 있다.

     

    이는 64비트 JVM의 문제로 인해 생긴 것으로 두 객체의 해시코드가 동일하더라도

     

    equals()의 값은 꼭 true일 필요는 없다고 기억해두기만 하자.

     

    중요한건 equals()의 값이 true일때, 두 객체의 해시코드는 동일해야 한다는 것이다.

    toString()

    인스턴스에 대한 정보를 문자열(String)로 제공할 목적으로 정의한 것이다.

     

    일반적으로 인스턴스나 클래스에 대한 정보, 인스턴스 변수들의 값을 문자열로 변환하여 반환하도록

     

    오버라이딩하여 사용하는 것이 보통이다.

     

    우선 한번 실행해보자.

     

     

    이전의 예시들과 똑같이 Member 객체를 이용해 toString() 메서드를 사용보았다.

     

    근데 결과값으로 이상한 문자가 찍혀있다.

     

    이 문자의 정체는 '클래스명+@+해당객체의 해시코드를 16진수로 나타낸 정수'다.

     

    근데 이 기본 toString은 별로 쓸모가 없다...

     

    그래서 일반적으로 오버라이딩을 통해 사용해준다.

     

     

     

    위와 같이 오버라이딩을 해주면 된다.

     

    toString으로 확인하려는 것은 객체에 관한 정보인데, 이때 필요한 정보는 개발자마다 다르다.

     

    그래서 굳이 정해진 형태는 없는데 위와 같이 멤버 변수 값을 확인하는데 많이 쓰인다.

     

     

     

    결과를 확인해보면 위와 같이 오버라이딩으로 설정한 내용을 확인할 수 있다.

    '백엔드 > 자바' 카테고리의 다른 글

    java.lang 패키지 - String 클래스 메서드  (0) 2020.08.12
    java.lang 패키지 - String 클래스 속성  (0) 2020.08.12
    예외에서 finally 블럭  (0) 2020.08.11
    예외 선언하기  (0) 2020.08.11
    예외 일으키기  (0) 2020.08.11
Designed by Tistory.