2021. 6. 17. 10:58ㆍJava
java.lang 패키지의 Object 클래스를 정리해보았다.
java.lang 패키지의 모든 클래스와 인터페이스는 import 없이 사용 가능하다. 주요 클래스는 아래와 같다.
Object 클래스
- 최상위 클래스
- 컴파일 시 extends Object 가 자동으로 붙는다.
- Object 클래스에는 11개의 메서드가 존재하며, 어떠한 Class 에서도 자유롭게 사용 가능하다.
- Object 클래스의 메서드는 아래와 같다.
Object 클래스의 중요한 메서드를 몇 가지 알아보자.
1. equals()
- equals 메서드는 원론적으로 == 연산자와 동일하게 메모리 번지를 비교한다.
- Object 클래스의 equlas() 메서드를 확인해보면 메모리 주소를 비교 코드를 볼 수 있다.
- 의문점이 있다면, 그동안 equals() 는 값을 비교하기 위해 썼는데 메모리 번지 비교라니????
- 이유는 String 클래스에서 Object 클래스의 equlas() 를 오버라이딩 하여 값을 비교하게 하였기 때문이다.
- 이는 논리적 동등을 지키기 위함이었고, 일반적으로 사용하는 방법이다.
- 논리적 동등이란? 객체의 주소가 달라도 저장 값이 같다면, 같은 객체로 보자는 개념이다.
- 하여, 사용자 정의 Class 에서는 논리적 동등을 만들기 위해 Object 클래스의 equlas() 를 오버라이딩 하여 값을 비교하게끔 만든다.
- String 클래스에서 Object 클래스의 equlas() 메서드 오버라이딩 코드를 확인해보자.
- 주소 비교를 값 비교로 재정의 한 걸 볼 수 있다.
- 앞으로 사용자 정의 Class 에서 논리적 동등을 위해 오버라이딩 해주는 걸 잊지 말자.
2. hashCode()
- 메모리 번지를 기준으로 객체를 식별할 수 있는 유일한 값을 의미한다. (10진수)
- 따라서 new 연산자로 생성된 인스턴스들은 해시 코드가 모두 다르다.
- 예제로 확인해보자.
import java.util.HashSet;
public class Example {
public static void main(String[] args) {
// Person 객체 3개 생성
Person person1 = new Person("사람", 20);
Person person2 = new Person("사람", 20);
Person person3 = new Person("사람", 20);
// hashSet 객체 생성
HashSet hashSet = new HashSet();
hashSet.add(person1);
hashSet.add(person2);
hashSet.add(person3);
// hashSet 은 중복이 허용 되지 않음
System.out.println(hashSet.size()); // "3" 출력
}
}
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
- 위 코드를 보면 hashSet 에 3개의 객체가 add 된 걸 확인할 수 있다. add 된 것부터 객체의 중복이 아니란 증거이다. (hashSet 은 중복이 허용되지 않음)
- 위에서 설명한 것처럼 Object 클래스의 hashCode 는 객체의 유일한 식별 값인 메모리 주소를 return 해주기 때문이다.
- 하지만 위의 정의는 우리가 바라는 개념이 아니다.
- hashCode() 는 일반적으로 두 객체가 동일한 객체인지 판단해주는 메서드로 사용된다.
- 그렇기에 사용자 정의 Class 에서는 hashCode() 를 오버라이딩하여 기능을 만들어주자.
- 주의점
: 해싱(hasing) 기법(=저장 또는 검색할 때 유용한 알고리즘)을 사용하는 Collection Framework (HashSet / HashMap / HashTable / LinkedHash)는 두 객체가 동등한 지의 판단을 내릴 때, 먼저 hashCode() 의 return 값을 보고, equlas() 의 return 값으로 최종 결정을 짓는다. 그렇기에 해싱 기법을 이용하는 hash 시리즈들을 사용할 거라면 equals() 도 오버라이딩 해주자!
import java.util.HashSet;
import java.util.Objects;
public class Example {
public static void main(String[] args) {
// Person 객체 3개 생성
Person person1 = new Person("사람", 20);
Person person2 = new Person("사람", 20);
Person person3 = new Person("사람", 20);
// hashSet 객체 생성
HashSet hashSet = new HashSet();
hashSet.add(person1);
hashSet.add(person2);
hashSet.add(person3);
// hashSet 은 중복이 허용 되지 않음
System.out.println(hashSet.size()); // "1" 출력
// toString
System.out.println(person1);
System.out.println(person2);
System.out.println(person3);
}
}
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
System.out.println("hashCode() 호출");
return Objects.hash(name, age);
}
@Override
public boolean equals(Object obj) {
System.out.println("equals() 호출");
if (obj instanceof Person) {
Person person = (Person) obj; // 다운캐스팅
// 같은지 비교
if (
this.name.equals(person.name) &&
this.age == person.age
){
return true;
}
}
return false;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
3. toString()
- 객체의 "패키지명@16진수코드" 를 반환하는 메서드
- 16진수는 주소를 리턴한 값이다.
- toString() 또한 우리는 문자 정보를 받도록 알고 있다.
- 그리고 위의 내용과 비슷하게 toString() 또한 오버라이딩 되어 쓰여왔다는 걸 알 수 있다.
- 위 hashCode 설명 코드를 보고, 예상해보자. 같은 객체이므로 toString 또한 주소 값이 똑같이 나올 것이다. 그리고 오버라이딩 해주었으니, 문자열로 출력될 것이다.
- 주소 같은 의미 없는 데이터를 반환하는 것보다는 의미 있는 문자 정보를 반환하도록 오버라이딩
ex) Date, String 클래스
4. clone()
- 객체를 복제
- 복제의 종류
: 얕은 복제 (Thin Clone) : 멤버변수 값만 복사하고 참조변수들은 주소를 서로 공유
: 깊은 복제 (Deep Clone) : 멤버변수 뿐만 아니라 참조변수들의 주소도 다르게 객체를 복사한다.
- Object 클래스의 clone() 메소드 기능은 Thin Clone 이다.
- 또한 java.lang.Cloneable 인터페이스를 구현한 객체만 복제가 가능하다. (implements Cloneable)
- 신기하게도 Cloneable 인터페이스는 아무 내용이 없다....!
- 아무 내용이 없지만, 이를 implements 하지 않은 클래스를 복제하게 되면 CloneNotSupportedException 발생
- 하여, 참조변수들은 프로그래머들이 직접 복제하는 코드를 필히 작성해줘야 한다.
- 얕은 복제 (Thin Clone) 실습 코드
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
Product originProduct = new Product("1", "TV", 4000, new int[]{ 1, 2}, new Objc());
Product cloneProduct = originProduct.cloneProduct();
// 가격 변경
originProduct.setPrice(1000);
// 배열 변경
int arr[] = originProduct.getArr();
arr[0] = 100;
//== 얕은 복제의 출력 확인
System.out.println("오리지날 : " + originProduct.toString());
System.out.println("===>클론 : " + cloneProduct.toString());
/**
* 얕은 복제의 출력 결과
*
* 오리지날 : Product{id='1', name='TV', price=1000, arr=[100, 2], objc=Objc@1b6d3586}
* ===>클론 : Product{id='1', name='TV', price=4000, arr=[100, 2], objc=Objc@1b6d3586}
*/
}
}
class Product implements Cloneable {
String id;
String name;
int price;
int[] arr;
Objc objc;
public Product(String id, String name, int price, int[] arr, Objc objc) {
this.id = id;
this.name = name;
this.price = price;
this.arr = arr;
this.objc = objc;
}
public int[] getArr() {
return arr;
}
public void setPrice(int price) {
this.price = price;
}
public void setArr(int[] arr) {
this.arr = arr;
}
@Override
public String toString() {
return "Product{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", price=" + price +
", arr=" + Arrays.toString(arr) +
", objc=" + objc +
'}';
}
public Product cloneProduct() {
Product cloneProduct = null;
try {
// Object 클래스의 clone
cloneProduct = (Product) this.clone(); // Thine Clone
} catch (CloneNotSupportedException e) {
e.getMessage();
}
return cloneProduct;
}
}
class Objc {
String temp;
}
- 깊은 복제 (Deep Clone) 실습 코드
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
Product originProduct = new Product("1", "TV", 4000, new int[]{ 1, 2}, new Objc());
Product cloneProduct = originProduct.cloneProduct();
// 가격 변경
originProduct.setPrice(1000);
// 배열 변경
int arr[] = originProduct.getArr();
arr[0] = 100;
//== 깊은 복제의 출력 확인
System.out.println("오리지날 : " + originProduct.toString());
System.out.println("===>클론 : " + cloneProduct.toString());
/**
* 깊은 복제의 출력 결과
*
* 오리지날 : Product{id='1', name='TV', price=1000, arr=[100, 2], objc=Objc@1b6d3586}
* ===>클론 : Product{id='1', name='TV', price=4000, arr=[1, 2], objc=Objc@1540e19d}
*/
}
}
class Product implements Cloneable {
String id;
String name;
int price;
int[] arr;
Objc objc;
public Product(String id, String name, int price, int[] arr, Objc objc) {
this.id = id;
this.name = name;
this.price = price;
this.arr = arr;
this.objc = objc;
}
public int[] getArr() {
return arr;
}
public void setPrice(int price) {
this.price = price;
}
public void setArr(int[] arr) {
this.arr = arr;
}
@Override
public String toString() {
return "Product{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", price=" + price +
", arr=" + Arrays.toString(arr) +
", objc=" + objc +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
// Object 클래스의 clone() 호출
Product cloneProduct = (Product) super.clone(); // Thin Clone
// 참조변수 복제 - 배열 및 객체
cloneProduct.arr = Arrays.copyOf(this.arr, this.arr.length);
cloneProduct.objc = new Objc();
return cloneProduct;
}
public Product cloneProduct() {
Product cloneProduct = null;
try {
// 오버라이딩 clone
cloneProduct = (Product) this.clone();
} catch (CloneNotSupportedException e) {
e.getMessage();
}
return cloneProduct;
}
}
class Objc {
String temp;
}
5. finalize()
- 객체를 소멸시킬 때 사용
- 기본적으로 GC 가 객체를 소멸하기 직전에 finalize() 를 실행시킨다.
- 객체가 소멸되기 직전 실행할 코드가 있따면 finalize() 를 오버라이딩 하여 사용하면 되지만, 그런일은 없을것이다.
- 사용할 일이 없으면 Object 클래스에 이런 메서드가 있다고만 알아두자.
* 참고: GC의 구동 시점은 ? 알 수 없다. 메모리가 부족하다. CPU 가 한가하다 등의 애매한 말이 있긴하지만 말도 안되는 말이다. 대신 GC 를 호출할 수 있는 방법은 있다. System.gc() 메서드를 사용하면 되지만, 바로바로 실행 되지는도 알 수 없고 순차적으로 실행 되는것도 알 수 없다.
'Java' 카테고리의 다른 글
[Java] 쓰레드(Thread) 란? 특징과 사용법 (0) | 2021.05.04 |
---|---|
[Java] 예외처리(Exception) 란? - 특징과 사용법 (0) | 2021.04.25 |
[Java] 스트림(Stream) 이란? - 특징과 사용법 (0) | 2021.04.24 |
[Java] Java8 에 새롭게 추가된 기능을 알아보자 (0) | 2021.04.12 |
[Java] 리플렉션(Reflection) 이란? (0) | 2021.03.10 |