관리 메뉴

Partially Committed

[CH09] java.lang νŒ¨ν‚€μ§€ λ³Έλ¬Έ

πŸ’» Study !/JAVA

[CH09] java.lang νŒ¨ν‚€μ§€

WonderJay 2022. 8. 21. 16:37
728x90
λ°˜μ‘ν˜•
SMALL

λ³Έ ν¬μŠ€νŒ…μ€ μžλ°”μ˜ 정석 ꡐ재λ₯Ό κ³΅λΆ€ν•˜λ©°, κ°„λ‹¨νžˆ 정리/기둝 μš©λ„λ‘œ μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€. ν˜Ήμ—¬, 잘λͺ»λœ λ‚΄μš©μ΄ μžˆλ‹€λ©΄ μ§€μ ν•΄μ£Όμ‹œλ©΄ κ°μ‚¬ν•˜κ² μŠ΅λ‹ˆλ‹€.

 

 


1. java.lang package

  μžλ°”μ˜ 기본이 λ˜λŠ” 클래슀λ₯Ό ν¬ν•¨ν•œ νŒ¨ν‚€μ§€λ‘œ, java.lang package 에 μ†ν•˜λŠ” class λŠ” import λ¬Έ 없이 μ‚¬μš©κ°€λŠ₯ν•˜λ‹€.

 

1.1 Object 클래슀

  1.1.1 public boolean equals(Object obj)

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

   μœ„와 같이 κ΅¬ν˜„λœ λ©”μ„œλ“œλ‘œ 객체가 μ„œλ‘œ 같은지 μ•„λ‹Œμ§€λ₯Ό νŒλ‹¨ν•œλ‹€.

class source{
    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");
        v2=v1;

        if(v1.equals(v2))
            System.out.println("v1 == v2");
        else
            System.out.println("v1 != v2");
    }
}

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

  equals λ©”μ„œλ“œλŠ” μ£Όμ†Œκ°’μœΌλ‘œ 객체λ₯Ό λΉ„κ΅ν•˜λ―€λ‘œ 멀버가 같더라도 μ£Όμ†Œκ°€ λ‹€λ₯΄λ©΄ false λ₯Ό λ°˜ν™˜ν•œλ‹€. v2 = v1 λ₯Ό μˆ˜ν–‰ν•˜λ©΄ μ°Έμ‘°λ³€μˆ˜ v2 λŠ” v1 이 μ°Έμ‘°ν•˜κ³  μž‡λŠ” μΈμŠ€ν„΄μŠ€μ˜ μ£Όμ†Œκ°€ μ €μž₯λ˜λŠ” κ²ƒμ΄λ―€λ‘œ v2 κ³Ό v1 의 μ£Όμ†ŒλŠ” κ°™κ²Œ 되고 이에 따라 true κ°€ λ°˜ν™˜λ˜λŠ” 것이닀. equals λ©”μ„œλ“œλŠ” 두 μ°Έμ‘° λ³€μˆ˜κ°€ 같은 객체λ₯Ό μ°Έμ‘°ν•˜λŠ” 지 μ—¬λΆ€λ₯Ό νŒŒμ•…ν•˜λŠ” 것인데, μΈμŠ€ν„΄μŠ€μ˜ 멀버 λ³€μˆ˜κ°€ 같은지 μ•„λ‹Œμ§€λ₯Ό νŒλ‹¨ν•  수 μžˆλ„λ‘ μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ˜€λ²„λΌμ΄λ”©μ„ ν•˜μ—¬ κ΅¬ν˜„ν•΄μ£Όλ©΄ λœλ‹€.

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

    public boolean equals(Object obj){
        if(obj instanceof Value)
            return value == ((Value)obj).value;
        else return false;
    }
}

  String 클래슀의 equals λ©”μ„œλ“œ μ—­μ‹œ Object 클래슀의 equals λ©”μ„œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ”©ν•˜μ—¬ String μΈμŠ€ν„΄μŠ€κ°€ κ°€μ§€λŠ” λ¬Έμžμ—΄μ„ λΉ„κ΅ν•˜λ„λ‘ κ΅¬ν˜„λ˜μ—ˆλ‹€.

 

  1.1.2 hashCode()

  hash function 을 κ΅¬ν˜„ν•œ κ²ƒμœΌλ‘œ 찾고자 ν•˜λŠ” 값을 μž…λ ₯ν•˜λ©΄ hash code λ₯Ό λ°˜ν™˜ν•œλ‹€. 32 bit JVM μ—μ„œλŠ” μ„œλ‘œ λ‹€λ₯Έ κ°μ²΄λŠ” 같은 hash code λ₯Ό κ°€μ§ˆ 수 μ—†μ—ˆμ§€λ§Œ, 64 bit JVM μ—μ„œλŠ” 8 byte μ£Όμ†Œκ°’μœΌλ‘œ ν•΄μ‰¬μ½”λ“œ (4 byte) λ₯Ό λ§Œλ“œλ―€λ‘œ 해쉬 μ½”λ“œκ°€ 쀑볡될 수 μžˆλ‹€. 클래슀의 μΈμŠ€ν„΄μŠ€ λ³€μˆ˜ 값을 μ΄μš©ν•΄μ„œ 객체의 κ°™κ³  닀름을 νŒλ‹¨ν•  λ•Œμ—λŠ” equals λ©”μ„œλ“œ 외에도 hashCode λ©”μ„œλ“œλ„ μ μ ˆν•˜κ²Œ μ˜€λ²„λΌμ΄λ”©ν•΄μ£Όμ–΄μ•Ό ν•œλ‹€. 같은 객체이면 hashCode λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν–ˆμ„ λ•Œμ˜ hash code 도 κ°™μ•„μ•Ό ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

