몬그로이

자바의 정석 09. java.lang 패키지와 유용한 클래스 01 본문

Organizing Docs/Java Docs

자바의 정석 09. java.lang 패키지와 유용한 클래스 01

Mon Groy 2024. 7. 7. 04:51

 java.lang 패키지

자바프로그래밍에 가장 기본이 되는 클래스들을 포함하고 있는 패키지

import문 없이 사용 가능

 

java.lang 패키지의 대표적인 클래스

 

1. Object

*모든 클래스의 최고 조상

 

Object 클래스의 메서드

 

@IntrinsicCandidate

protected native Object clone() throws CloneNotSupportedException;

*객체 자신의 복사본을 반환

public boolean equals(Object obj) {

       return (this == obj);

}

*객체 자신과 객체 obj가 같은 객체인지 알려줌(같으면  true)

 

@IntrinsicCandidate

public final native Class<?> getClass();

*객체 자신의 클래스 정보를 담고 있는 Class 인스턴스 반환

 

@IntrinsicCandidate

public native int hashCode();

*객체 자신의 해시코드 반환

 

public String toString() {

        return getClass().getName() + "@" + Integer.toHexString(hashCode());

}

*객체 자신의 정보를 문자열로 반환

 

@IntrinsicCandidate

public final native void notify();

*객체 자신을 사용하려고 기다리는 스레드를 하나만 깨운다

 

@IntrinsicCandidate

public final native void notifyAll();

*객체 자신을 사용하려고 기다리는 모든 스레드를 깨운다

 

public final void wait() throws InterruptedException {

        wait(0L);

}

---------------------------------------------------------------------------------------------------------

public final native void wait(long timeoutMillis) throws InterruptedException;

---------------------------------------------------------------------------------------------------------  

public final void wait(long timeoutMillis, int nanos) throws InterruptedException {

        if (timeoutMillis < 0) {

                throw new IllegalArgumentException("timeoutMillis value is negative");

        }

        if (nanos < 0 || nanos > 999999) {

                throw new IllegalArgumentException(

                "nanosecond timeout value out of range");

        }

        if (nanos > 0 && timeoutMillis < Long.MAX_VALUE) {

              timeoutMillis++;

        }

        wait(timeoutMillis);

}

*다른 스레드가 notify()나 notifyAll() 을 호출할 때까지

현재 스레드를 무한히 또는 지정된 시간(timeout, nanos)동안 기다리게 함

**timeout = 1/1000초, nanos = 1/10^9초

 

 

1.1 equals(Object obj)

"참조변수"를  매개변수로 받아 비교하여 결과값을 boolean 으로 알려주는 역할

public static void main(String[] args) {

       Value value1 = new Value(10);

       Value value2 = new Value(10);

 

        if (value1.equals(value2)) {

              System.out.println("value1 과 value2 는 같습니다.");

        } else {

                System.out.println("value1 과 value2 는 다릅니다.");

        }

 

        value1 = value2;

 

        if (value1.equals(value2)) {

                System.out.println("value1 과 value2 는 같습니다.");

        } else {

                System.out.println("value1 과 value2 는 다릅니다 .");

        }

}

 

equal 메서드 오버라이딩

equal 메서드로 참조변수의 주소값이 아닌

value 값을 비교하도록 만들기 위한 절차

class Person {

       long id;

 

       public boolean equals(Object obj) { //obj 는 Object 타입

               if (obj instanceof Person)

                        return id == ((Person) obj).id; //obj 를 Person 타입으로 변환하여 비

                else

                        return false;

        }

