본문 바로가기

Java/자바 기초

9. java.lang 패키지와 유용한 클래스

Object클래스

java.lang패키지는 자바프로그래밍에 가장 기본이 되는 클래스들을 포함하고 있으며, java.lang패키지의 클래스들은 import문 없이 바로 사용 가능 하다.

 

Object클래스는 모든 클래스의 최고 조상이므로, Object클래스의 멤버들은 모든 클래스에서 바로 사용 가능하다.

 

Object클래스는 멤버변수는 없고 오직 메서드 11개만 가지고 있다.

Object클래스의 메서드 - equals( )

equals( )는 매개변수로 객체의 참조변수를 받아서 비교하여 그 결과를 boolean값으로 알려주는 역할을 한다. 아래의 코드는 Object클래스에 저의되어 있는 equals메서드의 실제 내용이다.

public boolean equals(Object obj) {
		return (this==obj);
}

 

두 객체의 같고 다름을 참조변수의 값으로 판단하므로, 서로 다른 두 변수를 equals( )로 비교하면 항상 false를 결과로 얻게 된다.

class Ex9_1 {
    public static void main(String[] args) {
        Value v1 = new Value(10);
        Value v2 = new Value(10);

        if (v1.equals(v2))
            System.out.println("v1과 v2는 같습니다.");
        else
            System.out.println("v1과 v2는 다릅니다.");
    } // main
} 

class Value {
    int value;

    Value(int value) {
        this.value = value;
    }
}

/* 출력 결과
v1과 v2는 다릅니다.
*/

equals( )의 오버라이딩

equals()를 오버라이딩하여 인스턴스변수의 값을 비교하도록 아래의 예제와 같이 바꿀 수 있다.

class Person {
    long id;

    public boolean equals(Object obj) {
//obj가 Object타입이므로 id값을 참조하기 위해서는 ****Person타입으로 형변환이 필요하다.
        if(obj instanceof Person)
            return id ==((Person)obj).id; 
        else
            return false; //타입이 Person이 아니면 값을 비교할 필요도 없다.
    }

    Person(long id) {
        this.id = id;
    }
}

class Ex9_2 {
    public static void main(String[] args) {
        Person p1 = new Person(8011081111222L);
        Person p2 = new Person(8011081111222L);

        if(p1.equals(p2))
            System.out.println("p1과 p2는 같은 사람입니다.");
        else
            System.out.println("p1과 p2는 다른 사람입니다.");
    }
}

/* 출력 결과
p1과 p2는 같은 사람입니다.
*/

Object클래스의 메서드 - hashCode( )

hashCode메서드는 해싱(hashing)기법에 사용되는 '해시함수(hash function)'를 구현한 것이다.

해싱은 데이터관리기법 중의 하나로, 다량의 데이터를 저장하고 검색하는 데 유용하다.

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

 

같은 클래스의 인스턴스변수 값으로 객체의 같고 다름을 판단해야하는 경우, 두 객체가 같은 객체라면 해시코드도 같아야 하므로 equals메서드 뿐만 아니라 hachcode메서드도 함께 오버라이딩 해야 한다.

 

System.identityHashCode(Object obj)는 Object클래스의 hashCode메서드처럼 객체의 주소값으로 해시코드를 생성하여 반환한다.

class Ex9_3 {
    public static void main(String[] args) {
        String str1 = new String("abc");
        String str2 = new String("abc");

        System.out.println(str1.equals(str2));

//String클래스는 문자열의 내용이 같으면, 
//동일한 해시코드를 반환하도록 hashCode메서드가 오버라이딩되어 있기 때문에,
//문자열 내용이 같은 str1, str2에 대해 항상 동일한 해시코드값을 얻는다.
        System.out.println(str1.hashCode());
        System.out.println(str2.hashCode());

//객체의 주소값으로 해시코드를 생성하여 반환한다.
//str1과 str2가 해시코드는 같지만 서로 다른 객체라는 것을 알 수 있다.
        System.out.println(System.identityHashCode(str1));
        System.out.println(System.identityHashCode(str2));
    }
}