class source{
    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 λ₯Ό λ°˜ν™˜ν•˜λ„λ‘ μ˜€λ²„λΌμ΄λ”© λ˜μ–΄μžˆλ‹€. 반면 System.identityHashCode(Object x) λŠ” Object ν΄λž˜μŠ€μ—μ„œμ™€ 같이 객체의 μ£Όμ†Œκ°’μœΌλ‘œ hash Code λ₯Ό μƒμ„±ν•˜λ―€λ‘œ λͺ¨λ“  객체에 λŒ€ν•΄ 항상 λ‹€λ₯Έ 해쉬 μ½”λ“œλ₯Ό λ°˜ν™˜ν•¨μ„ 보μž₯ν•  수 μžˆλ‹€.

 

  1.1.3 toString()

    μΈμŠ€ν„΄μŠ€μ— λŒ€ν•œ 정보λ₯Ό λ¬Έμžμ—΄λ‘œ μ œκ³΅ν•œλ‹€. 

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

μœ„μ™€ 같이 κ΅¬ν˜„λ˜μ–΄ μžˆλ‹€. getClass() 와 hashCode() λŠ” Object ν΄λž˜μŠ€μ— μ •μ˜λœ κ²ƒμœΌλ‘œ μΈμŠ€ν„΄μŠ€ 생성 없이 호좜 κ°€λŠ₯ν•˜λ‹€.

class source{
    public static void main(String[] args) {
        Card c1 = new Card();
        Card c2 = new Card();

        System.out.println(c1.toString());
        System.out.println(c2.toString());
    }
}

class Card{
    String kind;
    int number;

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

클래슀 이름과 16μ§„μˆ˜μ˜ ν•΄μ‰¬μ½”λ“œκ°€ 좜λ ₯된 것을 확인할 수 μžˆλ‹€. μ„œλ‘œ λ‹€λ₯Έ μΈμŠ€ν„΄μŠ€μ΄λ―€λ‘œ λ‹€λ₯Έ ν•΄μ‰¬μ½”λ“œλ₯Ό 가지고 μžˆμŒμ„ μ•Œ 수 μžˆλ‹€. Card ν΄λž˜μŠ€μ—μ„œ toString() λ©”μ„œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ”©ν•˜μ—¬ μ»€μŠ€ν…€λ§ˆμ΄μ§•ν•  수 μžˆλ‹€.

class source{
    public static void main(String[] args) {
        Card c1 = new Card();
        Card c2 = new Card();

        System.out.println(c1.toString());
        System.out.println(c2.toString());
    }
}

class Card{
    String kind;
    int number;

    Card(){
        this("SPADE", 1);
    }
    Card(String kind, int number){
        this.kind = kind;
        this.number = number;
    }
    public String toString(){
        return "kind : " + kind + ", number : " + number;
    }
}

toString 은 Object ν΄λž˜μŠ€μ—μ„œ public 으둜 λ§Œλ“€μ–΄μ‘ŒλŠ”λ°, μ΄λŠ” 쑰상에 μ •μ˜λœ λ©”μ„œλ“œλ₯Ό μžμ†μ—μ„œ μ˜€λ²„λΌμ΄λ”©ν•  λ•Œ 쑰상에 μ •μ˜λœ μ ‘κ·Ό λ²”μœ„λ³΄λ‹€ κ°™κ±°λ‚˜ λ„“μ–΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ΄λ©°, 이에 따라 Object ν΄λž˜μŠ€μ—μ„œ toString() 이 public μ΄λ―€λ‘œ μžμ† ν΄λž˜μŠ€μ—μ„œλŠ” toString() 을 public 으둜 ν•  수 밖에 μ—†λŠ” 것이닀.

 

  1.1.4 clone()

  μ΄λŠ” μžμ‹ μ„ λ³΅μ œν•΄μ„œ μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό μ œκ³΅ν•œλ‹€. μΈμŠ€ν„΄μŠ€ λ³€μˆ˜μ˜ κ°’λ§Œ λ³΅μ‚¬ν•˜λ―€λ‘œ μ°Έμ‘° νƒ€μž…μ˜ μΈμŠ€ν„΄μŠ€ λ³€μˆ˜κ°€ 있으면 μ™„μ „ν•œ 볡사가 이루어지지 μ•ŠλŠ”λ‹€. 예λ₯Ό λ“€μ–΄ λ°°μ—΄μ˜ 경우 볡제된 μΈμŠ€ν„΄μŠ€λ„ 같은 λ°°μ—΄ μ£Όμ†Œλ₯Ό κ°€μ§€κ²Œ λœλ‹€λŠ” 것인데, μ΄λŸ¬ν•œ κ²½μš°μ— λŒ€ν•΄μ„œλŠ” μ˜€λ²„λΌμ΄λ”©ν•˜μ—¬ μƒˆλ‘œμš΄ 배열을 μƒμ„±ν•˜κ³  λ‚΄μš©μ„ λ³΅μ‚¬ν•˜λ„λ‘ κ΅¬ν˜„ν•΄μ£Όλ©΄ 해결이 κ°€λŠ₯ν•  것이닀.

class Point implements Cloneable{
    int x, y;
    Point(int x, int y){
        this.x = x;
        this.y = y;
    }
    public String toString(){
        return "x = " + x + " y = " + y;
    }
    public Object clone(){
        Object obj = null;
        try{
            obj = super.clone();
        } catch (CloneNotSupportedException e){
            // ...
        }
        return obj;
    }
}

class source{
    public static void main(String[] args) {
        Point original = new Point(3, 5);
        Point copy = (Point)original.clone();
        System.out.println(original);
        System.out.println(copy);
    }
}

clone() 을 μ‚¬μš©ν•˜λ €λ©΄ λ³΅μ œν•  ν΄λž˜μŠ€κ°€ Cloneable μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•΄μ•Όλ§Œ ν•˜λ©°, clone() 을 μ˜€λ²„λΌμ΄λ”©ν•˜λ©΄μ„œ protected μ ‘κ·Ό μ œμ–΄μžλ₯Ό public 으둜 λ°”κΎΈμ–΄ μ£Όμ–΄μ•Όλ§Œ 상속관계가 μ—†λŠ” λ‹€λ₯Έ ν΄λž˜μŠ€μ—μ„œλ„ clone() 을 ν˜ΈμΆœν•  수 있게 λœλ‹€. 

 

