728x90
Object 클래스
- 모든 클래스의 최고 조상이기 때문에 Object 클래스의 멤버들은 모든 클래스에서 바로 사용 가능
- 멤버변수는 없고 오직 11개의 메서드만 가지고 있다.
- 모든 클래스의 조상인 Object 클래스의 메서드들은 대부분 네이티브 메서드이다.
Native Method란
- 네이티브 메서드는 JVM이 설치된 OS의 메서드를 말한다.
- 네이티브 메서드는 보통 C언어로 작성되어 있는데, 자바에서는 메서드의 선언부만 정의하고 구현은 하지 않는다.
- Java Method와 Native Function을 Mapping하는 역활은
JNI(java native interface)
가 한다.
주요 메서드
protected Object clone()
: 객체 자신의 복사본을 반환한다.public boolean equals(Object obj)
:객체 자신과 객체 obj가 같은 객체인지 알려준다.protected void finalize()
: 객체가 소멸될 때 가비지 컬렉터에 의해 자동적으로 호출된다.- 이 때 수행되어야하는 코드가 있을 때 오버라이딩 한다.
- finalize 예측이 불가능하고, 위험하며 대부분 불필요하다
- Deprecated
public Class getClass()
: 객체 자신의 클래스 정보를 담고 있는 Class 인스턴스를 반환한다.public int hashCode()
: 객체 자신의 해시코드를 반환한다.public String toString()
: 객체 자신의 정보를 문자열로 반환한다.public void notify()
: 객체 자신을 사용하려고 기다리는 쓰레드를 하나만 깨운다.public void notifyAll()
: 객체 자신을 사용하려고 기다리는 모든 쓰레드를 깨운다.public void wait(), wait(long timeout), wait(long timeout, int nanos)
- 다른 쓰레드가 notify()나 notifyAll()을 호출할 때까지 현재 쓰레드를 무한히 또는 지정된 시간(timeout, nanos)동안 기다리게 한다.
- timeout: 천 분의 1초, nanos는 10^9분의 1초
Clone()
- 자신을 복제하여 새로운 인스턴스를 생성하는 일을 한다
- clone() 사용법
- 복제할 클래스가 Cloneable 인터페이스를 구현한 클래스에서만 clone() 호출 가능
- 그리고 접근제어자를 protected에서 public으로 변경
- 마지막으로 조상클래스의 clone()을 호출하는 코드가 포함된 try - catch문 작성
class Poketmon implements Cloneable {
String name;
Poketmon(String name) {
this.name = name;
}
@Override
public Object clone() {
Object obj = null;
try {
obj = super.clone(); // clone()은 반드시 예외처리를 해주어야 한다.
} catch(CloneNotSupportedException e) {}
return obj;
}
}
class CloneEx {
public static void main(String[] args){
Poketmon original = new Poketmon(3, 5);
Poketmon copy = (Poketmon)original.clone(); // 복제(clone)해서 새로운 객체를 생성
System.out.println(original);
System.out.println(copy);
}
}
Poketmon{name='메타몽'}
Poketmon{name='메타몽'}
- 하지만 clone()은 재정의하지 않는게 좋다고 한다.
- copy constructor 또는 copy factory 사용을 권장한다. ### 공변 반환타입(covariant return type)
- jdk 1.5부터 추가된 기능
- 오버라이딩할 때 조상 메서드의 반환타입을 자손 클래스의 타입으로 변경을 허용하는 것
- 이처럼 조상의 타입이 아닌, 실제로 반환되는 자손 객체의 타입으로 반환할 수 있어서 번거로운 형변환이 줄어드는 장점이 존재.
@Override
public Poketmon clone() {
Object obj = null;
try {
obj = super.clone();
} catch(CloneNotSupportedException e) {}
return obj;
}
// Poketmon copy = (Poketmon)original.clone();
Poketmon copy = original.clone();
얕은 복사와 깊은 복사
- clone()은 단순히 객체에 저장된 값을 그대로 복제할 뿐, 객체가 참조하고 있는 객체까지 복제하지는 않는다.
- 기본형 배열인 경우에는 아무런 문제가 없지만, 객체배열을 clone()으로 복제하는 경우에는 원본과 복제본이 같은 객체를 공유하므로 완전한 복제라고 보기 어렵다.
- 이러한 복제를 얕은 복사라고 한다, shallow copy
- 얕은 복사는 원본을 변경하면 복사본도 영향을 받는다.
- 반면에 원본이 참조하고 있는 객체까지 복제하는 것을 깊은 복사, deep copy
- 깊은 복사에서는 원본과 복사본이 서로 다른 객체를 참조하기 때문에 원본의 변경이 복사본에 영향을 미치지 않는다. ## getClass()
- 자신이 속한 클래스의 Class 객체를 반환하는 메서드
- Class 객체는 이름이 'Class'인 클래스 객체이다.
- 다음과 같이 정의
public final class Class implements ... { ... }
- 클래스 객체는 클래스의 모든 정보를 담고 있으며, 클래스 당 1개만 존재
- 클래스 파일이 '클래스 로더(ClassLoader)'에 의해 메모리에 올라갈 때, 자동으로 생성
- 먼저 기존에 생성된 클래스 객체가 메모리에 존재하는지 확인하고
- 있으면 클래스 객체의 참조를 반환
- 없으면 클래스 패스에 지정된 경로를 따라서 클래스 파일을 찾는다.
- 못찾으면 ClassNotFoundException 발생
- 찾으면 해당 클래스 파일을 읽어서 Class 객체로 변환
- 못찾으면 ClassNotFoundException 발생
- 즉 파일 형태로 저장되어 있는 클래스를 읽어서 Class 클래스에 정의된 형식으로 변환하는 것
- 따라서 클래스 파일을 읽어서 사용하기 편한 형태로 저장해 놓은 것이 클래스 객체이다.### Class 객체를 얻는 방법
- 클래스의 정보가 필요할 때, 먼저 Class 객체에 대한 참조를 얻어 와야 하는데, 해당 Class 객체에 대한 참조를 얻는 방법으로 여러 가지가 있다.
// case1: 생성된 객체로부터 얻는 방법
Class cObj = new Poketmon().getClass();
// case2: 클래스 리터럴(*.class)로 부터 얻는 방법
Class cObj = Poketmon.class;
// case3: 클래스 이름으로 부터 얻는 방법
// 특히 클래스 파일, 예를 들어 데이터베이스 드라이버를 메모리에 올릴 때 주로 사용
Class cObj = Class.forName("Poketmon");
- 클래스 객체를 이용하면 클래스에 정의된 멤버의 이름이나 개수 등, 클래스에 대한 모든 정보를 얻을 수 있기 때문에 Class 객체를 통해서 객체를 생성하고 메서드를 호출하는 등 보다 동적인 코드 작성 가능
Poketmon p = new Poketmon(); // new 연산자를 이용한 객체 생성
Poketmon p = Poketmon.class.newInstance(); // Class객체를 이용한 객체 생성
toString()
목적 : 인스턴스에 대한 정보를 문자열로 제공하기 위해
- 일반적으로, toString 메소드는 this 객체를 "텍스트로 표현"한 문자열을 리턴합니다. 결과는 간결하면서도 충분한 정보를 담고 있어야 하며, 사람이 읽기 쉬운 형태여야 합니다.
- 대부분의 경우 인스턴스 변수에 저장된 값들을 문자열로 표현
- Object class의 toString()
public String toString() {
return getClass().getName()+"@"+Integer.toHexString(hashCode());
}
- 가능하다면 toString 메소드는 객체 내의 중요 정보를 전부 담아 반환해야 한다.
- toString이 반환하는 문자열의 형식을 명시하건 그렇지 않건 간에, 어떤 의도인지는 문서에 분명하게 남겨야 한다.
- toString이 반환하는 문자열에 포함되는 정보들은 전부 프로그래밍을 통해서 가져올 수 있도록 하라.
- toString 반환값을 파싱해서 쓰는 일이 없도록 한다. toString 구현이 바뀌면 그런 코드는 다 못 쓰게 된다
equals(Object obj)
목적 : 물리적으로 다른 메모리에 위치하는 객체여도 논리적으로 동일함을 구현하기 위해
- 두 객체의 같고 다름을 참조변수의 값으로 판단
- 즉 두 참조변수에 저장된 주소값이 같은지를 판단하는 기능
public boolean equals(Object obj) {
return (this == obj)
}
- 클래스의 인스턴스변수 값으로 객체의 같고 다름을 비교하게 하고 싶다면 오버라이딩을 하면된다
- String class의 equals 메서드도 오버라이딩을 통해 String 인스턴스가 갖는 문자열 값을 비교하도록 되어 있다.
- String, Date, File, wrapper 클래스(Integer, Double 등)
- equals 메소드는 null이 아닌 객체 참조들에 대한 동치 관계를 구현하며, 동치 관계의 조건은 null이 아닌 참조 x, y, z에 대하여,
- 반사관계:
x.equals(x)
는true
여야 한다. - 대칭관계:
y.equals(x)
가true
이면,x.equals(y)
도true
여야 한다. - 추이관계:
x.equals(y)
가true
이고,y.equals(z)
도true
이면,x.equals(z)
또한true
여야 한다. - 일관성:
equals
비교에 필요한 정보가 수정되지 않았다면,x.equals(y)
를 여러 차례 실행한 결과는 일관성 있게true
만 리턴하거나false
만 리턴해야 한다. x.equals(null)
은false
여야 한다. ### 실제 위반되는 경우
- 반사관계:
- 실제 Timestamp 객체와 Date 객체를 같은 컬렉션에 보관하거나 섞어 쓰면 문제가 생길 수 있다고 한다.
- 대칭관계 위반
// Date Class
public boolean equals(Object obj) {
return obj instanceof Date && getTime() == ((Date) obj).getTime();
}
// Timestamp class
public boolean equals(Timestamp ts) {
if (super.equals(ts)) { // java.util.Date.equals
if (nanos == ts.nanos) {
return true;
} else {
return false;
}
} else {
return false;
}
}
public boolean equals(java.lang.Object ts) {
if (ts instanceof Timestamp) {
return this.equals((Timestamp)ts);
} else {
return false;
}
}
date.equals(timestamp)
: date 기준으로 시간이 같은지만 검사하므로 true가 된다.timestamp.equals(date)
: date는 Timestamp의 인스턴스가 아니므로 false가 된다. 인스턴스 검사를 하지 않고 date가 timestamp로 형변환이 가능하다 쳐도, nanos 검사에서 false가 나올 수 밖에 없다.
hashCode()
목적: 객체의 동일성을 위해 구현
- 해싱기법에 사용되는 해시함수를 구현한 것이다.
- 인스턴스가 저장된 가상머신의 주소를 10진수로 반환
- 오버라이딩 된 hashCode()는 세가지 조건을 만족해야 한다.
- 변경되지 않은 한 객체의
hashCode
메소드를 호출한 결과는 항상 똑같은integer
값이어야 한다.- 객체가 변경됐더라도
equals
메소드가 참고하는 정보가 변경되지 않았다면hashCode
값은 달라지지 않는다.
- 객체가 변경됐더라도
- equals 메서드를 이용한 비교에 의해서 true를 얻은 두 객체에 대해 각각 hashCode()호출해서 얻은 결과는 반드시 같아야 한다.
- f1 f2의 equals 메서드의 결과가 true라면 hashCode1, 2의 값은 같아야 한다
Fruit f1 = new Fruit("apple", 10); Fruit f2 = new Fruit("apple", 10); boolean b = f1.equals(f2); int hashCode1 = f1.hashCode(); int hashCode2 = f2.hashCode();
- 그러나 equals() 메소드가 다르다고 판별한 두 객체의
hashCode
값이 반드시 달라야 하는 것은 아니다.- 해시 테이블 성능이 향상을 위해 같지 않은 객체들이 각기 다른
hashCode
값을 가지면 좋다. - 서로 다른 객체에 대해서 해시코드값이 중복되는 경우가 많아질수록 해싱을 사용하는 컬렉션의 검색속도가 떨어진다.
- 해시 테이블 성능이 향상을 위해 같지 않은 객체들이 각기 다른
- 변경되지 않은 한 객체의
- 따라서 hashCode()메서드는 equals() 메서드와 밀접한 관계가 있다.
- 2번 조건에 위배되지 않기 위해
참고 및 출처
https://atoz-develop.tistory.com/
https://yoonemong.tistory.com/193
https://github.com/castello/javajungsuk3
https://docs.oracle.com/en/java/javase/11/docs/api/index.html
728x90
'java' 카테고리의 다른 글
[모던 자바 인 액션] 자바 8, 9, 10, 11에서 일어난 일 (0) | 2024.07.07 |
---|---|
static inner vs non-static inner class (0) | 2024.07.07 |
자바 제네릭스 (0) | 2024.07.07 |
java 버전별 차이 & 특징 (0) | 2024.07.07 |
String vs StringBuilder vs StringBuffer (0) | 2024.07.07 |