Object Class
Object클래스는 모든 클래스의 최고 조상이기 때문에 Object클래스의 멤버들은 모든 클래스에서 바로 사용 가능하다.
Object 클래스는 멤버변수는 없고 오직 11개의 메서드만 가지고 있다.
equals(Object obj)
매개변수로 객체의 참조변수를 받아서 비교하여 그 결과를 boolean값으로 알려주는 역활을 한다.
서로 다른 두 객체는 equals메서드로 비교하면 항상 false를 결과로 얻게 된다.
equals 메서드는 주소값으로 비교를 하기 때문에, 변수의 value가 같을지라도 변수의 주소값이 다르다면 false를 반환한다.
Class Value {
int value;
public Value(int value) {
this.value = value;
}
}
Value v1 = new Value(10);
Value v2 = new Value(10);
v1.equals(v2) // false
v2 = v1; // v2에 v1 주소값 저장
v1.equals(v2) // true
저장된 값으로 비교
만약 주소값이 아닌 저장된 값으로 비교를 원할 경우 equals 메서드를 오버리이딩해서 비교하면 된다.
Class Value {
int value;
public Value(int value) {
this.value = value;
}
@Overriding
public boolean equals(Object obj) {
if (obj != null && obj instanceof Value) {
return id == ((Value)obj).id;
} else {
return false;
}
}
}
String클래스의 equals()
String클래스 역시 Object클래스의 equals메서드를 그대로 사용하는 것이 아니라 위 처럼 오버라이딩을 통해 String인스턴스가 갖는 문자열 값을 비교하도록 되어있다.
그래서 String클래스의 equals메서드는 저장된 값만 일치하면 true값을 얻는 것이다.
hashCode()
해싱(hashing)기법에 사용되는 '해시함수(hash function)'를 구현한 것이다.
해싱은 데이터 관리기법 중의 하나인데 다량의 데이터를 저장하고 검색하는데 유용하다.
해쉬함수는 찾고자하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시코드(hach code)를 반환한다.
위에서 살펴본 것과 같이 클래스의 인스턴스변수 값으로 객체의 같고 다름을 판단해야하는 경우라면 equals메서드 뿐 만아니라 hashCode메서드도 적절히 오버라이딩 해야한다.
같은 값이라면 hashCode메서드를 호출했을 때의 결과값인 해시코드도 같아야 하기 때문이다.
만일 hashCode메서드를 오버라이딩 하지 않는다면 Object클래스에 정의된 대로 모든 객체가 서로 다른 해시코드값을 가질 것이다.
String str1 = new String("abc");
String str2 = new String("abc");
str1.hachCode(); // 96354
str2.hachCode(); // 96354
System.identityHashCode(str1); // 27134973
System.identityHashCode(str2); // 1284394
String클래스는 equals메서드와 마찬가지로 hashCode메서드가 오버라이딩 되어있기 때문에 hashCode()를 호출하면 항상 동일한 해시코드값을 얻는다.
반면에 System.identityHashCode(Object x)는 Object클래스의 hashCode메서드처럼 객체의 주소값으로 해시코드를 생성하기 때문에 항상 다른 해시코드값을 반환한다.
toString()
인스턴스에 대한 정보를 문자열(String)로 제공할 목적으로 정의한 것
클래스를 작성할 때 toString()을 오버라이딩하지 않는다면, 클래스이름에 16진수 해시코드를 얻게된다.
Class Value {
int value;
public Value(int value) {
this.value = value;
}
}
Value value = new Value(10);
value.toString(); // Value@103e94f
String 값으로 정보를 반환하려면 toString()을 오버라이딩 후 호출해야한다.
String클래스는 마찬가지로 toString메서드가 오버라이딩 되어있어 저장된 값으로 반환할 수 있다.
Class Value {
int value;
public Value(int value) {
this.value = value;
}
@Overriding
public String toString() {
return "value : " + value;
}
}
Value value = new Value(10);
value.toString(); // value : 10
String str = new String("KOREA");
str.toString(); // KOREA
clone()
clone메서드는 자신을 복제하여 새로운 인스턴스를 생성한다.
먼저 복제할 클래스가 Cloneable인터페이스를 구현해야하고, clone()을 오버라이딩하면서 접근 제어자를 protected에서 public으로 변경한다.
그래야만 상속관계가 없는 다른 클래스에서 clone()을 호출 할 수 있다.
Cloneable인터페이스가 구현되어 있다는 것은 클래스 작성자가 복제를 허용한다는 의미이다.
공변 반환타입(covariant return type)
JDK1.5부터 '공변 변환타입(covariant return type)'이라는 것이 추가되었는데, 이 기능은 오버라이딩할 때 조상 메서드의 변환 타입을 자손 클래스의 타입으로 변경을 허용하는 것이다.
clone() 호출해 클래스를 복제하였을때 return type은 Object클래스 형태이기 때문에 복제한 클래스로 형변환(Casting)을 해야한다.
class Point implements Cloneable { // Cloneable 인터페이스 구현
int x;
int y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point clone() { // 반환 타입을 Object에서 Point로 변경(공변 반환타입)
Object obj = null;
try {
obj = super.clone(); // clone()은 반드시 예외처리를 해주어야 한다.
} catch(CloneNotSupportedException e) { }
return (Point)obj; // Point타입으로 형변환
}
}
Point origin = new Point(3, 5);
Point copy = origin.clone(); // 복제
얕은 복사와 깊은 복사
clone()은 단순히 객체에 저장된 값을 그대로 복제할 뿐, 객체가 참조하고 있는 객체까지 복제하지는 않는다.
객체배열을 clone()으로 복제하는 경우 원본과 복제본이 같은 객체를 공유하므로 완전한 복제라고 보기 어렵다.
이러한 복제(복사)를 '얕은 복사(shallow copy)'라고 하며, 깊은 복사는 원본과 복사본이 서로 다른 객체를 참조하기 때문에 원본의 변경이 복사본에 영향을 미치치 않는다.
class Circle implements Cloneable {
Point p;
double r;
public Circle shallowCopy() { // 얕은 복사(Point는 복제되지 않는다.)
Object obj = null;
try {
obj = super.clone();
} catch(CloneNotSupportedException e) { }
return (Circle)obj;
}
public Circle deepCopy() { // 깊은 복사(Point까지 복제)
Object obj = null;
try {
obj = super.clone();
} catch(CloneNotSupportedException e) { }
Circle c = (Circle)obj;
c.p = new Point(this.p.x, this.p.y);
return c;
}
...
}
getClass()
자신이 속한 클래스의 Class객체를 반환하는 메서드이다.
Class객체는 클래스의 모든 정보를 담고 있으며, 클래스당 단 1개만 존재한다.
클래스 파일이 '클래스 로드(ClassLoader)'에 의해서 메모리에 올라갈 때, 자동적으로 생성된다.
클래스 로드는 파일 형태로 저장되어 있는 클래스를 읽어서 사용하기 편한 형태(Class클래스에 정의된 형식)로 변환한다.
Class객체를 얻는 방법
클래스의 정보가 필요할 때, 먼저 Class객체에 대한 참조를 얻어 와야 하는데, 해당 Class객체에 대한 참조를 얻는 방법은 여러가지 있다.
Class cObj = new Card().getClass(); // 생성된 객체로 부터 얻는 방법
Class cObj = Card.class; // 클래스 리터럴(*.class)로 부터 얻는 방법
Class cObj = Class.forName("Card"); // 클래스 이름으로 부터 얻는 방법
동적으로 객체를 생성하고 메서드를 호출하는 방법에 대해 더 알고 싶다면, '리플렉션 API(reflection API)'로 검색하면 된다.
final class Card {
String kind;
int num;
Card() {
this("SPADE", 1);
}
Card(String kind, int num) {
this.kind = kind;
this.num = num;
}
@Overriding
public String toString() {
return kind + ":" + num;
}
}
Card c = new Card("HEART", 3); // new 연산자로 객체 생성
Card c2 = Card.class.newInstance(); // Class객체를 통해서 객체 생성
Class obj = c.getClass();
System.out.println(c); // HEART:3
System.out.println(c2); // SPADE:1
System.out.println(obj.getName()); // Card
System.out.println(obj.toGenericString()); // final class Card
System.out.println(obj.toString()); // class Card
'Java' 카테고리의 다른 글
컬렉션 프레임워크(Collection Framework) (0) | 2021.09.30 |
---|---|
String Class (3) | 2021.09.30 |
연결된 예외(chained exception) (0) | 2021.09.30 |
예외 되던지기(exception re-throwing) (0) | 2021.09.27 |
사용자 정의 예외(Exception) 만들기 (0) | 2021.09.27 |