  1.1.5 곡변 λ°˜ν™˜νƒ€μž…(covariant return type)

  μ΄λŠ” μ˜€λ²„λΌμ΄λ”©μ„ ν•  λ•Œ 쑰상 λ©”μ„œλ“œμ˜ λ°˜ν™˜ νƒ€μž…μ„ μžμ† 클래슀의 νƒ€μž…μœΌλ‘œ λ²ˆκ²½ν•˜λŠ” 것이닀. covariant return type 을 μ‚¬μš©ν•˜λ©΄ μ‘°μƒμ˜ νƒ€μž…μ΄ μ•„λ‹ˆλΌ μ‹€μ œλ‘œ λ°˜ν™˜λ˜λŠ” μžμ† 객체 νƒ€μž…μœΌλ‘œ λ°˜ν™˜ν•  수 μžˆκΈ°μ— ν˜•λ³€ν™˜μ„ ν•˜μ§€ μ•Šμ•„λ„ λ˜μ–΄μ„œ νŽΈλ¦¬ν•˜λ‹€.

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

import java.util.Arrays;

class source{
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        int[] arrClone = arr.clone();
        arrClone[0] = 6;

        System.out.println(Arrays.toString(arr));
        System.out.println(Arrays.toString(arrClone));
    }
}

μœ„λŠ” clone() 을 μ΄μš©ν•˜μ—¬ 배열을 λ³΅μ‚¬ν•˜λŠ” 예제인데, Object ν΄λž˜μŠ€λŠ” protected 둜 μ •μ˜λœ clone() 을 이λ₯Ό μƒμ†λ°”λŠ” λ°°μ—΄μ—μ„œλŠ” public 으둜 μ˜€λ²„λΌμ΄λ”©ν•˜μ˜€κΈ° 떄문에 직접 호좜이 κ°€λŠ₯ν•œ 것이고 원본과 같은 νƒ€μž…μ„ λ°˜ν™˜ν•˜λ―€λ‘œ ν˜•λ³€ν™˜μ„ ν•˜μ§€ μ•Šμ•„λ„ λœλ‹€. μΌλ°˜μ μœΌλ‘œλŠ” 배열을 볡사할 λ•Œ System.arraycopy() λ₯Ό μ‚¬μš©ν•΄μ„œ λ‚΄μš©μ„ λ³΅μ‚¬ν•˜λŠ”λ° 이와 같이 clone() 으둜 κ°„λ‹¨νžˆ 볡사할 μˆ˜λ„ μžˆλ‹€. λ°°μ—΄ 외에도 java.util package 의 Vector, ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap, Calendar, Date 와 같은 ν΄λž˜μŠ€λ„ clone() 으둜 λ³΅μ œκ°€ κ°€λŠ₯ν•œλ°, clone() 으둜 λ³΅μ œκ°€ κ°€λŠ₯ν•œ ν΄λž˜μŠ€μΈμ§€ ν™•μΈν•˜κΈ° μœ„ν•΄μ„œλŠ” JAVA API μ—μ„œ Cloneable 을 κ΅¬ν˜„ν•˜μ˜€λŠ”μ§€ ν™•μΈν•˜λ©΄ λœλ‹€.

 

  1.1.6 얕은 볡사와 κΉŠμ€ 볡사

   clone() 으둜 객체λ₯Ό λ³΅μ‚¬ν•˜λ©΄ λ‹¨μˆœνžˆ 객체에 μ €μž₯된 값을 λ³΅μ‚¬ν•˜λŠ” 것이고 μ°Έμ‘°ν•˜λŠ” κ°μ²΄κΉŒμ§€ λ³΅μ œν•˜μ§€ μ•ŠλŠ”λ‹€. (Shallow copy()) μ΄λŸ¬ν•œ κ²½μš°μ—λŠ” 원본을 λ³€κ²½ν•  μ‹œ 볡사본 λ˜ν•œ 영ν–₯을 λ°›κ²Œ λœλ‹€. λ°˜λ©΄μ— 원본이 μ°Έμ‘°ν•˜κ³  μžˆλŠ” 객체 자체λ₯Ό λ³΅μ‚¬ν•˜λŠ” Deep copy 의 κ²½μš°μ—λŠ” 원본과 볡사본이 μ„œλ‘œ λ‹€λ₯Έ 객체λ₯Ό μ°Έμ‘°ν•˜λ―€λ‘œ μ„œλ‘œ 영ν–₯을 λ―ΈμΉ˜μ§€ μ•Šκ²Œ λœλ‹€.

class Circle implements Cloneable{
    Point p;
    double r;

    Circle(Point p, double r){
        this.p = p;
        this.r = r;
    }
    public Circle shallowCopy(){
        Object obj = null;
        try{
            obj = super.clone();
        } catch(CloneNotSupportedException e){ }
        return (Circle)obj;
    }

    public Circle deepCopy(){
        Object obj = null;
        try{
            obj = super.clone();
        } catch (CloneNotSupportedException e){ }
        Circle c = (Circle)obj;
        c.p = new Point(this.p.x, this.p.y);
        return c;
    }
    public String toString() {
        return "[p = " + p + ", r = " + r + "]";
    }
}

class Point{
    int x,y;
    Point(int x, int y){
        this.x = x;
        this.y = y;
    }
    public String toString(){
        return "(" + x + ", " + y + ")";
    }
}
class source{
    public static void main(String[] args) {
        Circle c1 = new Circle(new Point(1, 1), 2.0);
        Circle c2 = c1.shallowCopy();
        Circle c3 = c1.deepCopy();

        System.out.println("c1 = " + c1);
        System.out.println("c2 = " + c2);
        System.out.println("c3 = " + c3);

        c1.p.x = 9;
        c1.p.y = 9;

        System.out.println("===============");
        System.out.println("c1 = " + c1);
        System.out.println("c2 = " + c2);
        System.out.println("c3 = " + c3);

    }
}

c2 λŠ” c1 에 λŒ€ν•˜μ—¬ shallow copy 둜 μƒμ„±ν•œ κ°μ²΄μ΄λ―€λ‘œ c1 의 변경에 λŒ€ν•œ 영ν–₯을 λ°›κ³ , c3 λŠ” c1 에 λŒ€ν•˜μ—¬ deep copy 둜 μƒμ„±ν•œ κ°μ²΄μ΄λ―€λ‘œ c1 의 변경에 λŒ€ν•œ 영ν–₯을 받지 μ•ŠλŠ” 독립적인 κ°μ²΄μž„μ„ μ•Œ 수 μžˆλ‹€. 

 

 

