Object 클래스
자바 클래스의 공통 특성을 모아놓은 클래스. 모든 클래스는 Object 클래스를 상속받는다.
Extends 절을 굳이 쓰지 않아도 컴파일 과정에서 모든 클래스는 자동으로 Object 클래스의 서브클래스가 된다.
class A { // Extends가 없어도 모든 클래스는 Object 클래스를 자동으로 상속
1. Object 클래스의 메소드
hashcode : HashMap 클래스의 put, get, remove 메소드는 통 번호를 계산할 때 hashCode라는 메소드를 사용한다. (자료구조 p549 참조)
wait : 다른 스레드로부터 신호가 오기를 기다리는 메소드 (멀티스레드 p714 참조)
notify : 다른 스레드로 신호를 보내는 메소드 (멀티스레드 p714 참조)
notifyAll : 대기하고 있는 모든 스레드로 신호를 보내는 메소드 (멀티스레드 p717 참조)
1) toString 메소드
객체가 가지고 있는 값을 문자열로 만들어서 리턴.
String = obj.toString(); // 이 객체가 가진 값을 문자열로 만들어서 리턴
예제) toString 메소드의 사용예 (1)
import java.io.File;
class ObjectExample1 {
public static void main(String args[]) {
File file = new File("C:\\뻐꾸기"); // File 객체를 생성
String str = file.toString(); // File 객체 값을 문자열로 만드는 toString 메소드 호출
System.out.println(str);
}
}
결과) C:\뻐꾸기
직접 만든 클래스도 오브젝트 클래스의 서브클래스이므로 toString을 쓸 수 있다.
이런 경우 toString을 오버라이드해서 사용해야 한다.
1) toString 메소드를 오버라이드 하는 상품 재고 클래스
public class GoodsStock {
String goodsCode; // 상품코드
int stockNum; // 재고수량
GoodsStock(String goodsCode, int stockNum) {
this.goodsCode = goodsCode;
this.stockNum = stockNum;
}
public String toString() { // Object 클래스의 toString을 이 클래스에서 다시 한번 오버라이딩
String str = "상품코드:" + goodsCode + " 재고수량:" + stockNum;
return str; // 상품코드와 재고수량을 문자열로 만들어 리턴
}
}
- 위의 오버라이딩 없이 아래의 프로그램을 그냥 실행하면 GoodsStock@757aef라는 값이 출력된다. 제대로 출력하려면 위와 같이 오버라이딩을 통해 toString을 다시 선언해야만 한다.
- toString 메소드 앞에는 반드시 public 키워드를 붙여야만 한다. (Object 클래스의 toString 메소드가 public으로 선언되어 있기 때문)
2) 상품 재고 클래스를 사용하는 toString의 예
public class ObjectExmaple2 {
public static void main(String[] args) {
GoodsStock obj = new GoodsStock("57293", 100);
String str = obj.toString();
System.out.println(str);
}
}
결과) 상품코드:57293 재고수량:100
toString 메소드는 위와 같이 오버라이드해야만 의미있는 값을 리턴한다. 하지만 인식하지 못하는 곳에서 자동으로 호출되기도 하기 때문에 이 메소드를 선언해둘 필요가 있다.
예제) 문자열 연결 연산자에 의해 toString 메소드가 자동으로 호출되는 예
public class ObjectExmaple3 {
public static void main(String[] args) {
GoodsStock obj = new GoodsStock("57293", 100);
String str = "재고정보 = " + obj; // 문자열과 객체를 + 연산자로 연결
System.out.println(str);
}
}
※ 문자열과 객체를 + 연산자로 연결하면 그 객체의 toString 메소드가 자동으로 호출된 후에 문자열이 연결된다.
예제) System.out.println 메소드에 의해 toString 메소드가 간접적으로 호출되는 예
public class ObjectExmaple4 {
public static void main(String[] args) {
GoodsStock obj = new GoodsStock("57293", 100);
System.out.println(obj); // 객체를 System.out.println 메소드로 넘겨준다.
}
}
※ system.out.println 메소드에 문자열이 아닌 객체를 넘겨주면 메소드 안에서 간접적으로 toString 메소드가 호출된다.
2) equals 메소드
객체가 가지고 있는 값을 비교하는데 사용. 문자열이 아닌 객체에 대해서도 모두 사용 가능하다.
obj1.equals(obj2) // obj1 객체와 obj2 객체 안의 값이 같은지 비교
예제) GregorainCalendar 객체를 equals 메소드로 비교하는 예
import java.util.GregorianCalendar;
public class ObjectExample5 {
public static void main(String args[]) {
GregorianCalendar obj1 = new GregorianCalendar(2007, 0, 1); // 같은 값을 갖는
GregorianCalendar obj2 = new GregorianCalendar(2007, 0, 1); // 2개의 객체를 생성
if (obj1.equals(obj2)) // 두 객체의 값을 equals 메소드로 비교
System.out.println("같음");
else
System.out.println("다름");
}
}
결과) 같음
직접 만든 클래스도 오브젝트 클래스의 서브클래스이므로 equals을 쓸 수 있다.
이런 경우 equals을 오버라이드해서 사용해야 한다.
Object 클래스의 equals 메소드는 두 객체를 비교할 때 객체의 필드 값이 아니라 참조값을 가지고 비교한다. 그렇기 때문에 그 메소드를 그대로 상속받아 사용하면 같은 값을 갖는 객체도 다르다는 결과가 나온다. 따라서 제대로 된 결과를 리턴하려면 Object 클래스로부터 상속받은 equals 메소드를 오버라이드해야 한다.
equals 메소드를 오버라이드 할 때에는 먼저 파라미터로 넘어온 객체가 클래스 자신과 같은 타입인지 체크 후, 그 객체를 클래스 자신의 타입으로 캐스트한 다음에 필드별로 비교작업을 수행해야 한다.
예제) 직접 만든 클래스의 객체를 equals 메소드로 비교하는 예
1) 원 클래스
public class Circle {
int radius; // 반지름
Circle(int radius) {
this.radius = radius;
}
public boolean equals(Object obj) {
if (!(obj instanceof Circle)) // 파라미터 객체가 Circle 타입인지 검사
return false;
Circle circle = (Circle) obj; // 파라미터 객체를 Circle 타입으로 캐스트
if (radius == circle.radius) // 파라미터 객체와 객체 자신의 radius 필드값을 비교
return true;
else
return false;
}
}
2) 원 클래스를 사용하는 프로그램
public class ObjectExample6 {
public static void main(String[] args) {
Circle obj1 = new Circle(5); // 같은 값을 갖는
Circle obj2 = new Circle(5);2개의 객체를 생성
if (obj1.equals(obj2)) // 두 객체의 값을 equals 메소드로 비교
System.out.println("같음");
else
System.out.println("다름");
}
}
결과) 같음
하지만 위의 circle 클래스는 완벽하지 않다.
Object 클래스의 equals 메소드를 오버라이드 할 때는 hashCode 메소드도 같이 오버라이드 해야만 한다.
※ hashCode의 오버라이드 방법
hashCode는 서로 다른 객체라도 같은 필드 값을 갖는 객체이면 같은 값으로 만들기만 하면 된다. (p553 참조)
public int hashCode() {
return 1;
}
3) clone 메소드
객체를 복제하는 기능의 메소드. object 클래스의 메소드지만 복제 가능한 클래스는 정해져 있으므로 모든 클래스에 대해 호출할 수는 없다. 각 클래스의 API 규격서에서 Cloneable이라고 쓰여진 클래스만 가능하다.
객체 자신을 복제하는 메소드이기 때문에 이 메소드가 리턴하는 값은 메소드를 호출하는 데 사용한 객체와 똑같은 타입의 객체이다. 하지만 리턴 타입은 Object 타입으로 선언되어 있으므로 이 메소드가 리턴하는 객체를 그 타입에 맞게 사용하려면 캐스트 연산을 해야만 한다.
GregorianCalendar obj2 = (GregorianCalendar) obj1.clone();
// clone 메소드의 리턴값을 GregorianCalendar 타입으로 캐스트 후 GregorianCalendar 변수에 대입
예제) clone 메소드를 호출하는 예
import java.util.GregorianCalendar;
class ObjectExample7 {
public static void main(String args[]) {
GregorianCalendar obj1 = new GregorianCalendar(2007, 0, 1); // 객체를 생성
GregorianCalendar obj2 = (GregorianCalendar) obj1.clone(); // 객체를 복사
System.out.printf("%tF %n", obj1); // 원본객체 출력
System.out.printf("%tF %n", obj2); // 복사객체 출력
}
}
결과)
2007-01-01
2007-01-01
※ System.out.printf 메소드에 사용한 %tF 포맷 명세자는 날짜를 YYYY-MM-DD 형식으로 포맷하는 역할
복제 가능한 클래스를 직접 만드는 경우
1) Cloneable 인터페이스를 구현
2) clone 메소드를 오버라이드
예제) clone 메소드의 오버라이딩 예
1) 복제 가능한 직사작형 클래스를 사용하는 프로그램
class ObjectExample8 {
public static void main(String args[]) {
Rectangle obj1 = new Rectangle(10, 20); // Rectangle 객체 생성
Rectangle obj2 = (Rectangle) obj1.clone(); // clone 메소드로 Rectangle 객체 복제
System.out.println("obj1 = " + obj1.width + "x" + obj1.height);
System.out.println("obj2 = " + obj2.width + "x" + obj2.height);
}
}
2) 복제 가능한 직사각형 클래스
class Rectangle implements Cloneable { // Cloneable 인터페이스를 구현
int width, height;
Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public Object clone() {// clone 메소드를 오버라이드
try {
return super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
}
4) finalize 메소드
객체가 제거되기 전에 자동으로 호출되는 메소드.
자바에서 객체가 제거되는 방법
1) new 키워드를 이용해 생성한 객체를 변수에 대입하면 변수에는 객체 자체가 아니라 참조값만 저장된다.
객체 자체는 heap이라는 별도의 메모리 영역에 저장된다. 참조값은 힙 안에 있는 객체 위치를 찾는 정보다.
ex) obj = new StringBuilder("토끼"); // obj라는 변수에 토끼라는 객체의 참조값 저장.
2) 힙에 있는 객체는 그 객체를 가리키는 참조값을 통해서만 사용할 수 있다.
그렇기 때문에 변수에 저장된 참조값을 잃거나 변수 자체가 없어지면 그 객체는 사용할 수 없게 된다.
ex) obj = new StringBuilder("거북이"); // obj라는 변수에 거북이라는 새로운 객체를 넣으면 기존의 토끼라는 객체는 사용할 수 없게 된다.
3) 하지만 참조값을 잃었다고 힙에 있는 객체가 바로 사라지는 것이 아니다. 이런 힙에 있는 쓸 수 없는 객체를 가비지라고 하는데, 힙에 가비지가 많아지면 다른 객체를 생성할 메모리가 부족해진다. JVM에서는 가비지 컬렉터라는 모듈이 주기적으로 실행되어 힙에 있는 가비지를 자동으로 제거한다.
가비지 컬렉터는 힙이 가비지로 가득 차서 새로운 객체를 생성할 메모리가 부족하거나, 프로그램에서 특별히 다른 할 일이 없을 때 임의로 작동된다. 객체가 소멸될 때 해야할 일을 써넣을 때, finalize 메소드는 오버라이드 해놓으면 가비지 컬렉터가 객체를 제거하기 전에 자동으로 호출한다.
예제) finalize 메소드를 오버라이드한 클래스의 예
public class ResourceUser {
ResourceUser() { // 생성자 안에서 시스템 자원을 할당받음.
}
void user() { // 할당받은 시스템 자원을 사용.
}
protected void finalize() { // 시스템 자원의 할당 해제
}
}
finalize 메소드를 사용했을 때의 장단점
finalize 메소드를 이용하여 시스템 자원을 해제하면 그 메소드를 따로 호출할 필요도 없고, 객체가 존재하는 동안 시스템 자원을 자유로이 사용할 수 있어서 좋습니다.
하지만 프로그램의 전체 실행 시간에 비해 시스템 자원을 사용하는 시간이 아주 짧을 때 이런 방법을 사용하면 시스템 자원의 해제가 불필요하게 지연될 수 있습니다. 그런 식의 지연이 많아지면 프로그램의 다른 부분이나 다른 프로그램에서 필요로 하는 시스템 자원이 부족하거나 고갈되는 문제를 초래할 수 있습니다.
5) getClass 메소드
객체가 속하는 정보를 알아내는 메소드
Class cls = obj.getClass(); // 객체가 속하는 클래스의 정보를 리턴
Class 클래스는 클래스에 대한 여러가지 정보를 담는 기능을 하는데, 그런 정보는 get 메소드로 가져올 수 있다.
String str = cls.getName(); // 클래스의 이름을 리턴
Class superCls = cls.getSuperclass(); // 슈퍼클래스(상위클래스)의 정보를 리턴
Field field[] = cls.getDeclaredFields(); // 클래스에 선언되어 있는 필드 정보를 가져오는 메소드
Method method[] = cls.getDeclaredMethods(); // 클래스에 선언되어 있는 메소드 정보를 가져오는 메소드
예제) getClass 메소드의 사용 예
1) 직사각형 클래스
class Rectangle {
int width, height;
Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
int getArea() {
return width * height;
}
}
2) get 메소드를 사용하는 프로그램
import java.lang.reflect.Field;
import java.lang.reflect.Method;
class ObjectExample9 {
public static void main(String args[]) {
Rectangle obj = new Rectangle(10, 20); // Ractangle 클래스의 객체
Class cls = obj.getClass(); // getClass 메소드를 Class 타입 변수로 선언
String name = cls.getName(); // 클래스의 이름을 가져오는 getName 메소드
System.out.println("클래스 이름: " + name);
Class superCls = cls.getSuperclass(); // 슈퍼클래스의 정보를 가져오는 getSuperclass 메소드
String superName = superCls.getName(); // 가져온 슈퍼클래스의 이름을 가져오는 getName
System.out.println("슈퍼클래스 이름: " + superName);
Field field[] = cls.getDeclaredFields(); // 클래스에 선언된 필드 정보를 가져오는 메소드
System.out.println("필드: ");
for (int cnt = 0; cnt < field.length; cnt++)
System.out.println(" " + field[cnt]);
Method method[] = cls.getDeclaredMethods(); // 클래스에 선언된 메소드 정보를 가져오는 메소드
System.out.println("메서드: ");
for (int cnt = 0; cnt < method.length; cnt++)
System.out.println(" " + method[cnt]);
}
}
결과)
클래스 이름: object_calss.Rectangle
슈퍼클래스 이름: java.lang.Object
필드:
int object_calss.Rectangle.width
int object_calss.Rectangle.height
메서드:
int object_calss.Rectangle.getArea()
'자바의 기초문법' 카테고리의 다른 글
자료구조 클래스: ArrayList, LinkedList, HashMap, HashSet (0) | 2015.12.22 |
---|---|
Wrapper 클래스: value, parse, valueOf, Boxing/Unboxing (0) | 2015.12.22 |
package와 접근제어 public, private, protected, final (0) | 2015.12.22 |
날짜와 시간 자바 클래스: Date, Calendar, GregorianCalendar (0) | 2015.12.21 |
String 클래스 문제: indexOf, chatAt(), Integer.parseInt, substring (0) | 2015.12.21 |