7. 기본 API 클래스 1
API란?
Application Programming Interface의 약자이다.
컴포넌트들을 결합하기 위한 매개체 인터페이스이다.
호출을 위한 수단이다.
내부 구현을 볼 수 없는 경우도 많지만, 자바 내부의 표준 API들은 내부 구현을 볼 수 있다.
API들은 경로
\jre\lib\rt.jar
라는 압축 파일에 저장되어 있다.자바 API 도큐먼트에서 API의 내용을 확인할 수 있다. API 클래스의 필드, 생성자, 메소드 등을 확인할 수 있다.
java.lang 패키지
자바 프로그램의 기본적인 클래스를 담고 있는 패키지로 import
없이 사용할 수 있다.
java.lang 대표 내장 클래스
Object
자바 클래스의 최상위 클래스로 사용
System
표준 입력 장치(키보드)로부터 데이터를 입력받을 때 사용
표준 출력 장치(모니터)로 출력하기 위해 사용
자바 가상 기계를 종료시킬 때 사용
쓰레기 수집기를 실행 요청할 때 사용
Class
클래스를 메모리로 로딩할 때 사용
String
문자열을 저장하고 여러가지 정보를 얻을 때 사용
StringBuffer
,StringBuilder
문자열을 저장하고 내부 문자열을 조작할 때 사용
Math
수학 함수를 이용할 때 사용
Wrapper
Byte
,Short
,Character
,Integer
,Float
,Double
,Boolean
,Long
primitive type을 감싸는(Wrapping) 클래스
기본 타입의 데이터를 갖는 객체를 만들 때 사용
primitive 타입에 null이 불가한 특성 등을 해결할 때
편의 메소드를 이용하고 싶을 때
문자열을 기본 타입으로 변환할 때 사용
입력값 검사에 사용
java.util 패키지
주로 컬렉션 클래스들이 있다.
Arrays
배열을 조작(비교, 복사, 정렬, 찾기)할 때 사용
Calendar
운영체제의 날짜와 시간을 얻을 때 사용
Date
날짜와 시간 정보를 저장하는 클래스
Objects
객체 비교, 널(
null
) 여부 등을 조사할 때 사용
StringTokenizer
특정 문자로 구분된 문자열을 뽑아낼 때 사용
Random
난수를 얻을 때 사용
Object 클래스 (java.lang)
모든 클래스가 기본적으로 상속하는 최상위 부모 클래스이다.
객체 비교 (Object.equals())
파라미터가 Object
타입이라 모든 클래스를 전부 파라미터로 받을 수 있다. 또한, Object
클래스는 모든 클래스의 최상위 클래스이기 때문에 모든 클래스는 .equals()
메소드를 갖는다.
Object
클래스의 .equals()
메소드는 기본적으로 ==
연산자와 동일한 동작을 갖는다. 하지만 .equals()
메소드는 Object
에서 기본으로 제공하는 형태로는 거의 쓰이지 않고 메소드의 내용을 오버라이드하여, .equals()
메소드의 동작을 두 객체가 논리적으로 동일한지 판단하는데 쓴다.
이를테면 String
클래스에서는 .equals()
가 객체의 주소를 비교하지 않고, 오직 문자열의 내용을 비교하도록 오버라이딩 되어있다.
.equals()
메소드를 오버라이드할 때는 인자로Object
타입의 객체를 받기 때문에 사실상 어떠한 클래스의 객체도 올 수 있다. 그래서instanceof
연산자로 해당 객체가 내가 원하는 클래스의 객체인지 확인하는 작업이 가장 먼저 필요하다.
객체 해시코드(hashCode())
.hashCode()
는 객체 식별에 사용되는 유일한 정수값을 반환한다.유일하기 때문에, 모든 객체는 다른 정수값을 반환한다.
논리적 동등을 완벽하게 비교하려면
.equals()
와.hashCode()
를 둘 다 오버라이드 해야 한다.컬렉션 프레임워크
HashSet
,HashMap
,Hashtable
는.hashCode()
와.equals()
두 메소드 모두를 이용하여 논리적 동등을 비교한다..hashCode()
를 수행하고 같은 반환값을 가지는지 먼저 확인하고.equals()
를 통해 다시 한번 같은 반환값을 가지는지 확인한다.
그렇다면 객체 해시코드를 반환하는
.hashCode()
메소드를 오버라이드 해버리면 유일한 값은 어떻게 찾아와야 할까? 그 해답은 바로System.identityHascode()
메소드를 사용하면 기존의.hashCode()
와 같은 기능을 이용할 수 있다.
HashMap 코드로 살펴보기
Key 클래스
Key
라는 클래스를 새로 작성하여.equals()
메소드와.hashCode()
메소드를 위와 같이 오버라이딩 하였다.클래스 멤버 중
id
로만 논리적 동치 판단을 하도록 구성하였다.
Main 클래스
HashMap
객체를 생성하고,Key
의 타입은 위에서 직접 만든Key
클래스로 하였다..put()
메소드로 값을 넣을 때 사용한 키는key1
변수이다..get()
메소드로 값을 가져올 때 사용한 키는key2
변수이다.하지만, 둘의
id
필드의 값은 같고,.equals()
와.hashCode()
를 그에 맞게 구현했기에 논리적 동치가 가능하다.
실행 결과
.hashCode()
메소드나.equals()
메소드가 호출되면 콘솔에 출력하도록 프로그래밍 하였다..hashCode()
가 비교를 위해 2번 호출되는 것을 볼 수 있다..equals()
는 한번만 호출되어도 비교가 가능하여 1번 호출된 것을 볼 수 있다.
결과적으로
key1
과key2
가HashMap
내부 구현에 따라 논리적 동치로 판단되고 저장했던 문자열인홍길동
이 출력된 것을 볼 수 있다.
객체 문자 정보(toString())
.toString()
메소드는 객체의 문자 정보를 출력한다.기본 구현은 객체의 주소인
클래스명@16진수해시코드
를 리턴한다.
보통 번지수는 사용자의 관심사가 아니어서 의미없는 정보이기 때문에, 오버라이드하여 사용자의 관심사인 필드의 내용 등을 출력하는 방식으로 많이 고쳐쓴다.
객체 복제(clone())
원본 객체의 필드값과 동일한 값을 가지는 새로운 객체를 생성하는 메소드이다.
원본 객체의 안전을 보장하기 위해 많이 사용된다.
이 메소드를 사용하려면 클래스에서
Cloneable
인터페이스를 구현해야 한다.구현하지 않으면, 설계자가 복제를 허용하지 않는다는 뜻이다.
구현하지 않은 채로
.clone()
메소드를 쓰면CloneNotSupportedException
예외가 발생한다.
CloneNotSupportedException
예외처리가 필요하기 때문에try-catch
구문이 필요하다.
getter
의 결과로 복제된 클래스를 내보내면, 클래스의 불변성을 지킬 수 있다.
얕은 복제(thin clone)
단순히 필드 값만 복사해서 객체를 복제하는 것을 말한다.
참조 객체를 복사할 때는 동일한 객체를 생성하는 것이 아닌 단순히 주소만 복사한다.
얕은 복제(thin clone) 코드 테스트
CloneableClass
클래스 작성,.getCloneableClass()
메소드 호출 시에Object
에서 상속받은.clone()
메소드를 그대로 사용하여 반환한다.
위와 같이 그냥 기본으로
Object
에서 상속받은.clone()
메소드를 사용하면 얕은 복제가 된다.
Main
클래스 작성,System.identityHashCode()
로 두 객체가 가지고 있는id
필드의Integer
객체가 같은 객체인지 해시코드를 출력해본다.
위는 얕은 복제를 한 객체를 직접 출력해보는 소스와 결과다.
CloneableClass
의 객체들은 각각 다른 주소를 가지고 있지만, 내부에 있는Integer
클래스 필드는 같은 객체를 가리키고 있다.Integer
클래스가 각각 다른 객체를 가리키게 만드려면 깊은 복제(deep clone)가 필요하다.
깊은 복제(deep clone)
참조하고 있는 객체도 새로 생성하여 복제한다.
깊은 복제를 원할 때는
.clone()
메소드를 재정의하여 직접 참조 객체를 복사하는 코드를 작성해야 한다.
깊은 복제(deep clone) 코드 테스트
CloneableClass
클래스 재작성
clone()
메소드를 오버라이드하여, 매번clone()
메소드가 호출될 때마다Integer
객체를 새롭게 생성하도록 바꾸었다.
Main
클래스 재작성
오버라이드된
clone()
메소드가 정상적으로 새로운Integer
객체를 만들어내는지 다시한번System.identityHashCode()
메소드를 통해 테스트
서로 다른 hash 값을 반환하는 것을 확인했다.
객체 소멸자(finalize())
객체가 가비지 콜렉터에 의해 소멸당하기 전에 마지막으로 실행시키는 메소드이다.
객체 소멸 전에 마지막으로 사용했던 자원을 닫고 싶거나 데이터를 저장하고 싶다면
Object
의.finalize()
를 재정의할 수 있다.가비지 콜렉터는 객체가 쓰레기가 된 그 순간에 바로 작동하는 것이 아니다.
System.gc()
메소드를 호출하여 가급적 빨리 쓰레기 수집기를 돌릴 수 있다.
가비지 콜렉터는 객체를 순서대로 소멸시키지도 않는다.
그래서
finalize()
메소드가 호출되는 시점 자체도 명확하지 않다.그래서 사실은 리소스 회수는 그냥 일반 메소드에 명시적으로 호출하는 것이 좋다.
Objects 클래스
대략적 정적 메소드들의 기능
객체 비교
해시코드 생성
null 여부
객체 문자열 리턴
정적 메소드 목록
int compare(T a, T b, Comparator<T> c)
: Comparator를 이용하여 두 객체 a와 b를 비교한다.boolean deepEquals(Object a, Object b)
: 두 객체의 깊은 비교(배열의 항목까지 비교)boolean equals(Object a, Object b)
: 두 객체의 얕은 비교(주소만 비교)int hash(Object... values)
: 매개값이 저장된 배열의 해시코드 생성boolean isNull(Object obj)
: 객체가 null인지 조사boolean nonNull(Object obj)
: 객체가 null이 아닌지 조사T requireNonNull(T obj)
: 객체가 null이면 예외 발생T requireNonNull(T obj, String message)
: 객체가 null인 경우 예외 발생(주어진 예외 메시지 포함)T requireNonNull(T obj, Supplier<String> messageSupplier)
: 객체가 null인 경우 예외 발생(람다식이 만든 예외 메시지 포함)String toString(Object o)
: 객체의.toString()
리턴 값을 리턴String toString(Object o, String nullDefault)
: 객체의.toString()
리턴 값 리턴, 첫번째 매개값이 null인 경우, 두번째 매개값 리턴
객체 비교(compare(T a, T b, Comparator<T>c)
)
compare(T a, T b, Comparator<T>c)
)Objects.compare(T a, T b, Comparator<T>c)
메소드는 두 객체를 비교자(Comparator)로 비교해서 int
값을 리턴한다. java.util.Comparator<T>
는 제네릭 인터페이스 타입으로 두 객체를 비교하는 compare(T a, T b)
메소드 시그니처가 있다.
.compare()
메소드는 int
타입을 리턴하며, 일반적으로 a
와 b
를 비교하여 a
가 b
보다 작으면 음수(Minus)
, 같으면 0(Zero)
, 크면 양수(Plus)
를 리턴하도록 구현 클래스를 만든다.
Wrapper
클래스에는 일반적으로 위와 같이 2가지의 인자를 비교하여 작으면 음수, 같으면 0, 크면 양수를 리턴하는 메소드가 구현되어 있다.Integer.compare()
와 같은 형식으로 구현되어 있다.
나중에는 위의
.compare()
메소드를 이용하여 컬렉션에 대한 정렬 등을 수행할 수도 있다. 또한 직접 만든 클래스를 정렬하고 싶다면,Comparator<>
혹은Comparable<>
인터페이스를 상속하여,.compare()
메소드를 오버라이드하면 된다.
Comparable 상속을 이용한 컬렉션 정렬 예제
Student 클래스
StudentTest 클래스
나는 컬렉션의 .sort()
에 대해 간단히 comapre
메소드가 음수
일 때만 변화를 준다고 이해하고 있다. 왜냐하면 음수
외에 다른 어떤 값을 주어도 변화가 일어나지 않기 때문이다. 무조건 음수
만 반환하게 return -1;
을 하면 순서가 반대로된 리스트가 반환된다.
Last updated