  1.1.7 getClass()

    자기 μžμ‹ μ΄ μ†ν•œ Class 객체λ₯Ό λ°˜ν™˜ν•œλ‹€. Class κ°μ²΄λŠ” 이름이 Class 인 클래슀의 객체으둜 μ•„λž˜μ™€ 같이 μ •μ˜λ˜μ–΄ μžˆλ‹€.

public final class Class implements ... { ... }

이 Class κ°μ²΄λŠ” 클래슀의 λͺ¨λ“  정보λ₯Ό λ‹΄κ³  있으며 클래슀 λ‹Ή ν•˜λ‚˜λ§Œ μ‘΄μž¬ν•˜κ³  ClassLoader 에 μ˜ν•΄ λ©”λͺ¨λ¦¬μ— 클래슀 파일이 μ˜¬λΌκ°€κ²Œ 될 λ•Œ μžλ™μœΌλ‘œ μƒμ„±λœλ‹€. ClassLoader λŠ” μ‹€ν–‰ μ‹œ ν•„μš”ν•œ 클래슀λ₯Ό λ©”λͺ¨λ¦¬μ— λ™μ μœΌλ‘œ μ˜¬λ¦¬λŠ” 역할을 ν•œλ‹€. μƒμ„±λœ 클래슀 객체가 λ©”λͺ¨λ¦¬μ— μžˆλŠ”μ§€ ν™•μΈν•˜κ³ , 있으면 객체의 μ°Έμ‘°λ₯Ό λ°˜ν™˜, μ—†μœΌλ©΄ classpath 에 λ”°λΌμ„œ 클래슀 νŒŒμΌμ„ μ„œμΉ˜ν•œλ‹€. μ΄λ•Œ 클래슀 νŒŒμΌμ„ 찾지 λͺ»ν•˜λŠ” κ²½μš°μ—λŠ” ClassNotFoundException 이 λ°œμƒν•œλ‹€.

 

  1.1.8 Class 객체λ₯Ό μ–»λŠ” 방법

   ν΄λž˜μŠ€ 정보λ₯Ό μ–»μœΌλ €λ©΄ Class 객체에 λŒ€ν•œ μ°Έμ‘°λ₯Ό λ¨Όμ € 얻어와야 ν•œλ‹€.

 

Class cObj = new Card().getClass();

// μƒμ„±λœ 객체둜 λΆ€ν„° μ–»λŠ” 방법

Class cObj = Card.class;

// 클래슀 λ¦¬ν„°λŸ΄(*.class)으둜 λΆ€ν„° μ–»λŠ” 방법

Class cObj = Class.forName("Card");

// 클래슀 μ΄λ¦„μœΌλ‘œλΆ€ν„° μ–»λŠ” 방법

 

 forName() 은 νŠΉμ • 클래슀 νŒŒμΌμ„ λ©”λͺ¨λ¦¬μ— 올릴 λ•Œ 주둜 μ‚¬μš©ν•œλ‹€. Class 객체λ₯Ό μ΄μš©ν•΄μ„œ ν΄λž˜μŠ€μ— λŒ€ν•œ λͺ¨λ“  정보λ₯Ό 얻을 수 μžˆμœΌλ―€λ‘œ Class 객체λ₯Ό 톡해 객체λ₯Ό μƒμ„±ν•˜κ³  λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” λ“± 보닀 동적인 μ½”λ“œ μž‘μ„±μ΄ κ°€λŠ₯해진닀.

 

  1.2 String Class

  μžλ°”μ—μ„œλŠ” λ¬Έμžμ—΄μ„ String 클래슀λ₯Ό μ΄μš©ν•΄μ„œ 닀룬닀.

 

  1.2.1 λ³€κ²½ λΆˆκ°€λŠ₯ν•œ(immutable) 클래슀

   String ν΄λž˜μŠ€μ—μ„œλŠ” λ¬Έμžμ—΄μ„ μ €μž₯ν•˜κΈ° μœ„ν•œ λ¬Έμžν˜• λ°°μ—΄ μ°Έμ‘°λ³€μˆ˜ value λ₯Ό μΈμŠ€ν„΄μŠ€ λ³€μˆ˜λ‘œ μ •μ˜ν•˜μ˜€λ‹€. 

 

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

 

일단 μƒμ„±λœ String μΈμŠ€ν„΄μŠ€μ˜ value λŠ” read only 이며 λ³€κ²½ν•  수 μ—†λ‹€. 예λ₯Ό λ“€μ–΄ String 객체 2개λ₯Ό + μ—°μ‚°μžλ₯Ό μ΄μš©ν•˜μ—¬ λ¬Έμžμ—΄μ„ κ²°ν•©ν•˜λŠ” 경우, μΈμŠ€ν„΄μŠ€ λ‚΄μ˜ λ¬Έμžμ—΄μ΄ λ°”λ€ŒλŠ” 것이 μ•„λ‹Œ μƒˆλ‘œμš΄ λ¬Έμžμ—΄μ΄ λ‹΄κΈ΄ String μΈμŠ€ν„΄μŠ€κ°€ μƒμ„±λ˜λŠ” 원리인데, 이 떄문에 μ˜ˆμƒμΉ˜ λͺ»ν•˜κ²Œ λ©”λͺ¨λ¦¬ 곡간을 μ°¨μ§€ν•˜κ²Œ λ˜λ―€λ‘œ κ²°ν•©νšŸμˆ˜λ₯Ό μ΅œλŒ€ν•œ μ€„μ΄λŠ” 것이 νš¨μœ¨μ μ΄λ‹€. λ¬Έμžμ—΄ κ°„ κ²°ν•©, μΆ”μΆœκ³Ό 같이 λ¬Έμžμ—΄μ„ 자주 닀루어야 ν•  κ²½μš°μ—λŠ” String 클래슀 외에 StringBuffer 클래슀λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 쒋은데, StringBuffer ν΄λž˜μŠ€λŠ” λ¬Έμžμ—΄ 변경이 κ°€λŠ₯ν•΄μ„œ ν•˜λ‚˜μ˜ μΈμŠ€ν„΄μŠ€ λ§ŒμœΌλ‘œλ„ λ¬Έμžμ—΄μ„ μΆ©λΆ„νžˆ λ‹€λ£° 수 μžˆλ‹€.

 