       Person(long id) {

                this.id = id;

        }

}

 

       public class EqualsEx2 {

        public static void main(String[] args) {

        Person p1 = new Person(1231232L); //p1 id 값을 지정해 놓은 생성자

        Person p2 = new Person(1243425L); //p2 id 값을 지정해 놓은 생성자

       }

* String 클래스의 equals 메서드도 이처럼 오버라이딩하여 문자열 값을 비교하도록 되어있다

**String 뿐만 아니라 Date, File, wrapper클래스(Integer, Double 등)도 그렇다

***StringBuffer 클래스는 예외

 

1.2 hashCode() 

hashing 기법에 사용되는 hash 함수를 구현한 것

해시함수는 찾고자 하는 값을 입력하면, 그 값이 저장된 '위치'를 알려주는 '해시코드'를 반환한다

*hashing: 데이터 관리기법 중 하나로, 다량의 데이터를 저장하고 검색하는 데 유용

**8byte 의 주소값으로 4byte 의 해시코드를 만들기 때문에 중복될 수 있음

 

Object 클래스의 hashCode() 와 String 클래스의 hashCode() 의 차이

Object 객체에서는 같은 주소를 가진 Object만이 같은 해시코드를 갖는다.

String 객체에서는 같은 문자열을 가진 String은 같은 해시코드를 갖는다.

 

클래스의 객체의 같고 다름을 인스턴스변수 값으로 판단하고자 한다면

equals 메서드 뿐 아니라 hashCode 메서드도 오버라이딩해야 한다.

객체라면 hashCode 메서드를 호출했을 때의 결과인 해시코드도 같아야 하기 때문이다

*HashMap 이나 HashSet 과 같은 클래스에 저장할 객체라면 반드시 hashCode 메서드를 오버라이딩 해야 한다

   String str1 = new String("abc");

   String str2 = new String("abc");

 

   System.out.println(str1.equals(str2)); //true - String 이므로!

   System.out.println(str1.hashCode()); // hashCode 동일

   System.out.println(str2.hashCode()); //  String.hashCode() 는 문자열을 기준으로 hashCode 생성

   System.out.println(System.identityHashCode(str1)); // 두 hashCode 다름

   System.out.println(System.identityHashCode(str2)); // System.identityHashCode() 는 참조값을 기준으로 hasCode 생성

 

1.3 toString() 

 

인스턴스에 대한 정보를 문자열로 제공할 목적으로 정의된 메서드

* 인스턴스에 대한 정보를 문자열로 제공한다는 것은 인스턴스 변수에 저장된 값들을 문자열로 표현한다는 뜻이다

public String toString() {

       return getClass().getName() + "@" + Integer.toHexString(hashCode()); //16진수해시코드 반환

}

 

String 클래스에서 오버라이드한 toString()

public String toString() {

       return this;

}

 

출력결과가 "Korea" 인 이유는 println() 메서드에 있다

public static void main(String[] args) {

       String str = "Korea";

       System.out.println(str);

}

 

PrintStream 클래스의 println 메서드와 print 메서드

public void println(String x) {

       if (getClass() == PrintStream.class) {

                writeln(String.valueOf(x));

        } else {

                synchronized (this) {

                        print(x);

                        newLine();

                }

        }

}

public void print(String s) {

       write(String.valueOf(s));

}

따라서 str 의 value 값이 출력되는 것

 

다시 앞서 본 String 클래스의 toString 메서드로 올라가보면

public String toString() {

       return this;

}

이 상태이기 때문에, String 클래스의 toString 을 사용하고자 한다면 오버라이딩이 필수인 것이다

 

1.4 clone()

자신을 복제하여 새로운 인스턴스를 생성함

단순히 인스턴스변수의 값만 복사하기 때문에

참조타입의 인스턴스 변수가 있는 클래스는 완전한 인스턴스 복제가 이루어지지 않음

 

배열을 복제할 경우, 복제된 인스턴스도 같은 배열의 주소를 갖기 때문에

복제된 인스턴스의 작업이 원래의 인스턴스에 영향을 미친다

따라서 이런 경우엔 clone 메서드를 오버라이딩하여

새로운 배열을 생성하고 배열 내용을 복사하도록 해야 한다

 

clone 을 사용하려면

복제할 클래스가 Cloneable 인터페이스를 구현해야 하고

clone() 을 오버라이딩하면서 protected 를 public 으로 변경해야 한다

 

 

Object 클래스의 clone() 메서드

@IntrinsicCandidate

protected native Object clone() throws CloneNotSupportedException;

* native 메서드

더보기

native 메서드

 

Java 가상 머신이 아닌 외부에서 제공되는 코드를 호출함

네이티브 메서드의 실제 구현은 보통 네이티브 코드(C, C++ 등)로 작성

 

* 네이티브 메서드는 Java의 객체 지향 원칙을 어기는 경향이 있으므로, 적절한 상황에서만 사용해야 함

 

Point 클래스에서 오버라이딩한 clone 메서드

public class Point implements Cloneable {

              ...

        public Object clone() {

                Object obj = null;

                try {

                        obj = super.clone();

                } catch (CloneNotSupportedException e) { }

                return obj;

        }

}

 

clone() 메서드 사용

Point original = new Point(3, 5);

Point copy = (Point) original.clone();

 

공변 반환타입(covariant return type)

오버라이딩할 때 조상메서드의 반환타입을 자손 클래스의 타입으로 변경을 허용하는 것

public Point clone() {

Object obj = null;

try {

obj = super.clone();

} catch (CloneNotSupportedException e) { }

return (Point)obj;

}

 

이렇게 반환타입을 변경하면 clone() 메서드 사용시 형변환을 하지 않아도 된다

Point original = new Point(3, 5);

Point copy = original.clone(); //Point 로 형변환할 필요가 없어짐

 

배열의 복사

//clone() 을 이용한 복사

int[] arr = {1, 2, 3, 4, 5};

int[] arrClone = arr.clone();

 

 //arraycopy() 를 이용한 복사

int[] arr = {1, 2, 3, 4, 5};

int[] arrClone = new int[arr.length];

System.arraycopy(arr,0,arrClone,0,arr.length);

더보기

*arraycopy 배열

public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

src: 복사할 배열, srcPos: 복사를 시작할 인덱스,

dest: 복사된 요소가 저장될 배역, destPos: 붙여넣기를 시작할 인덱스, length: 복사할 요소의 개수

배열 뿐 아니라 java.util 패키지의

Vector, ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap, Calendar, Date 클래스들도

clone 을 이용해 복제가 가능하다

*복제 가능 여부는 Cloneable 을 구현하였는지 확인하면 됨

 

 

얕은 복사와 깊은 복사

 

얕은 복사(shallow copy)

clone 은 단순히 객체에 저장된 값을 복제할 뿐

객체가 참조하고 있는 객체까지 복제하지는 않는다

이 경우 기본형 배열인 경우에는 아무런 문제가 없지만,

객체배열을 clone() 으로 복제하는 경우에는 원본과 복제본이 같은 객체를 공유하므로

완전한 복제라고 보기 어렵다

얕은 복사에서는 원본을 변경하면 복사본도 영향을 받는다

public Circle clone() { //얕은 복사 - 복사본과 원본이 같은 인스턴스를 가리킴

       Object obj = null;

        try {

                obj = super.clone();

        } catch ( CloneNotSupportedException e) { }

        return (Circle) obj;

}

 

깊은 복사(deep copy)

원본이 참조하고 있는 객체까지 복제하는 것

원본과 복사본이 서로 다른 객체를 참조하기 때문에

원본의 변경이 복사본에 영향을 미치지 않는다

public Circle clone() { //깊은 복사 - 복사본과 원본이 서로 다른 인스턴스르 가리킴

        Object obj = null;

        try {

                obj = super.clone();

        } catch ( CloneNotSupportedException e) { }

        Circle c = (Circle) obj;

        c.p = new Point(this.p.x, this.p.y); //새로운 Point 인스턴스를 가리킴

        return c;

}

 

 

1.5 getClass()

자신이 속한 클래스의 'Class 객체'를 반환하는 메서드

Class 객체는 모든 정보를 담고 있으며, 클래스 당 1개만 존재함

클래스 파일이 '클래스 Loader'에 의해서 메모리에 올라갈 때, 자동으로 생성됨

더보기

클래스 로더

 

실행 시 필요한 클래스를 동적으로 메모리에 로드하는 역할

기존에 생성된 클래스 개체가 메모리에 존재하는지 확인하고,

있으면 객체의 참조를 반환하고,

없으면 클래스 패스(path)에 지정된 경로를 따라 클래스 파일을 읽는다

못 찾으면 ClassNotFoundException 이 발생하고,

찾으면 해당 클래스 파일을 읽어서 Class 객체로 반환한다

그러니까 파일 형태로 저장되어 있는 클래스를 읽어서 Class 클래스에 정의된 형식으로 변환하는 것이다

 

Class 객체를 얻는 방법

Class cObj = new Card().getClass(); //생성된 객체로부터 얻는 방법

Class cObj = Card.class; // 클래스 리터럴(*.class)로부터 얻는 방법

Class cObj = Class.forName("Card"); // 클래스 이름으로부터 얻는 방법

*특히 forName() 은 특정 클래스 파일, 예를 들어 데이터베이스 드라이버를 메모리에 올릴 때 주로 사용

 

Class 객체를 이용하면 클래스에 정의된

멤버 이름이나 개수 등 클래스에 대한 모든 정보를 얻을 수 있기 때문에

Class 객체를 통해서 객체를 생성하고 메서드를 호출하는 등

동적인 코드를 작성할 수 있다

 

 

 

 

 

 

 

 

 

 

'Organizing Docs > Java Docs' 카테고리의 다른 글

자바의 정석 11. 컬렉션 프레임웍(Collection Framework) 01  (0) 2024.07.07
ListIterator  (0) 2024.07.07
String - hashCode()  (0) 2024.07.06
자바의 정석 8. 예외처리  (1) 2024.07.05
Coding 01  (0) 2024.07.04