java.lang패키지1 : Object클래스의 메서드(equals(), hashcode(), clone(), getClass())

  • java.lang패키지의 가장 큰 특징 : import할 필요 없다.

Object클래스

  • 가장 최상위 클래스로 모든 크래스의 조상 클래스.

https://kookyungmin.github.io/language/2018/06/11/java_20/




equals() 메서드

  • 두 객체의 주소값이 같은지 비교 : ==연산자사용.
  • 두객체의 내용이 같은지 비교 : equals()사용.
    • 이때 equals()메서드 오버라이딩을 사용한다.




hashCode()메서드

  • equals()만 재정의해서는 안되고 반드시 equals()와 hashCode()를 함께 재정의해야만 부작용이 없다.

어떤 부작용?

  • hashCode : 클래스의 주소값을 변환하여 생성한 객체의 고유한 정수값(실제주소값은 아님)
  • HashSet이나 HashMap등에서 객체를 구별하기 위한 일종의 키값으로 사용함.




equals()와 hashcode()는 항상 함께 쓰기

  • set계열 라이브러리를 쓰지않으면 사실 hashcode를 안 써도 된다.
    • 하지만 내가 아닌 다른 개발자가 set계열을 사용할 수 있으므로 DTO타입인 경우 equals()와 hashcode()를 모두 오버라이딩해주는 것이 좋다
  • 주석은 hashcode를 오버라이딩한 부분이다.
    • hashcode overriding전에는 내가 만든 두 개의 동일한 클래스인 Product를 같은 객체인지 인식하지 못한다.
    • hashcode overriding후에는 Product를 동일한 객체로 인식한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
class Product{
String sn;

public Product(String sn){
this.sn = sn;
}

@Override
public String toString() {
return "Product [sn=" + sn + "]";
}

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

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Product other = (Product) obj;
if (sn == null) {
if (other.sn != null)
return false;
} else if (!sn.equals(other.sn))
return false;
return true;
}
}

public class HashCodeTest {
public static void testString(){
Set<String> set = new HashSet<>();
set.add(new String("Hello"));
set.add(new String("Hello"));
System.out.println(set);
}

public static void testProduct(){
Set<Product> st = new HashSet<>();
st.add(new Product("9877"));
st.add(new Product("9877"));
System.out.println(st);
}

public static void main(String[] args) {
testString();
testProduct();
}

}

//hashcode 주석해재 전
[Hello]
[Product [sn=9877], Product [sn=9877]]

//hashcode 주석해재 후
[Hello]
[Product [sn=9877]]




clone()메서드

  • 자신을 복제해서 새로운 객체를 생성하는 메서드
  • 사용처 : 배열 같은 객체를 clone()하여 원본 훼손 X -> 다양한 테스트 가능.
  • 규칙 :
    1. clone()메서드는 protected 접근제한자를 사용하기 때문에 오버라이딩하지 않고는 외부에서 호출할 수 없다. 외부 호출을 위해 오버라이딩시 public 접근 제한자로 변경하기.
    2. throws CloneNotSupportedException 처리하기.




얕은 복사(shallow copy)와 깊은 복사(deep copy)

  • 기본적으로 clone에서 사용하는 복사 방식은 얕은 복사.

얕은 복사 : 단순히 객채가 가지는 기본형의 값과 객체의 참조값만 복사해서 복제본 생성.

  • 복사된 객체의 인스턴스 변수는 원본 객체의 인스턴스 변수와 같은 메모리 주소를 참조한다.
  • 기본형데이터 : 그대로 복제되어 원본데이터 중 기본형데이터의 값이 바뀐다고해서 clone한 객체의 값이 바뀌지않는다. 연결되어있지않다.
  • 참조형데이터 : 단순히 원본 객체에 대한 레퍼런스값을 복사했기때문에 원본데이터 중 참조형데이터의 값이 변경되면 clone한 객체의 값도 변경된다. 반대로 clone한 참조형데이터를 변경하면 원본 참조형데이터의 값도 변경된다.

깊은 복사 : 얕은 복사 + 참조의 내용을 공유하지 않음

  • 객체를 복사 할 때, 해당 객체와 인스턴스 변수까지 복사하는 방식.
  • 전부를 복사하여 새 주소에 담기 때문에 참조를 공유하지 않는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class Person implements Cloneable{
String name;
int age;
int[] score;

public Person(String name, int age, int[] score){
this.name = name;
this.age = age;
this.score = score;
}

@Override
public String toString() {
return "person [name=" + name + ", age=" + age + ", score=" + Arrays.toString(score) + "]";
}

@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}


}

public class CustomCloneTest {

public static void main(String[] args) throws CloneNotSupportedException {
Person or = new Person("홍길동", 20, new int[]{100,30});

Object cloned = or.clone();
System.out.println("원본과 복제품1 >> "+or+" : "+cloned);
System.out.println("원본과 복제품1의 해쉬코드비교 >> "+System.identityHashCode(or)
+" : "+System.identityHashCode(cloned));

//원본의 기본형데이터 변경
or.age = 77;
System.out.println("원본의 age변경 >> "+or+" : "+cloned);

//원본의 참조형데이터변경
or.score[0] = 3;
System.out.println("원본의 score변경 >> "+or+" : "+cloned);

//클론의 기본형데이터 변경
p1.name = "재재";
System.out.println("클론의 name변경 >> "+or+" : "+p1);

//클론의 참조형데이터변경
Person p1 = (Person) cloned; //다운캐스팅
p1.score[0] = 12;
System.out.println("클론의 score변경 >> "+or+" : "+p1);

}

}

//출력값
원본과 복제품1 >> [name=홍길동, age=20, score=[100, 30]] : [name=홍길동, age=20, score=[100, 30]]
원본과 복제품1의 해쉬코드비교 >> 366712642 : 1829164700

원본의 age변경 >> [name=홍길동, age=77, score=[100, 30]] : [name=홍길동, age=20, score=[100, 30]]
원본의 score변경 >> [name=홍길동, age=77, score=[3, 30]] : [name=홍길동, age=20, score=[3, 30]]
클론의 name변경 >> [name=홍길동, age=77, score=[12, 30]] : [name=재재, age=20, score=[12, 30]]
클론의 score변경 >> [name=홍길동, age=77, score=[12, 30]] : [name=홍길동, age=20, score=[12, 30]]




getClass()메서드

  • JVM은 .class파일을 메모리에 읽어 들인 후 class 타입의 객체로 관리한다.
  • getClass() : 객체가 속한 class의 정보를 알아내는 메서드이다.