  1.2.2 λ¬Έμžμ—΄μ˜ 비ꡐ

   λ¬Έμžμ—΄μ„ λ§Œλ“œλŠ” 것은 λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄μ„ μ§€μ •ν•˜λŠ” 것과 String 클래슀의 μƒμ„±μžλ₯Ό μ΄μš©ν•˜λŠ” 방법이 μžˆλ‹€. 

 

String str1 = "abc"; // λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄ "abc" 의 μ£Όμ†Œκ°€ str1 에 μ €μž₯됨.

String str2 = new String("abc"); // μƒˆλ‘œμš΄ String μΈμŠ€ν„΄μŠ€λ₯Ό 생성함.

 

μƒμ„±μžλ₯Ό μ΄μš©ν•˜λŠ” 경우 new operator 에 μ˜ν•΄ λ©”λͺ¨λ¦¬ 할당이 μ΄λ£¨μ–΄μ Έμ„œ μƒˆλ‘œμš΄ String μΈμŠ€ν„΄μŠ€κ°€ μƒμ„±λ˜λŠ” 것이고, λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄μ€ 이미 μ‘΄μž¬ν•˜λŠ” 것을 μž¬μ‚¬μš©ν•˜λŠ” 것이닀. 

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

        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));

        String str3 = new String("\"abc\"");
        String str4 = new String("\"abc\"");

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

String ν΄λž˜μŠ€μ—μ„œ μ˜€λ²„λΌμ΄λ”© 된 equals λ©”μ„œλ“œλ₯Ό μ΄μš©ν•˜μ—¬ λΉ„κ΅ν•œ 경우 λ¬Έμžμ—΄μ— μ €μž₯된 λ‚΄μš©μ„ λΉ„κ΅ν•˜λ―€λ‘œ λͺ¨λ‘ true λ₯Ό 얻을 수 μžˆμ§€λ§Œ, == λ₯Ό μ΄μš©ν•˜μ—¬ λΉ„κ΅ν–ˆμ„ λŒ€μ—λŠ” String μΈμŠ€ν„΄μŠ€μ˜ 경우 μ£Όμ†Œλ₯Ό λΉ„κ΅ν•˜λ―€λ‘œ κ²°κ³Όκ°€ false 둜 λ‚˜μ˜€λŠ” 것이닀.

 

  1.2.3 λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄

   μ†ŒμŠ€ 파일 λ‚΄μ˜ λͺ¨λ“  λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄μ€ 컴파일 ν•  λ•Œ 클래슀 νŒŒμΌμ— μ €μž₯λ˜λŠ”λ°, 같은 λ‚΄μš©μ˜ λ¬Έμžμ—΄μ€ 단 ν•œλ²ˆλ§Œ μ €μž₯λœλ‹€. λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄λ„ String μΈμŠ€ν„΄μŠ€μ΄κ³  μƒμ„±λ˜λ©΄ λ³€κ²½ν•  수 μ—†κΈ° λ•Œλ¬Έμ— ν•˜λ‚˜μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό κ³΅μœ ν•˜λ©΄ 되기 λ•Œλ¬Έμ΄λ‹€. 클래슀 νŒŒμΌμ—λŠ” λͺ¨λ“  λ¦¬ν„°λŸ΄ λͺ©λ‘λ“€μ΄ μžˆλŠ”λ°, 클래슀 파일이 클래슀 λ‘œλ”μ— μ˜ν•΄ λ©”λͺ¨λ¦¬μ— μ˜¬λΌκ°€κ²Œ 되면 ν•΄λ‹Ή λ¦¬ν„°λŸ΄λ“€μ΄ JVM λ‚΄μ˜ Constant pool 에 μ €μž₯λœλ‹€.

 

  1.2.4 빈 λ¬Έμžμ—΄(empty string)

    길이가 0 인 λ¬Έμžμ—΄μ„ λ§ν•˜λ©°, C μ—μ„œλŠ” λ¬Έμžμ—΄μ˜ 끝에 null λ¬Έμžκ°€ λΆ™μ§€λ§Œ μžλ°”μ—μ„œλŠ” 그렇지 μ•ŠλŠ”λ‹€. λŒ€μ‹  λ¬Έμžμ—΄μ˜ 길이 정보λ₯Ό λ”°λ‘œ μ €μž₯ν•œλ‹€.

 

  1.2.5 String 클래슀의 μƒμ„±μžμ™€ λ©”μ„œλ“œ

  https://docs.oracle.com/javase/7/docs/api/java/lang/String.html

 

String (Java Platform SE 7 )

Compares two strings lexicographically. The comparison is based on the Unicode value of each character in the strings. The character sequence represented by this String object is compared lexicographically to the character sequence represented by the argum

