[Java] 기본 클래스

3 분 소요

java.lang

지금까지 자바로 프로그램을 구현하면서 String, Integer와 같은 클래스를 사용했다. 이러한 클래스들은 어디에 있는 걸까? 이들은 모두 java.lang 패키지에 속해 있다. String 클래스의 전체 이름은 java.lang.String이고, Integer 클래스의 전체 이름은 java.lang.Integer이다. 이처럼 java.lang 패키지에는 기본적으로 많이 사용하는 클래스들이 포함되어 있다.

자바에서는 외부 패키지에서 선언한 클래스를 사용하고자 할 때는 import문으로 클래스가 어느 패키지에 속해 있는지 선언해야한다. 근데 우리는 지금까지 String 클래스를 쓰면서 import java.lang.String을 쓴 적이 없다. 그럼에도 우리는 String 클래스를 바로바로 사용할 수 있었다. 그 이유는 컴파일을 하게 되면 자동으로 컴파일러가 import java.lang.*; 을 추가 하기 때문에 직접 써주지 않아도 우리는 java.lang의 하위 클래스를 사용 할 수 있다.

우리가 여기서 다룰 기본 클래스들은 모두 java.lang 패키지에 속한 클래스들이다.
그러면 모든 자바 클래스의 최상위 클래스인 java.lang.Object에 대해서 알아보자.

최상위 클래스, Object

Object 클래스는 모든 자바 클래스의 최상위 클래스로, 모든 클래스는 Object 클래스를 상속받는다. 이 역시 컴파일러가 자동으로 extends Object를 추가해주기 때문에, 우리가 따로 추가할 필요는 없다.

우리가 직접 만드는 클래스 뿐만 아니라, JDK에서 제공하는 클래스도 모두 Object 클래스에서 상속을 받는다.

Object 클래스에 정의된 메서드 중 주로 사용하는 메서드는 다음과 같다.

메서드 설명
String toString() 객체를 문자열로 표현하여 반환한다. 재정의하여 객체에 대한 설명이나 특정 멤버 변수 값을 반환한다.
boolean equals(Object obj) 두 인스턴스가 동일한지 여부를 반환한다. 재정의하여 논리적으로 동일한 인스턴스임을 정의할 수 있다.
int hashCode() 객체의 해시 코드 값을 반환한다.
class getClass() 객체의 클래스를 반환한다.
void finalize() 인스턴스가 힙 메모리에서 제거될 때 가비지 컬렉터(GC)에 의해 호출되는 메서드이다. 네트워크 연결 해제, 열려있는 파일 스트림 해제 등을 구현한다.
void wait() 멀티스레드 프로그램에서 사용하는 메서드이다. 스레드를 ‘기다리는 상태’ (non runnable)로 만든다.
void notify() wait() 메서드에 의해 기다리고 있는 스레드(non runnable)를 실행가능한 상태 (runnable)로 바꾼다.

Object 메서드 중에는 재정의할 수 있는 메서드가 있고, 그렇지 않은 메서드가 있다. 여기서는 자주 재정의하여 사용하는 메서드 위주로 알아보자.

toString() 메서드

toString()은 객체 정보를 문자열로 바꾸어 준다. Object 클래스를 상속받은 모든 클래스는 toString()을 재정의할 수 있는데, String 이나 Integer등의 여러 JDK 클래스에는 toString() 클래스가 이미 재정의 되어있다.

toString의 원형은 인스턴스 클래스의 이름과 주소 값을 보여준다.
예제로 책 번호와 제목을 담고 있는 Book 클래스의 인스턴스를 생성하여 참조변수를 출력해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package objectclass;

public class ToStringEx {
    public static void main(String[] args){
        Book book1 = new Book(200,"개미");

        System.out.println(book1);
        System.out.println(book1.toString());
    }

}
class Book{
    int bookNumber;
    String bookTile;

    Book(int bookNumber, String bookTile){
        this.bookNumber = bookNumber;
        this.bookTile = bookTile;
    }
}

>>> objectclass.Book@3fb6a447
>>> objectclass.Book@3fb6a447

System.out.println()에 참조 변수를 넣으면 인스턴스의 정보가 호출되는데, 이는 자동으로 toString()을 호출하는 것이다.
toString() 메서드의 원형은 다음과 같다.

1
getClass().getName()+'@'+Integer.toHexString(hashCode())

위의 정의를 살펴보면, ‘클래스 이름@해시코드 값’을 출력하라는 뜻인걸 알 수 있다.
따라서 위처럼 objectclass.Book@해시코드값이 출력된다.

equals 메서드

equals() 의 원래 기능은 두 인스턴스의 주소값을 비교하여 boolean 값으로 반환해 주는 것이다. 주소 값이 같다면 당연히 같은 인스턴스이다. 그런데 서로 다른 주소 값을 가짐에도 같은 인스턴스라고 정의해줄 수 있는 경우가 있다. 따라서 물리적 동일성(인스턴스의 메모리 주소가 같음) 뿐 아니라 논리적 동일성(논리적으로 두 인스턴스가 같음)을 구현할 때도 equals() 메서드를 재정의하여 사용한다.

생성된 두 인스턴스가 ‘같다’는건 어떤 의미일까?

먼저 물리적 동일성의 경우, 당연히 메모리 주소값이 같은 경우 일 것이다.

1
2
Student studentLee = new Student(100, "이민재");
Student studentLee2 = studentLee;

예를 들어 위처럼 동일한 인스턴스를 두 변수가 가르키게 한다면, studentLee와 studentLee2의 힙메모리 주소는 같으므로 물리적으로 동일하다 할 수 있다.

하지만 이 경우를 보자.

1
2
3
Student studentLee = new Student(100, "이민재");
Student studentLee2 = studentLee;
Student studentMin = new Student(100,"이민재");

이러한 경우는 studentLee와 studentMin이 다른 메모리 주소를 가르키지만, 논리적으로는 같은 학생으로 처리해주어야 할 것이다. 이 상황을 구현할 에제를 만들어 살펴보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package objectclass;

class Student{
    String studentName;
    int studentID;

    public Student(String studentName, int studentID){
        this.studentName = studentName;
        this.studentID = studentID;
    }
}
public class EqualsTest {
    public static void main(String[] args){
        Student studentLee = new Student("이민재", 100);
        Student studentLee2 = studentLee;
        Student studentMin = new Student("이민재",100);

        if (studentLee.equals(studentLee2)){
            System.out.println("두 주소값이 같습니다.");
        }
        else{
            System.out.println("두 주소값이 다릅니다..");
        }
        if (studentLee.equals(studentMin)){
            System.out.println("두 주소값이 같습니다.");
        }
        else{
            System.out.println("두 주소값이 다릅니다.");
        }
    }


}

>>>  주소값이 같습니다.
>>>  주소값이 다릅니다.

위는 equals()의 원래 기능으로 두 변수의 논리적 동일성이 어떻든간에, 두 인스턴스의 주소가 다르다면 false를 반환한다.
만약 논리적으로 같은 인스턴스인지 확인하고싶다면, 재정의 하여 그렇게 확인하도록 구현해주어야 한다.

따라서 앞서 말한것 처럼 String 클래스와 Integer 클래스에서는 이미 equals()가 재정의 되어있기 때문에, 주소값과 관계없이 두 인스턴스가 논리적으로 같다면 true를 반환한다.

카테고리:

업데이트:

댓글남기기