모든 클래스들의 최상위 클래스
Object 클래스에 대해서 알아보았습니다.
Object 클래스 메소드
즉, 모든 클래스를 사용할 때 Object클래스를 상속받습니다. 이런것들을 컴파일러가 처리해주고 있습니다.
그럼 int와 같은 것들도 Object 클래스를 상속받을까요?( 접은 글을 확인해보세요 )
그렇지 않습니다.
Java의 모든 클래스는 암시적으로 또는 명시적으로 Object 클래스를 상속받습니다.
하지만 int는 기본 자료형으로 값을 저장하기에 객체로 다뤄지지 않고 직접 값을 처리할 때 사용하기 때문입니다.
어떤 패키지안에 있는 클래스일까?
바로 java.lang 패키지입니다.
java.lang.Object 클래스는 필드를 가지지 않고, 11개의 메소드로 구성되어있습니다.
메소드 예제
그 중 많이 쓰는 메소드를 알아보겠습니다.
toString()
Car car01 = new Car();
Car car02 = new Car();
System.out.println(car01.toString());
System.out.println(car02.toString());
// Car@1dico29743
// Car@insdavim12
toString()은 해당 인스턴스에 대한 정보를 문자열로 반환합니다.
그리고 반환되는 문자열은 클래스 이름과 함께 구분자로 '@'가 사용되며, 16진수 해시 코드가 추가됩니다.
16진수 해시 코드 값은 인스턴스 주소를 가리키는 값으로 인스턴스마다 다르게 반환됩니다.
즉 새로운 객체가 생성될 때마다 JVM이 힙 영역에 새로운 메모리 공간을 할당하고, 이공간에 객체의 데이터가 저장되는 것입니다.
그리고 할당된 메모리 공간의 주소값이 참조 변수에 저장되는 것입니다.
equals()
인스턴스를 매개변수로 전달받는 참조 변수와 비교해서, 결과 값을 반환합니다.
이때 참조 변수가 가리키는 값을 비교하므로, 서로 다른 객체는 언제나 false를 반환합니다.
이때, 주의할 점은 primitive type과 reference type에 대해 인지하고 있는가 입니다.
primitive와 reference type의 차이점은 이글을 읽고 와주세요~!
2024.01.25 - [분류 전체보기] - Primitive type & Reference type
equals()메소드는 객체를 비교해주는 메소드이기에 primitive와 같이 값을 비교하는 용도로 사용하지 않습니다.
'=='을 사용해서 비교하죠. 하지만 객체를 비교할때에는 equals()를 사용해야합니다.
만약 primitive type의 객체를 비교하고 싶다면 오토 박싱을 통해 Wrapper 클래스로 감싸주게된다면 사용할 수 있습니다.
Car car01 = new Car();
Car car02 = new Car();
System.out.println(car01.equals(car02));
car01 = car02; // 두 참조 변수가 같은 주소를 가리킴.
System.out.println(car01.equals(car02));
// false
// true
clone()
Object 클래스의 clone()은 필드의 값만 복사합니다.
혹시 필드의 값이 배열이나 인스턴스라면, clone()을 통해 복제되지 않으며, 이때는 재정의를 통해 사용해야합니다.
# 아래 3개의 메서드는 호출하는 스레드가 반드시 고유 락을 갖고 있어야 합니다.(synchronized 블록 내에서 진행하면됩니다.)
wait() // wait는 뒤에 2개의 파라미터를 가지며 첫번째 파라미터는 timeout, 두번째 파라미터는 nanos입니다. 시간을 세부적으로 할때 사용됩니다.
notify()
notifyAll()
import java.util.*;
class Car implements Cloneable {
private String modelName;
① private ArrayList<String> owners = new ArrayList<String>();
public String getModelName() { return this.modelName; } // modelName의 값을 반환함
public void setModelName(String modelName) { this.modelName = modelName; } // modelName의 값을 설정함
public ArrayList getOwners() { return this.owners; } // owners의 값을 반환함
public void setOwners(String ownerName) { this.owners.add(ownerName); } // owners의 값을 추가함
public Object clone() {
try {
② Car clonedCar = (Car)super.clone();
③ // clonedCar.owners = (ArrayList)owners.clone();
return clonedCar;
④ } catch (CloneNotSupportedException ex) {
ex.printStackTrace();
return null;
}
}
}
public class Object03 {
public static void main(String[] args) {
⑤ Car car01 = new Car();
car01.setModelName("아반떼");
car01.setOwners("홍길동");
⑥ System.out.println("Car01 : " + car01.getModelName() + ", " + car01.getOwners() + "\n");
⑦ Car car02 = (Car)car01.clone();
⑧ car02.setOwners("이순신");
⑨ System.out.println("Car01 : " + car01.getModelName() + ", " + car01.getOwners());
⑩ System.out.println("Car02 : " + car02.getModelName() + ", " + car02.getOwners());
}
}
②번에서 clone()을 오버라이딩 했습니다.
그리고 7번에서는 clone() 메서드를 호출하여 복제를 수행하고 있습니다.
하지만 ②번 라인처럼 clone() 메소드를 재정의하면, 필드의 값이 ①번 라인처럼 인스턴스일 때는 제대로 된 복제를 수행할 수 없습니다.
⑧번 라인에서는 복제된 인스턴스인 car02의 owners 필드에 새로운 값을 하나 추가합니다.
하지만 ⑨번 라인의 실행 결과를 보면, ⑦번 라인의 결과와는 달리 원본 인스턴스인 car01의 owners 필드에도 새로운 값이 추가되었음을 확인할 수 있습니다.
이처럼 단순히 부모 클래스의 clone() 메소드를 호출하여 clone() 메소드를 재정의하면, 배열이나 인스턴스인 필드는 복제되는 것이 아닌 해당 배열이나 인스턴스를 가리키는 주소값만이 복제되는 것입니다.
따라서 정확한 복제를 위해서는 ③번 라인처럼 배열이나 인스턴스인 필드에 대해서는 별도로 clone() 메소드를 구현하여 호출해야 합니다.
다만 이렇게 메소드를 호출할때 힙영역에 있는 것을 비교하는 것이기에 락을 통해서 교착상태로 되는 것을 방지할 필요가 있는 것입니다.
getClass()
런타임 클래스 객체의 정보를 가져옵니다.
JVM의 클래스 로더가 힙 영역에 class 객체를 생성하고, 이 객체의 인스턴스를 클래스 객체라고 합니다.
Rumtime Class Object의 메서드로는
getName() : Class의 이름을 반환
getFields() : Class의 필드를 반환
getConstructors() : Class의 생성자를 반환
getMethods() : Class의 메소드를 반환
이렇게 존재합니다.
// 임의의 Class를 선언
public class ObjectClass {
public String a = "hello, world";
public Integer b = 28;
public ObjectClass(String a, Integer b) {
this.a = a;
this.b = b;
}
public void method() {
System.out.println("hello, world");
}
}
// ObejctClass를 생성하여, [getClass, getName, getFields, getConstructors, getMethods] 메서드의 결과값을 확인
public class ClassTest {
private static ObjectClass objectClass = new ObjectClass("hello, world", 28);
public static void main(String[] args) throws NoSuchFieldException {
// getClass 메서드
Class<? extends ObjectClass> objClass = objectClass.getClass();
System.out.println("getClass(): " + objClass);
// getName 메서드
String className = objectClass.getClass().getName();
System.out.println("\ngetName(): " + className);
// getFields 메서드
Field[] fields = objectClass.getClass().getFields();
System.out.println("\ngetFields(): " + fields);
for (Field field:fields) {
System.out.println("원소 출력: " + field);
}
// getConstructors 메서드
Constructor<?>[] constructors = objectClass.getClass().getConstructors();
System.out.println("\ngetConstructors(): " + constructors);
for (Constructor<?> constructor:constructors) {
System.out.println("원소 출력: " + constructor);
}
// getMethods 메서드
Method[] methods = objectClass.getClass().getMethods();
System.out.println("\ngetMethods(): " + methods);
for (Method method:methods) {
System.out.println("원소 출력: " + method);
}
}
}
hashCode()
객체의 해시코드를 반환해 줍니다.
기본 값은 주소값을 해시코드로 반환해주기 때문에, 객체의 주소가 아닌 다른 데이터의 주소를 반환하기 위해서 오버라이딩 해서 재정의 할수 있습니다.
참고 : http://happinessoncode.com/2017/10/05/java-object-wait-and-notify/
'기술 스텍 > Java' 카테고리의 다른 글
자바 버전에 따른 차이 (0) | 2024.02.19 |
---|---|
Casting 이란? (0) | 2024.02.17 |
직렬화란? (0) | 2024.02.03 |
Call By Value와 Call By Reference 차이점 분석 (0) | 2024.01.20 |
JVM 내부 구조 정리 (1) | 2024.01.14 |