docs.oracle.com

 

  1.2.6 join() κ³Ό StringJoiner

    join() 은 μ—¬λŸ¬ λ¬Έμžμ—΄ 사이사이에 κ΅¬λΆ„μžλ₯Ό λ„£μ–΄ κ²°ν•©ν•˜λŠ” split() κ³Ό λ°˜λŒ€μ˜ μž‘μ—…μ„ ν•œλ‹€. java.util.StringJoiner 클래슀λ₯Ό μ΄μš©ν•˜λŠ” 방법도 μžˆλ‹€.

import java.util.StringJoiner;

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

        System.out.println(String.join("-", arr));

        StringJoiner sj = new StringJoiner("/", "[", "]");
        for(String s : arr)
            sj.add(s);
        System.out.println(sj.toString());
    }
}

  1.2.7 μœ λ‹ˆμ½”λ“œμ˜ 보좩문자

    String 클래슀의 λ©”μ„œλ“œλ₯Ό 보면 λ§€κ°œλ³€μˆ˜ νƒ€μž…μ΄ int 인 것도 μ‘΄μž¬ν•˜λŠ”λ°, μ΄λŠ” ν™•μž₯된 μœ λ‹ˆμ½”λ“œλ₯Ό 닀루기 μœ„ν•¨μ΄λ‹€.  μœ λ‹ˆμ½”λ“œλŠ” 16bit(2byte) 문자 μ²΄κ³„μ˜€λŠ”λ°, ν˜„μž¬ 20bit 둜 ν™•μž₯λ˜μ—ˆλ‹€. κ·ΈλŸ¬λ―€λ‘œ ν•˜λ‚˜μ˜ 문자λ₯Ό char 으둜 닀루지 λͺ»ν•˜κ³  int 둜 λ‹€λ£¨κ²Œ 된 것인데, ν™•μž₯에 μ˜ν•΄ μƒˆλ‘­κ²Œ μΆ”κ°€λœ λ¬Έμžλ“€μ„ supplementary characters 라고 ν•œλ‹€. String λ©”μ„œλ“œμ˜ λ§€κ°œλ³€μˆ˜κ°€ int 인 κ²½μš°μ—λŠ” 보톡 supplementary characters 을 μ§€μ›ν•˜κ³ , char 인 κ²½μš°μ—λŠ” 그렇지 μ•ŠλŠ”λ‹€λŠ” μ •λ„λ§Œ κΈ°μ–΅ν•΄λ‘μž.

 

  1.2.8 문자 인코딩 λ³€ν™˜

    getBytes(String charsetName) 을 μ‚¬μš©ν•˜λ©΄ λ¬Έμžμ—΄μ˜ 인코딩을 λ³€ν™˜ν•  수 μžˆλ‹€. Java μ—μ„œλŠ” UTF-16 을 μ‚¬μš©ν•˜λŠ”λ° λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄μ— ν¬ν•¨λ˜λŠ” λ¬Έμžλ“€μ€ OS 인코딩을 μ‚¬μš©ν•œλ‹€. ν•œκΈ€μ˜ 경우 CP949 λ₯Ό μ‚¬μš©ν•˜κ³  UTF-8 둜 λ³€κ²½ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ•„λž˜μ™€ 같이 ν•΄μ£Όλ©΄ λœλ‹€.

 

byte[] utf8_str = "κ°€".getBytes("UTF-8");

String str = new String(utf8_str, "UTF_8");

 

μ„œλ‘œ λ‹€λ₯Έ 문자 인코딩을 μ‚¬μš©ν•˜λŠ”λ° 데이터λ₯Ό μ£Όκ³ λ°›κΈ° μœ„ν•΄μ„œλŠ” 인코딩을 잘 해주지 μ•ŠμœΌλ©΄, κΈ€μžκ°€ κΉ¨μ§€κ²Œ λœλ‹€.

 

  1.2.9 String.format()

    format() 은 formating 된 λ¬Έμžμ—΄μ„ λ§Œλ“œλŠ” λ©”μ„œλ“œλ‘œ, printf() 와 μ‚¬μš©λ²•μ΄ λ™μΌν•˜λ‹€.

 

  1.2.10 κΈ°λ³Έν˜• 값을 String 으둜 λ³€ν™˜

    κΈ°λ³Έν˜•μ„ λ¬Έμžμ—΄λ‘œ λ°”κΎΈκΈ° μœ„ν•΄μ„œλŠ” 빈 λ¬Έμžμ—΄μ„ λ”ν•¨μœΌλ‘œμ¨ κ°„λ‹¨νžˆ κ΅¬ν˜„ν•  수 μžˆλ‹€. μ΄λŠ” μ°Έμ‘° λ³€μˆ˜κ°€ κ°€λ¦¬ν‚€λŠ” μΈμŠ€ν„΄μŠ€μ˜ toString() 을 ν˜ΈμΆœν•˜κ²Œ ν•˜λ©°, μ΄λ‘œλΆ€ν„° String 으둜 λ³€ν™˜λ˜μ–΄ λ°˜ν™˜ν•˜λŠ” 것이닀. 이 방법 외에 valueOf() λ₯Ό μ‚¬μš©ν•˜λŠ” 방법도 μžˆλŠ”λ°, 빈 λ¬Έμžμ—΄μ„ λ”ν•˜λŠ” 것 보닀 μ„±λŠ₯이 μ’‹λ‹€. 

 

int i = 100;

String str1 = i + "";

String str2 = String.valueOf(i);

 

 

  1.2.11 String 을 κΈ°λ³Έν˜• κ°’μœΌλ‘œ λ³€ν™˜

   κ·Έλ ‡λ‹€λ©΄ String 을 κΈ°λ³Έν˜•μœΌλ‘œ λ³€ν™˜ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ–΄λ–»κ²Œ ν• κΉŒ? μ΄λŠ” valueOf() λ₯Ό μ΄μš©ν•˜κ±°λ‚˜ parseInt() λ₯Ό μ‚¬μš©ν•˜λ©΄ λœλ‹€.

 

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

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

 

