JongDDA의 한걸음 한걸음씩
[Java] java.lang 패키지 & util classes 본문
java.lang 패키지는 자바프로그래밍에 가장 기본이 되는 클래스들을 포함하고 있다. 그렇기 때문에 java.lang 패키지의 클래스들은 import문 없이도 사용할 수 있게 되어 있다.
Object 클래스
Object 클래스는 모든 클래스의 최고 조상이기 때문에 Object 클래스의 멤버들은 모든 클래스에서 바로 사용 가능하다.
Object 클래스의 메서드 | 설명 |
protected Object clone() | 객체 자신의 복사본을 반환한다. |
public boolean equals (Object obj) | 객체 자신과 객체 obj가 같은 객체인지 알려준다. |
protected void finalize() | 객체가 소멸될 때 가비지 컬렉터에 의해 자동적으로 호출된다. 이 때 수행 되어야하는 코드가 있을 때 오버라이딩한다. |
public Class getClass() | 객체 자신의 클래스 정보를 담고 있는 Class 인스턴스를 반환한다. |
public int hashCode() | 객체 자신의 해시코드를 반환한다. |
public String toString() | 객체 자신의 정보를 문자열로 반환한다. |
public void notify() | 객체 자신을 사용하려고 기다리는 쓰레드를 하나만 깨운다. |
public void notifyAll() | 객체 자신을 사용하려고 기다리는 모든 쓰레드를 깨운다. |
public void wait() public void wait(long timeout) public void wait(long timeout, int nanos) |
다른 쓰레드가 notify()나 notifyAll()을 호출할 때까지 현재 쓰레드를 무한히 또는 지정된 시간(timeout, nanos)동안 기다리게 한다. |
Object 클래스는 멤버변수는 없고 오직 11개의 메서드만 가지고 있다. 이 메서드들은 모든 인스턴스가 가져야 할 기본적인 것들이다.
Object 클래스의 메서드 - equals()
매개변수로 객체의 참조변수를 받아서 비교하여 그 결과를 boolean 값으로 알려주는 역할을 한다.
public boolean equals(Object obj){
return (this == obj);
}
두 객체의 같고 다름은 참조변수의 값으로 판단한다. 따라서 서로 다른 두 객체를 equals 메서드로 비교하면 항상 false를 결과로 얻게된다.
EX)
public class EqualsEx {
public static void main(String[] args) {
Value v1 = new Value(10);
Value v2 = new Value(10);
if(v1 == v2) {
System.out.println("같은 객체");
}else {
System.out.println("다른 객체");
}
System.out.println("--------------------------------");
if(v1.equals(v2)) {
System.out.println("같은 객체");
}else {
System.out.println("다른 객체");
}
}
}
class Value{
int value;
Value(int value){
this.value = value;
}
}
equals 메소드는 주소값으로 비교를 하기 때문에 값이 같을지라도 결과는 false로 출력한다.
equals()의 오버라이딩
Object 클래스로부터 상속받은 equals메서드는 결국 두 개의 참조변수가 같은 객체를 참조하고 있는지, 즉 두 참조변수에 저장된 값(주소값)이 같은지를 판단하는 기능밖에 할 수 없다는 것을 알 수 있다. 하지만 오버라이딩을 통해 주소가 아닌 객체에 저장된 내용을 비교하도록 변경이 가능하다.
public class EqualsEx {
public static void main(String[] args) {
EqualsOverriding eo1 = new EqualsOverriding(10);
EqualsOverriding eo2 = new EqualsOverriding(10);
if(eo1.equals(eo2)) {
System.out.println("같은 값");
}else {
System.out.println("다른 값");
}
}
}
class EqualsOverriding {
int num = 0;
public boolean equals(Object obj) {
if(obj instanceof EqualsOverriding) {
return num == ((EqualsOverriding)obj).num; // 형변환
}else {
return false;
}
}
EqualsOverriding(int num){
this.num = num;
}
}
Object 클래스의 메서드 - hashCode()
hashCode() 메서드는 해싱(hashing)기법에 사용되는 해시함수(hash function)를 구현한 것이다. 해싱은 데이터관리기법 중의 하나인데 대량의 데이터를 저장하고 검색하는데 유용하다. 해시함수는 찾고자하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시코드(hash code)를 반환한다.
일반적으로 해시코드가 같은 두 객체가 존재하는 것이 가능하지만, Object 클래스에 정의된 hashCode 매서드는 객체의 주소값을 이용해서 해시코드를 만들어 반환하기 때문에 서로 다른 두 객체는 결코 같은 해시코드를 가질 수 없다.
클래스의 인스턴스변수 값으로 객체의 같고 다름을 판단해야하는 경우 equals 메서드 뿐 아니라 hashCode 메서드도 적절히 오버라이딩해야 한다. 같은 객체라면 hashCode 매서드를 호출했을떄 결과값인 해시코드도 같아야 한다.
public class Ex {
public static void main(String[] args) {
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(str1.equals(str2));
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
System.out.println(System.identityHashCode(str1));
System.out.println(System.identityHashCode(str2));
}
}
String 클래스는 문자열의 내용이 같으면 동일한 해시코드를 반환하도록 hashCode 매서드가 오버라이딩되어 있기 때문에, 문자열의 내용이 같은 str1과 str2에 대해 hashCode()를 호출하면 항상 동일한 해시코드값을 얻는다.
반면에 identityHashCode()는 Object 클래스의 hashCode 메서드처럼 객체의 주소값으로 해시코드를 생성하기 떄문에 모든 객체에 대해 항상 다른 해시코드값을 반환할 것을 보장한다. 따라서 str1과 str2가 해시코드는 같지만 서로 다른 객체라는 것을 알 수 있다.
Object 클래스의 메서드 - toString()
toString() 매서드는 인스턴스에 대한 정보를 문자열(String)로 제공할 목적으로 정의한 것이다.
public String toString(){
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
public class Card {
String kind;
int number;
Card(){
this("SPADE", 1);
}
Card(String kind, int number){
this.kind = kind;
this.number = number;
}
}
class Ex {
public static void main(String[] args) {
Card c1 = new Card();
Card c2 = new Card();
System.out.println(c1.toString());
System.out.println(c2.toString());
}
}
Card 인스턴스 두 개를 생성한 다음, 각 인스턴스에 toString()을 호출한 결과이다. 서로 다른 인스턴스에 대해서 toString()을 호출하였으므로 클래스의 이름은 같아도 해시코드값이 다르다는 것을 확인할 수 있다.
toString()의 오버라이딩
public class Card2 {
String kind;
int number;
Card2(){
this("SPADE", 1);
}
Card2(String kind, int number){
this.kind = kind;
this.number = number;
}
public String toString(){
return "kind : " + kind +", number : " + number; // Card2 인스턴스의 kind와 number를 문자열로 반환한다.
}
}
class Ex {
public static void main(String[] args) {
Card2 c1 = new Card();
Card2 c2 = new Card();
System.out.println(c1.toString());
System.out.println(c2.toString("Java",10));
}
}
Card2 인스턴스의 toString()을 호출하면 인스턴스가 갖고 있는 인스턴스 변수 kind와 number의 값을 문자열로 반환하여 반환하도록 toString()을 오버라이딩 하였다.
String 클래스
String 클래스는 문자열을 저장하고 이를 다루는데 필요한 메서드를 제공한다.
변경불가능한(immutable) 클래스
String 클래스에는 문자열을 저장하기 위해서 문자형 배열 참조변수(char []) value를 인스턴스 변수로 정의해놓고 있다. 인스턴스 생성 시 생성자의 매개변수로 입력받는 문자열은 이 인스턴스변수(value)에 문자형 배열(char[])로 저장되는 것이다. 한번 생성된 String 인스턴스가 갖고 있는 문자열은 읽어 올 수만 있고, 변경할 수는 없다.
문자열(String)의 비교
public class StringEx {
public static void main(String[] args) {
String str1 = "abc";
String str2 = "abc";
String str3 = new String("abc");
String str4 = new String("abc");
System.out.println("str1 == str2 ? " + (str1 == str2));
System.out.println("str1.equals(str2) ? " + (str1.equals(str2)));
System.out.println();
System.out.println("str3 == str4 ? " + (str3 == str4));
System.out.println("str3.equals(str4) ? " + (str3.equals(str4)));
}
}
문자열을 만들 떄는 문자열 리터럴을 지정하는 방법과 String 클래스의 생성자를 사용해서 만드는 2가지 방법이 있다.
String 클래스의 생성자를 이용한 경우에는 new 연산자에 의해서 메모리 할당이 이루어지기 때문에 항상 새로운 String 인스턴스가 생성된다. 그러나 문자열 리터럴은 이미 존재하는 것을 재사용한다. 따라서 String 인스턴스의 주소를 등가비교연산자로 비교했을 때 String 클래스의 생성자를 이용한 경우 다른 결과가 나온다.
String 클래스의 생성자와 메서드
String 클래스 내에는 많은 생성자와 메서드들이 정의되어 있다. 몇 가지만 살펴보자
메서드 | 설명 |
String(String s) | 주어진 문자열(s)을 갖는 String 인스턴스를 생성한다. |
String(char[] value) | 주어진 문자열(value)을 갖는 String인스턴스를 생성한다. |
char charAt(int index) | 지정된 위치(index)에 있는 문자를 알려준다. |
String concat(String str) | 문자열(str)을 뒤에 덧붙인다. |
int indexOf(int ch, int pos) | 주어진 문자(ch)가 문자열에 존재하는지 지정된 위치(pos)로부터 확인하여 위치(index)를 알려준다. 못 찾으면 -1을 반환한다. (index는 0부터 시작) |
boolean contains(CharSequence s) | 지정된 문자열(s)가 포함되어 있는지 검사한다. |
int length | 문자열의 길이를 알려준다. |
String replace(char old, char nw) | 문자열 중의 문자(old)를 새로운 문자(nw)로 바꾼 문자열을 반환한다. |
int compareTo(String str) | 문자열(str)과 사전순서로 비교한다. 같으면 0을, 사전순으로 이전이면 음수를, 이후면 양수를 반환한다. |
String[] split(String regex) | 문자열을 지정된 분리자(regex)로 나누어 문자열 배열에 담아 반환한다. |
String substring(int begin) String substring(int begin, int end) |
주어진 시작위치(begin)부터 끝 위치(end)범위에 포함된 문자열을 얻는다. 이 때, 시작위치의 문자는 범위에 포함되지만. 끝 위치의 문자는 포함되지 않는다. |
String toLowerCase() | String인스턴스에 저장되어있는 모든 문자열을 소문자로 변환하여 반환한다. |
String toUpperCase() | String인스턴스에 저장되어있는 모든 문자열을 대문자로 변환하여 반환한다. |
String toString() | String인스턴스에 저장되어 있는 문자열을 반환한다. |
String trim() | 문자열의 왼쪽끝과 오른쪽 끝에 있는 공백을 없앤 결과를 반환한다. 이 때 문자열 중간에 있는 공백은 제거되지 않는다. |
static String valueOf() | 지정된 값을 문자열로 변환하여 반환한다. |
StringBuffer 클래스
String 클래스는 인스턴스를 생성할 때 지정된 문자열을 변경할 수 없지만 StringBuffer클래스는 변경이 가능하다. 내부적으로 문자열 편집을 위한 버퍼(Buffer)를 가지고 있으며, StringBuffer인스턴스를 생성할 떄 그 크기를 지정할 수 있다.
StringBuffer 생성자
StringBuffer 클래스의 인스턴스를 생성할 때, 적절한 길이의 char형 배열이 생성되고, 이 배열은 문자열을 저장하고 편집하기 위한 공간(Buffer)으로 사용된다.
public StringBuffer(int length){
value = new char[length];
shared = false;
}
public StringBuffer(){
this(16); // 버퍼의 크기를 지정하지 않으면 버퍼의 크기는 16이 된다.
}
public StringBuffer(String str){
this(str.length() +16); // 지정한 문자열의 길이보다 16 더 크게 버퍼를 생성
append(str);
}
StringBuffer를 이용하여 다음과 같이 내용 변경이 가능하다.
public class StringEx {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("abc");
System.out.println(sb);
sb.append("123"); // sb내용 뒤에 "123" 추가
System.out.println(sb);
StringBuffer sb2 = sb.append("AA"); // 뒤에 "AA" 내용추가
System.out.println(sb2);
System.out.println(sb);
}
}
sb 와 sb2는 모두 같은 StringBuffer인스턴스를 가리키고 있으므로 같은 내용이 출력된다.
StringBuffer의 비교
StringBuffer는 equals메서드를 오버라이딩하지 않아서 StringBuffer클래스의 equals 메서드를 사용해도 등가비교연산자(==)로 비교한 것과 같은 결과를 얻는다.
따라서 비교를 위해 toString을 호출하여 String 인스턴스를 얻은 다음, 여기에 equals 메서드를 사용해서 비교해야한다.
public class StringEx {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");
System.out.println("sb == sb2 ? : " + (sb == sb2));
System.out.println("sb.equals(sb2) ? : " + (sb.equals(sb2)));
// StringBuffer의 내용을 String으로 반환한다.
String s = sb.toString();
String s2 = sb2.toString();
System.out.println("s.equals(s2) ? : " + (s.equals(s2)));
}
}
Math 클래스
Math 클래스는 기본적인 수하계산에 유용한 메서드로 구성되어 있다. Math 클래스의 생성자는 접근 제어자가 private이기 때문에 다른 클래스에서 Math인스턴스를 생성할 수 없도록 되어있다. 그 이유는 클래스 내에 인스턴스변수가 하나도 없어서 인스턴스를 생성할 필요가 없기 때문이다. Math 클래스는 모두 static 이며 자연로그(e), 원주율(PI) 2개의 상수만 정의해 놓았다.
Math의 메서드
자주쓰이는 몇가지만 살펴보자
매서드 | 설명 |
static 자료형 abs() | 주어진 값의 절대값을 반환 |
static double cell(double a) | 주어진 값을 올림하여 반환 |
static double floor(double a) | 주어진 값을 내림하여 반환 |
static double max(double a, double b) | 주어진 두 값을 비교하여 큰 쪽을 반환 |
static double min(double a, double b) | 주어진 두 값을 비교하여 작은 쪽을 반환 |
static double random() | 0.0~1.0 범위의 임의의 double값을 반환한다. |
static long round(double a) | 소수점 첫째자리에서 반올림한 정수값(long)을 반환한다. 두 정수의 정가운데 있는 값은 항상 큰 정수를 반환 |
static double rint(double a) | 주어진 double값과 가장 가까운 정수값을 double형으로 반환한다. 단, 두 정수의 정가운데 값은 짝수로 반환 |
래퍼(wrapper) 클래스
때로 기본형(primitive type) 변수도 어쩔 수 없이 객체로 다뤄야 하는 경우가 있다. 이 때 사용되는 것이 래퍼(wrapper)클래스이다. 자바에서 8개의 기본형을 대표하는 8개의 래퍼클래스가 있고, 이 클래스들을 이용하면 기본형 값을 객체로 다룰 수 있다.
기본형 | 래퍼클래스 | 생성자 |
boolean | Boolean | Boolean(boolean value) Boolean(String s) |
char | Character | Character(char value) |
byte | Byte | Byte(byte value) Byte(String s) |
short | Short | Short(short value) Short(String s) |
int | Integer | Integer(int value) Integer(String s) |
long | Long | Long(long value) Long(String s) |
float | Float | Float(double value) Float(float value) Float(String s) |
double | Double | Double(double value) Double(String s) |
래퍼 클래스의 생성자는 매개변수로 문자열이나 각 자료형의 값들을 인자로 받는다. 이 때 주의해야할 것은 생성자의 매개변수로 문자열을 제공할 때, 각 자료형에 알맞는 문자열을 사용해야 한다.
public class WrapperEx {
public static void main(String[] args) {
Integer i = new Integer(100);
Integer i2 = new Integer(100);
System.out.println("i == i2 ? : " + (i == i2));
System.out.println("i.equals(i2) ? : " + (i.equals(i2)));
System.out.println("i.compareTo(i2) ? : " + i.compareTo(i2));
System.out.println("i.toString() ? : " + i.toString());
System.out.println("int의 최대값" + Integer.MAX_VALUE);
System.out.println("int의 최소값" + Integer.MIN_VALUE);
System.out.println("SIZE = " + Integer.SIZE + " bits");
System.out.println("BYTES = " + Integer.BYTES + " bytes");
System.out.println("TYPE = " + Integer.TYPE);
}
}
문자열을 숫자로 변환하기
Wrapper 클래스를 이용하여 문자열을 숫자로 변환 가능하다.
int i = new Integer("100").intValue();
int i2 = Integer.parseInt("100"); // 주로 이 방법을 많이 사용
int i3 = Integer.valueOf("100");
오토박싱 & 언박싱
오토박싱(autoboxing)은 기본형 값을 래퍼 클래스의 객체로 자동 형변환 해주며, 반대로 변환하는 것을 언박싱(unboxing)이라고 한다. 기본형과 참조형 간의 덧셈을 가능하게 해준다.
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(10); // 오토박싱, 10 -> new Integer(10)
int value = list.get(0); // 언박싱, new Integer(10) -> 10
기본형 값을 래퍼클래스의 객체로 변환하지 않아도 되므로 편리하다.
'개발 > Java' 카테고리의 다른 글
[Java] 날짜와 시간, Calendar 클래스 (0) | 2021.08.27 |
---|---|
[Java] 예외처리(exception handling) (0) | 2021.08.23 |
[Java] 내부 클래스(Inner class) (0) | 2021.08.20 |
[Java] 입출력(I/O) (0) | 2021.08.19 |
[Java] 인터페이스(Interface) (0) | 2021.08.18 |