/* 출력 결과
true
96354
96354
27134973
1284693
*/

Object클래스의 메서드 - toString( )

toString메서드는 인스턴스에 대한 정보를 문자열(String)로 제공할 목적으로 정의한 것이며, 이는 대부분의 경우 인스턴스 변수에 저장된 값들을 문자열로 표현한다는 뜻이다.

 

Object 클래스에 정의된 toString()은 아래와 같다.

public String toString() {
		return getClass().getName()+"@"+Integer.toHexString(hashCode());
}

 

toString()을 오버라이딩하지 않는다면, 클래스이름과 16진수의 해시코드를 얻게된다.

class Card {
	String kind;
	int number;

	Card() {
		this("SPADE", 1);
	}

	Card(String kind, int number) {
		this.kind = kind;
		this.number = number;
	}
}

class Ex9_4 {
	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@19e0bfd
Card@139a55
*/

toString( )의 오버라이딩

class Card2 {
    String kind;
    int number;

    Card2() {
        this("SPADE", 1);  // Card(String kind, int number)¸¦ È£Ãâ
    }

    Card2(String kind, int number) {
        this.kind = kind;
        this.number = number;
    }

    //오버라이딩
    public String toString() {
        return "kind : " + kind + ", number : " + number;
    }
}

class Ex9_5 {
    public static void main(String[] args) {
        Card2 c1 = new Card2();
        Card2 c2 = new Card2("HEART", 10);
        System.out.println(c1.toString());
        System.out.println(c2.toString());
    }
}

/* 출력 결과
kind : SPADE, number : 1
kind : HEART, number : 10
*/

Obejct클래스에 정의된 toString()의 접근 제어자가 public이므로 Card2클래스의 toString()의 접근제어자도 public이다.

조상에 정의된 메서드를 자손에서 오버라이딩할 때는 조상에 정의된 접근범위보다 같거나 더 넓어야 하기 때문이다.

String클래스

변경불가능한(immutable) 클래스

문자열을 char형의 배열로 다루는 기존의 다른 언어들과는 달리, 자바에서는 문자열을 위한 클래스인 String클래스를 제공한다.

 

String클래스에는 문자형 배열 참조변수(char[ ]) 'value'를 인스턴스 변수로 정의해놓고 있으며, 인스턴스 생성 시 생성자의 매개변수로 입력받은 문자열은 이 인스턴스 변수(value)에 문자형 배열(char[ ])로 저장된다.

public final class String implements java.io.Serializable, Comparable {
	private char[] value;
	...

 

한 번 생성된 String인스턴스가 가지고 있는 문자열은 읽어 올 수만 있고, 변경할 수 없다.

아래의 코드와 같이 ‘+’연산자를 이용해서 문자열을 결합하는 경우, 인스턴스 내의 문자열이 바뀌는 것이 아니라 새로운 문자열(”ab”)가 담긴 String인스턴스가 생성되는 것이다.

String a = "a";
String b = "b";
a = a+b; //"ab"라는 String 인스턴스가 새로 생성됨.

이처럼 덧셈연산자’+’를 사용해서 문자열을 결합하는 것은 매 연산 시 마다 새로운 문자열을 가진 String 인스턴스가 생성되어 메모리 공간을 차지하게 되므로 가능한 한 결합횟수를 줄이는 것이 좋다.

 

StringBuffer클래스를 이용하면 해당 인스턴스에 저장된 문자열은 변경이 가능하므로 하나의 StringBuffer인스턴스만으로도 문자열을 다루는 것이 가능하다.

문자열(String)의 비교

문자열을 만드는 방법은 문자열 리터럴을 지정하는 방법과 String클래스의 생성자를 사용해서 만드는 방법이 있다.

String클래스의 생성자를 이용한 경우, new연산자로 메모리할당이 이루어지므로, 새로운 String인스턴스가 생성된다.

반면, 문자열 리터럴은 이미 존재하는 것을 재사용하는 것이다.

class Ex9_6 {
    public static void main(String[] args) {
        String str1 = "abc"; //문자열 리터럴 "abc"의 주소가 str1에 저장됨.
        String str2 = "abc"; //문자열 리터럴 "abc"의 주소가 str2에 저장됨.
        System.out.println("String str1 = \"abc\";");
        System.out.println("String str2 = \"abc\";");

        System.out.println("str1 == str2 ?  " + (str1 == str2));
        System.out.println("str1.equals(str2) ? " + str1.equals(str2));
        System.out.println();

        String str3 = new String("abc");  //새로운 String인스턴스를 생성
        String str4 = new String("abc");  //새로운 String인스턴스를 생성

        System.out.println("String str3 = new String(\"abc\");");
        System.out.println("String str4 = new String(\"abc\");");

        System.out.println("str3 == str4 ? " + (str3 == str4));
        System.out.println("str3.equals(str4) ? " + str3.equals(str4));
    }
}

/* 출력 결과
String str1 = "abc"
String str2 = "abc"
str1 == str2 ? true   //문자열 리터럴이라 str1, str2는 같은 String 인스턴스를 참조한다.
str1.equals(str2) ? true

String str3 = new String("abc")
String str4 = new String("abc")
str3 == str4 ? false  //String 클래스 생성자를 사용해서 서로 다른 String 인스턴스를 참조한다.
str3.equals(str4) ? true
*/

문자열 리터럴(String리터럴)

자바 소스파일에 포함된 모든 문자열 리터럴은 컴파일 시에 클래스 파일에 저장된다.

이 때 문자열 리터럴도 String인스턴스이고 한 번 생성하면 내용을 변경할 수 없으므로 하나의 인스턴스를 공유하기 위해 같은 내용의 문자열은 한 번만 저장된다.

빈 문자열(empty string)

크기가 0인 배열은 어느 타입이나 생성 가능하며, 크기가 0인 char형 배열을 저장하는 문자열을 빈 문자열이라고 한다.

 

String s = “”; 는 char[] chArr = new char[0]; 과 같이 길이가 0인 char형 배열을 String 인스턴스 내부에 저장하고 있는 것이다.

 

String s = “”; 과 같은 표현이 가능하다고 하서, char c=’’; 가 가능한 것은 아니다.

char형 변수에는 반드시 하나의 문자를 지정해야한다.

String클래스의 생성자와 메서드

join( )과 StringJoiner

join( )은 여러 문자열 사이에 구분자를 넣어 결합하는데 사용하며, StringJoiner클래스도 문자열을 결합하는데 사용하다.

import java.util.StringJoiner;

class Ex9_9 {
    public static void main(String[] args) {
        String animals = "dog,cat,bear";
        String[] arr   = animals.split(",");

        System.out.println(String.join("-", arr));
        //dog-cat-bear

        StringJoiner sj = new StringJoiner("/","[","]");
        //[dog/cat/bear]

        for(String s : arr)
            sj.add(s);

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

문자열과 기본형 간의 변환

기본형을 문자열로 변경하는 방법은 숫자에 빈 문자열""을 더해주거나 valueOf( )를 사용하는 방법이 있다. 성능은 valueOf( )가 더 좋다.

int i = 100;
String str1 = i + "";
String str2 = String.valueOf(i);

문자열을 기본형으로 변환하는 방법은 parseInt( )를 사용하거나 valueOf( )를 사용하는 방법이 있다.

int i = Integer.parseInt("100");
int i2 = Integer.valueOf("100");

StringBuffer클래스

변경가능한(mutable) 클래스

StringBuffer클래스는 String클래스처럼 문자형 배열(char[ ])을 내부적으로 가지고 있지만, String클래스와 달리 내용을 변경할 수 있다.

StringBuffer의 생성자

StringBuffer클래스의 인스턴스를 생성할 때, 문자열을 저장하고 편집하기 위한 공간(buffer)으로 사용되는 char형 배열이 생성된다.

 

생성자 StringBuffer(int length)를 사용하여 StringBuffer인스턴스에 저장될 문자열의 길이를 고려하여 충분히 여유있는 크기를 지정하는 것이 바람직하다. 크기를 지정하지 않으면 16개의 문자를 지정할 수 있는 크기의 버퍼를 생성한다.

버퍼의 크기가 작업하려는 문자열의 길이보다 작을 때는 내부적으로 버퍼의 크기를 증가시키는 작업이 수행된다.

StringBuffer의 변경

append( )는 매개변수로 입력된 값을 문자열로 변환하여 StringBuffer인스턴스가 저장하고 있는 문자열 뒤에 덧붙인다.

StringBuffer의 비교

StringBuffer클래스는 String클래스와 달리 equals메서드를 오버라이딩하지 않아 등가비교연산자(==)로 비교한 것과 같은 결과를 반환한다.

StringBuffer인스턴스에 담긴 문자열을 비교하기 위해서는 StringBuffer클래스에 오버라이딩 되어있는 toString메서드를 호출해서 String인스턴스를 얻은 다음, equals메서드를 사용해서 비교해야 한다.

class Ex9_11 {
    public static void main(String[] args) {
        StringBuffer sb  = new StringBuffer("abc");
        StringBuffer sb2 = new StringBuffer("abc");

        System.out.println("sb == sb2 ? " + (sb == sb2));    //"sb == sb2 ? false
        System.out.println("sb.equals(sb2) ? " + sb.equals(sb2)); //sb.equals(sb2) ? false

        // StringBuffer의 내용을 String으로 변환한다.
        String s  = sb.toString();    // String s = new String(sb);와 같다.
        String s2 = sb2.toString();

        System.out.println("s.equals(s2) ? " + s.equals(s2));  //s.equals(s2) ? true
    }
}

StringBuffer의 생성자와 메서드

StringBuilder

StringBuffer는 멀티쓰레드에 안전하도록 동기화되어 있어 멀티쓰레드로 작성된 프로그램이 아닌 경우에는 불필요한 동기화로 StringBuffer의 성능을 저하시킨다. 이를 보완하기 위해 StringBuffer에서 쓰레드의 동기화만 뺀 StringBuilder가 새로 추가되었다.

 

StringBuilder는 StringBuffer와 완전히 똑같은 기능으로 작성되어 있어서, 소스코드에서 StringBuffer대신 StringBuilder를 사용하도록 바꾸기만 하면 된다.

Math클래스

Math클래스는 기본적인 수학계산에 유용한 메서드로 구성되어 있다.

클래스 내에 인스턴스변수가 하나도 없으므로 Math클래스의 메서드는 모두 static이다.

Math의 메서드

래퍼(wrapper) 클래스

래퍼(wrapper) 클래스는 기본형 값도 객체로 다뤄져야 할 때가 있어 기본형을 클래스로 정의한 것을 말한다.

기본형을 대표하는 8개의 래퍼클래스가 있으며, 내부에 기본형 변수를 가지고 있어 기본형 값을 객체로 다룰 수 있다.

생성자의 매개변수로 문자열을 제공할 때, 각 자료형에 알맞은 문자열을 사용해야한다.

예를 들어, ‘new Integer(”1.0”); 과 같이 하면 NumberFormatException이 발생한다.

래퍼(wrapper) 클래스 예제

래퍼 클래스들은 모두 equals()가 오버라이딩되어 있어서 주소값이 아닌 객체가 가지고 있는 값을 비교한다.

class Ex9_14 {
    public static void main(String[] args) {
        Integer i  = new Integer(100);
        Integer i2 = new Integer(100);

        System.out.println("i==i2 ? "+(i==i2));  //i==i2 ? false
        System.out.println("i.equals(i2) ? "+i.equals(i2)); //i.equals(i2) ? true
        System.out.println("i.compareTo(i2)="+i.compareTo(i2)); //i.compareTo(i2)=0
        System.out.println("i.toString()="+i.toString()); //i.toString()=100

        System.out.println("MAX_VALUE="+Integer.MAX_VALUE); //MAX_VALUE=2147483647
        System.out.println("MIN_VALUE="+Integer.MIN_VALUE); //MIN_VALUE=-2147483647
        System.out.println("SIZE="+Integer.SIZE+" bits");  //SIZE=32 bits
        System.out.println("BYTES="+Integer.BYTES+" bytes"); //BYTES=4 bytes
        System.out.println("TYPE="+Integer.TYPE); //TYPE=int
    }
}

Number클래스

Number클래스는 추상클래스로 내부적으로 숫자를 멤버변수로 갖는 래퍼 클래스들의 조상이다.

기본형 중에서 숫자와 관련된 래퍼 클래스들은 모두 Number클래스의 자손이다.

그 외에 long으로도 다룰 수 없는 큰 범위의 정수를 처리하기 위한 BigInteger와 double로도 다룰 수 없는 큰 범위의 부동 소수점수를 처리하기 위한 BigDecimal 등이 있다.

문자열을 숫자로 변환하기

문자열 → 기본형 : 타입.parse타입(String s) 형식의 메서드는 반환값이 기본형이다.

- byte b = Byte.parseByte("100");

- short s = Short.parseShort("100");

- int i = Integer.parseInt("100");

- float f = Float.parseFloat("3.14");

- dobule d = Double.parseDouble(”3.14”);

 

문자열 → 래퍼클래스: 타입.valueOf() 형식의 메서드는 반환값이 래퍼 클래스 타입이다.

- Byte b = Byte.valueOf("100");

- Short s = Short.valueOf("100");

- Integer i = Integer.valueOf("100");

- Float f = Float.valueOf("3.14");

- Double d = Double.valueOf(”3.14”);

 

문자열이 10진수가 아닌 다른 진법(radix)의 숫자일 때도 변환이 가능하도록 다음과 같은 메서드가 제공된다.

static int parseInt(String s, int radix) // 문자열 s를 radix진법으로 인식
static Integer valueOf(String s, int radix)

int i2 = Integer.parseInt("100",2); //100(2) -> 4
int i16 = Integer.parseInt("FF", 16); //FF(16) -> 255

//radix를 생략하면 10진법으로 간주해서 NumberFormatException 에러 발생
//int i16 = Integer.parseInt("FF")

오토박싱 & 언박싱

기본형 값을 래퍼클래스의 객체로 자동 변환해주는 것을 '오토박싱(autoboxing)'이라 한다.

반대로 래퍼클래스의 객체를 기본형 값으로 변환하는 것을 '언박싱(unboxing)'이라고 한다.

컴파일러가 자동으로 변환하는 코드를 넣어준다.

오토박싱 & 언박싱 예제

class Ex9_16 {
    public static void main(String[] args) {
        int i = 10;

        // 기본형을 참조형으로 형변환(형변환 생략가능)
        Integer intg = (Integer)i; // Integer intg = Integer.valueOf(i);
        Object obj = (Object)i;    // Object obj = (Object)Integer.valueOf(i);

        Long     lng = 100L;  // Long lng = new Long(100L);

        int i2 = intg + 10;   // 참조형과 기본형간의 연산 가능
        long l = intg + lng;  // 참조형 간의 덧셈도 가능

        Integer intg2 = new Integer(20);
        int i3 = (int)intg2;  // 참조형을 기본형으로 형변환도 가능(형변환 생략가능)

        Integer intg3 = intg2 + i3; 

        System.out.println("i     ="+i);            //i     =10
        System.out.println("intg  ="+intg);         //intg  =10
        System.out.println("obj   ="+obj);          //obj   =10
        System.out.println("lng   ="+lng);          //lng   =100
        System.out.println("intg + 10  ="+i2)       //intg + 10  =20;
        System.out.println("intg + lng ="+l);       //intg + lng =110;
        System.out.println("intg2 ="+intg2);        //intg2 =20;
        System.out.println("i3    ="+i3);           //i3    =20;
        System.out.println("intg2 + i3 ="+intg3);   //intg2 + i3 =40;
    }
}

'Java > 자바 기초' 카테고리의 다른 글

10. 날짜와 시간 & 형식화  (0) 2022.03.14
8. 예외처리  (0) 2022.03.14
7. 객체지향 프로그래밍 II  (0) 2022.03.11
6. 객체지향 프로그래밍 I  (0) 2022.03.11
5. 배열  (0) 2022.03.11