사싀 valueOf() 의 λ°˜ν™˜ νƒ€μž…μ€ int κ°€ μ•„λ‹Œ integer 인데, μ΄λŠ” auto-boxing 에 μ˜ν•΄ μžλ™ λ³€ν™˜λœ 것이닀. parseInt() λ©”μ„œλ“œλŠ” valueOf() 와 λ°˜ν™˜ νƒ€μž…λ§Œ λ‹€λ₯΄μ§€ κ°™λ‹€.

 

public static Integer valueOf(String s) throws NumberFormatException{

return Integer.valueOf(parseInt(s, 10));

}

 

class source{
    public static void main(String[] args) {
        int iVal = 100;
        String strVal = String.valueOf(iVal);

        double dVal = 200.0;
        String strVal2 = dVal + "";

        double sum = Integer.parseInt("+" + strVal) + Double.parseDouble(strVal2);
        double sum2 = Integer.valueOf(strVal) + Double.valueOf(strVal2);

        System.out.println(String.join("", strVal, "+", strVal2, "=") + sum);
        System.out.println(strVal+"+"+strVal2+"="+sum2);

    }
}

parseInt() 와 parseFloat() 같은 λ©”μ„œλ“œλŠ” λ¬Έμžμ—΄μ— 곡백 ν˜Ήμ€ λ¬Έμžκ°€ μžˆλŠ” κ²½μš°μ— NumberFormatException 을 λ°œμƒμ‹œν‚€λ―€λ‘œ, μŠ΅κ΄€μ μœΌλ‘œ trim() 을 μ‚¬μš©ν•˜μ—¬ μ–‘λμ˜ 곡백을 μ œκ±°ν•˜μ—¬ formating ν•˜λŠ” 것이 μ’‹λ‹€. μœ μ˜ν•΄μ•Ό ν•  것은 λΆ€ν˜Έλ₯Ό μ˜λ―Έν•˜λŠ” '+' λ‚˜ μ†Œμˆ˜μ μ„ μ˜λ―Έν•˜λŠ” '.' 그리고 float ν˜• 값을 μ˜λ―Έν•˜λŠ” f 와 같은 μžλ£Œν˜• μ ‘λ―Έμ‚¬λŠ” ν—ˆμš©λœλ‹€. 

 

class source{
    public static void main(String[] args) {
        String fullName = "Hello.java";
        int index = fullName.indexOf('.');
        String fileName = fullName.substring(0, index);
        // fullName.substring(index+1, fullName.length());
        String ext = fullName.substring(index+1);

        System.out.println("name : " + fileName);
        System.out.println("ext : " + ext);
    }
}

 

  1.3 StringBuffer ν΄λž˜μŠ€μ™€ String Builder 클래슀

    StringBuffer λŠ” 내뢀에 λ¬Έμžμ—΄μ„ νŽΈμ§‘ν•  수 μžˆλŠ” buffer κ°€ μžˆμ–΄μ„œ 생성 μ‹œ μ§€μ •λœ λ¬Έμžμ—΄μ„ λ³€κ²½ν•  수 μžˆλ‹€. StringBuffer 을 생성할 λ•Œ, λ²„νΌμ˜ 길이λ₯Ό 지정할 수 μžˆλŠ”λ° λ„ˆλ¬΄ μž‘κ²Œ ν•˜λ©΄ λ²„νΌμ˜ 길이λ₯Ό λŠ˜λ €μ£ΌλŠ” μž‘μ—…μ΄ 더 자주 λ°œμƒν•  수 μžˆμœΌλ―€λ‘œ νš¨μœ¨μ„±μ΄ 떨어짐에 μœ μ˜ν•΄μ•Όν•œλ‹€. 

 

public final class StringBuffer implements java.io.Serializable {

private char[] value;

// ...

}

 

  1.3.1 StringBuffer 의 μƒμ„±μž

  StringBuffer 을 생성할 λ•Œ, 길이λ₯Ό μ—¬μœ  있게 지정해야 ν•œλ‹€. λ§Œμ•½ λ³„λ„λ‘œ λ²„νΌμ˜ 크기λ₯Ό 지정해주지 μ•ŠμœΌλ©΄ 16개의 문자λ₯Ό μ €μž₯ν•  수 μžˆλŠ” 크기의 버퍼λ₯Ό μƒμ„±ν•œλ‹€.

 

public StringBuffer(int length){

value = new char[length];

shared = false;

}

public StringBuffer() {

this(16);

}

public StringBuffer(String str) {

this(str.length() + 16);

append(str);

}

 

  1.3.2 StringBuffer 의 λ³€κ²½

    StringBuffe λŠ” String κ³ΌλŠ” 달리 λ‚΄μš©μ„ λ³€κ²½ν•  수 μžˆλ‹€. StringBuffer sb = new StringBuffer("abc"); λ‘œ StringBuffer 을 μƒμ„±ν•˜μ˜€λ‹€κ³  κ°€μ •ν•΄λ³΄μž. 그리고 sb.append("aa"); λ₯Ό μˆ˜ν–‰ν•œλ‹€λ©΄ sb 의 λ‚΄μš© 뒀에 "aa" κ°€ μΆ”κ°€λ˜λŠ”λ°, 기쑴에 가지고 있던 μžμ‹ μ˜ μ£Όμ†Œλ₯Ό λ°˜ν™˜ν•œλ‹€. κ·Έλž˜μ„œ sb.append("aa").append("cc"); 와 같이도 ν•  수 μžˆλ‹€.

 

  1.3.3 StringBuffer 의 비ꡐ

   StringBuffer ν΄λž˜μŠ€μ—μ„œλŠ” equals λ©”μ„œλ“œλ₯Ό λ³„λ„λ‘œ μ˜€λ²„λΌμ΄λ”© ν•˜μ§€ μ•Šμ•„μ„œ, String ν΄λž˜μŠ€μ—μ„œ == λ“±κ°€ 비ꡐ μ—°μ‚°μžλ₯Ό μ‚¬μš©ν•œ 것과 같은 κ²°κ³Όκ°€ λ‚˜νƒ€λ‚œλ‹€. κ·Έλž˜μ„œ μ›ν™œν•˜κ²Œ λΉ„κ΅ν•˜κΈ° μœ„ν•΄μ„œλŠ” toString() 을 ν˜ΈμΆœν•œ λ‹€μŒμ— equals() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ’‹λ‹€.

 

  1.3.4 StringBuffer 클래슀의 μƒμ„±μžμ™€ λ©”μ„œλ“œ

https://www.geeksforgeeks.org/stringbuffer-class-in-java/

 

StringBuffer class in Java - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

  1.3.5 StringBuilder λž€?

   StringBuffer λŠ” λ©€ν‹°μ“°λ ˆλ“œμ— μ•ˆμ „ν•˜λ„λ‘ λ™κΈ°ν™”λ˜μ–΄μžˆλŠ”λ°, μ΄λŠ” StringBuffer 의 μ„±λŠ₯을 λ–¨μ–΄λœ¨λ¦¬λŠ” μš”μΈμ΄ λœλ‹€. λ©€ν‹° μ“°λ ˆλ”©μ΄ ν•„μš”μ—†λŠ” ν”„λ‘œκ·Έλž¨μ˜ 경우 StringBuffer λŠ” λΆˆν•„μš”ν•œ λ™κΈ°ν™”λ‘œ 인해 μ„±λŠ₯이 μ €ν•˜λœλ‹€. κ·Έλž˜μ„œ μ“°λ ˆλ“œλ₯Ό λ™κΈ°ν™”ν•˜λŠ” κΈ°λŠ₯만 μ œμ™Έν•œ StringBuilder κ°€ μΆ”κ°€λœ κ²ƒμœΌλ‘œ, StringBuffer 와 μ™„μ „νžˆ λ™μΌν•˜λ‹€.

 

  1.4 래퍼(wrapper) 클래슀

    OOP μ—μ„œλŠ” λͺ¨λ“  것은 λ‹€ Object 둜 닀루어져야 ν•˜μ§€λ§Œ μžλ°”μ—μ„œλŠ” 8개의 κΈ°λ³Έν˜•μ„ Object 둜 닀루지 μ•ŠλŠ” λŒ€μ‹  높은 μ„±λŠ₯을 μ–»μ—ˆλ‹€. (μ™„μ „ν•œ 객체지ν–₯ μ–Έμ–΄κ°€ μ•„λ‹ˆλΌλŠ” 것이닀.) ν•˜μ§€λ§Œ κΈ°λ³Έν˜• λ³€μˆ˜ λ˜ν•œ 객체둜 닀루어야 ν•˜λŠ” κ²½μš°κ°€ λ°œμƒν•˜λ©° μ΄λ•Œ μ‚¬μš©ν•˜λŠ” 것이 wrapper class 이닀. 

 

public final class Integer extends Number implements Comparable { 

...

private int value;

...

}

 

  wrapper Class λŠ” λͺ¨λ‘ equals() λ©”μ„œλ“œκ°€ μ˜€λ²„λΌμ΄λ”© λ˜μ–΄ μžˆμ–΄ νŽΈλ¦¬ν•˜λ‹€. μ˜€ν† λ°•μ‹±μ΄ λœλ‹€κ³  ν•  지라도 Integer 객체에 비ꡐ μ—°μ‚°μžλ₯Ό μ‚¬μš©ν•  수 μ—†λŠ”λ°, μ΄λŸ¬ν•œ κ²½μš°μ—λŠ” compareTo() λ₯Ό μ‚¬μš©ν•˜λ©΄ λœλ‹€. λ˜ν•œ toString() 도 μ˜€λ²„λΌμ΄λ”©λ˜μ–΄μ„œ 객체가 가지고 μžˆλŠ” 값을 λ¬Έμžμ—΄μœΌλ‘œ λ³€ν™˜ν•˜μ—¬ λ°˜ν™˜ν•œλ‹€.

 

  1.5 μ˜€ν† λ°•μ‹± & μ–Έλ°•μ‹± (autoboxing & unboxing)

   JDK1.5 μ΄μ „μ—μ„œλŠ” κΈ°λ³Έν˜•κ³Ό μ°Έμ‘°ν˜•μ˜ 연산이 λΆˆκ°€λŠ₯ν•˜μ—¬ wrapper 클래슀둜 κΈ°λ³Έν˜•μ„ 객체둜 λ§Œλ“  λ‹€μŒ 연산을 ν–ˆμ–΄μ•Όλ§Œ ν–ˆλ‹€. ν•˜μ§€λ§Œ ν˜„μž¬λŠ” κ°€λŠ₯ν•˜λ©°, μ»΄νŒŒμΌλŸ¬μ— μ˜ν•΄ κΈ°λ³Έν˜• 값을 wrapper class 의 객체둜 μžλ™μœΌλ‘œ λ³€ν™˜ν•΄μ£ΌλŠ” 것을 autoboxing 이라고 ν•˜κ³ , λ°˜λŒ€μ˜ μž‘μ—…μ€ unboxing 이라고 ν•œλ‹€.


References

http://www.yes24.com/Product/Goods/24259565

 

Java의 정석 - YES24

졜근 7λ…„λ™μ•ˆ μžλ°” λΆ„μ•Όμ˜ 베슀트 μ…€λŸ¬ 1μœ„λ₯Ό μ§€μΌœμ˜¨ `μžλ°”μ˜ 정석`의 μ΅œμ‹ νŒ. μ €μžκ°€ μΉ΄νŽ˜μ—μ„œ 12λ…„κ°„ 직접 λ…μžλ“€μ—κ²Œ 닡변을 ν•΄μ˜€λ©΄μ„œ μ΄ˆλ³΄μžκ°€ μ–΄λ €μ›Œν•˜λŠ” 뢀뢄을 잘 νŒŒμ•…ν•˜κ³  μ“΄ μ±…. 뿐만 μ•„

www.yes24.com

 

 

728x90
λ°˜μ‘ν˜•
LIST
Comments