Skip to content

Latest commit

Β 

History

History
740 lines (500 loc) Β· 30.9 KB

File metadata and controls

740 lines (500 loc) Β· 30.9 KB

item 17 : λ³€κ²½ κ°€λŠ₯성을 μ΅œμ†Œν™”ν•˜λΌ

1. 뢈편 클래슀

{% hint style="info" %} λΆˆλ³€ ν΄λž˜μŠ€λŠ” κ°€λ³€ ν΄λž˜μŠ€λ³΄λ‹€ μ„€κ³„ν•˜κ³  κ΅¬ν˜„ν•˜κ³  μ‚¬μš©ν•˜κΈ° μ‰¬μš°λ©°, 였λ₯˜κ°€ 생길 여지도 적고 훨씬 μ•ˆμ „ν•˜λ‹€. {% endhint %}

1) λΆˆλ³€ ν΄λž˜μŠ€λž€?

μΈμŠ€ν„΄μŠ€μ˜ λ‚΄λΆ€ 값을 μˆ˜μ •ν•  수 μ—†λŠ” ν΄λž˜μŠ€λ‹€. λΆˆλ³€ μΈμŠ€ν„΄μŠ€μ— κ°„μ§λœ μ •λ³΄λŠ” κ³ μ •λ˜μ–΄ 객체가 νŒŒκ΄΄λ˜λŠ” μˆœκ°„κΉŒμ§€ μ ˆλŒ€ 달라지지 μ•ŠλŠ”λ‹€.

2. 클래슀λ₯Ό λΆˆλ³€μœΌλ‘œ λ§Œλ“€κΈ° μœ„ν•΄ μ§€μΌœμ•Ό ν•˜λŠ” 5가지 κ·œμΉ™

1) 객체의 μƒνƒœλ₯Ό λ³€κ²½ν•˜λŠ” λ©”μ„œλ“œ(λ³€κ²½μž) setterλ₯Ό μ œκ³΅ν•˜μ§€ μ•ŠλŠ”λ‹€.

ν•˜μ§€λ§Œ λΆˆλ³€ 객체의 μ€‘μš”μ„±μ΄ λŒ€λ‘λ˜λ©° setter μ‚¬μš©μ„ μ§€μ–‘ν•˜λŠ” νλ¦„μœΌλ‘œ λ³€ν™”λ˜μ—ˆλ‹€.

Date ν΄λž˜μŠ€λŠ” λ‹€μŒκ³Ό 같은 λ§Žμ€ λ³€κ²½ λ©”μ„œλ“œλ“€μ΄ μžˆμ—ˆλŠ”λ°...

image

ν•˜μ§€λ§Œ λ³€κ²½ν•  수 μ—†κ³ , λ©€ν‹° μŠ€λ ˆλ“œμ—μ„œλ„ μ•ˆμ „ν•œ λ‚ μ§œ & μ‹œκ°„ κ΄€λ ¨ ν΄λž˜μŠ€κ°€ ν•„μš”ν–ˆλ‹€.

κ·Έλž˜μ„œ λ“±μž₯ν•œ LocalDate ν΄λž˜μŠ€λŠ” setter λ₯Ό μ œκ³΅ν•˜μ§€ μ•ŠλŠ” λΆˆλ³€ 클래슀 μž„

🧐 λ§Œμ•½ 객체의 μƒνƒœλ₯Ό λ³€κ²½ν•˜λŠ” λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•œλ‹€λ©΄?

public class Person {

    private String name;

    public Person(final String name) {
        this.name = name;
    }

    public String name() {
        return name;
    }
    
    /* λ³€κ²½μž */
    public void setName(final String name) {
        this.name = name;
    }
}

λ°”κΉ₯μ—μ„œ 객체의 μƒνƒœλ₯Ό λ³€κ²½ν•  수 있음

public class Main {

    public static void main(String[] args) {
        Person boki = new Person("boki ");
        System.out.println(boki .name());
        boki.setName("lotto");
        System.out.println(boki.name()); // lotto
    }
}

클래슀λ₯Ό λΆˆλ³€μœΌλ‘œ λ§Œλ“€ 수 μ—†μŒ

2) 클래슀λ₯Ό ν™•μž₯ν•  수 없도둝 ν•œλ‹€.

ν•˜μœ„ ν΄λž˜μŠ€μ—μ„œ λΆ€μ£Όμ˜ν•˜κ²Œ ν˜Ήμ€ λ‚˜μœ μ˜λ„λ‘œ 객체의 μƒνƒœλ₯Ό λ³€ν•˜κ²Œ λ§Œλ“œλŠ” μ‚¬νƒœλ₯Ό 막아쀀닀. 상속을 λ§‰λŠ” λŒ€ν‘œμ  인 방법은 클래슀λ₯Ό final둜 μ„ μ–Έν•˜λŠ” 것이닀.

μƒμ†μ˜ 방법을 λ§‰λŠ” 방법 2가지

  1. 클래슀λ₯Ό final둜 μ„€μ •ν•˜λŠ” 것

클래슀λ₯Ό 상속받을 수 μžˆλ‹€λ©΄, ν•΄λ‹Ή ν΄λž˜μŠ€λŠ” 값이 변경될 수 μžˆμŒμ„ μ˜λ―Έν•˜λŠ”λ°, 이게 μ–΄λ–»κ²Œ κ°€λŠ₯ν• κΉŒλ₯Ό final둜 ν•˜μ§€ μ•Šμ•˜μ„ λ•Œλ₯Ό 보면!

public class Animal {

    private String type;

    public Animal(final String type) {
        this.type = type;
    }

    public String type() {
        return type; // type() λ©”μ„œλ“œλ₯Ό 톡해 type ν•„λ“œμ˜ 값을 λ°˜ν™˜
    }
}

setterλ₯Ό μ œκ±°ν•¨μœΌλ‘œμ¨ 객체의 μƒνƒœλ₯Ό λ³€κ²½ν•˜λŠ” λ©”μ„œλ“œ 제곡 ν•˜μ§€ μ•Šλ„λ‘ μˆ˜μ •

{% hint style="success" %} κ·Έλ ‡λ‹€λ©΄ 값을 λ°”κΎΈλ €λ©΄? private둜 λ‚΄λΆ€ κ°’ λ°”κΏ€ μˆ˜λŠ” μ—†μœΌλ‚˜ 바뀐 κ²ƒμ²˜λŸΌ μ‚¬μš© κ°€λŠ₯ {% endhint %}

Dog 클래슀 (Animal을 상속):

public class Dog extends Animal {

    private String type;

    // type ν•„λ“œλ₯Ό private으둜 μ„ μ–Έν•˜κ³ , μƒμ„±μžλ₯Ό 톡해 μ΄ˆκΈ°ν™”
    public Dog(final String type) {
        super(type);
        this.type = "Unknown Type " + type;  // μƒμ„±μžμ—μ„œ this.type에 "Unknown Type " λ¬Έμžμ—΄μ„ λΆ™μ—¬ μ΄ˆκΈ°ν™”
    }

    @Override
    public String type() {
        return "Dog Type: " + this.type;
    }
}

Main 클래슀:

public class Main {

    // animal.type()을 ν˜ΈμΆœν•˜λ©΄ Dog ν΄λž˜μŠ€μ—μ„œ μ˜€λ²„λΌμ΄λ”©λœ type() λ©”μ„œλ“œκ°€ 호좜
    public static void main(String[] args) {
        Animal animal = new Dog("Bulldog");
        System.out.println(animal.type());
    }
}

μ‹€ν–‰ κ²°κ³Ό:

Dog Type: Unknown Type Bulldog

이 μ½”λ“œκ°€ λ³΄μ—¬μ£ΌλŠ” 점:

  • 상속을 ν†΅ν•œ λ™μž‘ λ³€κ²½: Dog ν΄λž˜μŠ€λŠ” Animal 클래슀λ₯Ό 상속받아 type() λ©”μ„œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ”©ν•˜κ³  μžˆλ‹€. 이λ₯Ό 톡해 λΆ€λͺ¨ 클래슀의 λ™μž‘μ„ μžμ‹ ν΄λž˜μŠ€μ—μ„œ λ³€κ²½ν•  수 μžˆμŒμ„ 보여쀀닀.
  • ν•„λ“œ μˆ¨κΉ€(Field Hiding): Dog ν΄λž˜μŠ€μ—μ„œ type ν•„λ“œλ₯Ό λ‹€μ‹œ μ„ μ–Έν•¨μœΌλ‘œμ¨, λΆ€λͺ¨ 클래슀의 type ν•„λ“œλ₯Ό 가리고 μžˆλ‹€. μ΄λŠ” 쒋은 섀계가 μ•„λ‹ˆλ©°, ν˜Όλž€μ„ μ΄ˆλž˜ν•  수 μžˆλ‹€.
  • λΆˆλ³€μ„± μœ„λ°˜ κ°€λŠ₯μ„±: Animal ν΄λž˜μŠ€λŠ” μžμ‹ μ˜ ν•„λ“œλ₯Ό private으둜 μ„ μ–Έν•˜κ³  μžˆμ§€λ§Œ, 상속을 톡해 μžμ‹ ν΄λž˜μŠ€μ—μ„œ λ™μž‘μ΄ 변경될 수 μžˆλ‹€. λ”°λΌμ„œ λΆˆλ³€μ„±μ„ 보μž₯ν•˜κΈ° μœ„ν•΄μ„œλŠ” 클래슀의 상속을 λ§‰κ±°λ‚˜(final 클래슀), ν•„λ“œλ₯Ό final둜 μ„ μ–Έν•˜λŠ” λ“±μ˜ μ‘°μΉ˜κ°€ ν•„μš”ν•˜λ‹€.

λΆˆλ³€μ„±μ„ μœ μ§€ν•˜λŠ” 방법:

  • 클래슀λ₯Ό final둜 μ„ μ–Έν•˜μ—¬ 상속을 κΈˆμ§€
public final class Animal {
    // 클래슀 λ‚΄μš©
}
  • ν•„λ“œλ₯Ό private final둜 μ„ μ–Έν•˜μ—¬ ν•„λ“œκ°€ ν•œ 번 μ΄ˆκΈ°ν™”λœ ν›„ λ³€κ²½λ˜μ§€ μ•Šλ„λ‘ ν•œλ‹€.
private final String type;
  • λ©”μ„œλ“œλ₯Ό final둜 μ„ μ–Έν•˜μ—¬ μ˜€λ²„λΌμ΄λ”©μ„ λ°©μ§€ν•œλ‹€.
public final String type() {
    return type;
}

μˆ˜μ •λœ Animal 클래슀 (λΆˆλ³€μ„±μ„ μœ μ§€ν•˜λ„λ‘):

public final class Animal {

    private final String type;

    public Animal(final String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }
}

μ΄λ ‡κ²Œ μˆ˜μ •ν•˜λ©΄ Animal 클래슀λ₯Ό 상속할 수 μ—†μœΌλ©°, ν•„λ“œλ„ λ³€κ²½ν•  수 μ—†κ²Œ λ˜μ–΄ λΆˆλ³€μ„±μ΄ μœ μ§€λœλ‹€.

{% hint style="warning" %} μ—¬κΈ°μ„œ 의문 : μ΄λ ‡κ²Œν•˜λ©΄ν΄λΌμ΄μ–ΈνŠΈκ°€ κ²°κ΅­ μˆ˜μ •λ„ λͺ»ν•΄μ„œ λΆˆλ³€ ν΄λž˜μŠ€λŠ” 였히렀 μ•ˆμ’‹μ€κ²Œ μ•„λ‹Œκ°€? {% endhint %}

λ‹΅λ³€ : λΆˆλ³€ 클래슀λ₯Ό μ‚¬μš©ν•˜λ©΄ ν΄λΌμ΄μ–ΈνŠΈκ°€ 객체의 μƒνƒœλ₯Ό 직접 μˆ˜μ •ν•  수 μ—†κΈ° λ•Œλ¬Έμ— λΆˆνŽΈν•˜λ‹€κ³  λŠλ‚„ 수 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ λΆˆλ³€ ν΄λž˜μŠ€λŠ” μ—¬λŸ¬ 가지 μž₯점을 μ œκ³΅ν•˜λ©°, μ΄λŸ¬ν•œ 이점을 톡해 μ½”λ“œμ˜ μ•ˆμ •μ„±κ³Ό 신뒰성을 높일 수 μžˆμŠ΅λ‹ˆλ‹€.

ν΄λΌμ΄μ–ΈνŠΈκ°€ 값을 λ³€κ²½ν•˜κ³  싢을 λ•Œ

λΆˆλ³€ ν΄λž˜μŠ€μ—μ„œλŠ” 객체의 μƒνƒœλ₯Ό 직접 λ³€κ²½ν•  수 μ—†μ§€λ§Œ, λ³€κ²½λœ 값을 가진 μƒˆλ‘œμš΄ 객체λ₯Ό μƒμ„±ν•˜μ—¬ λ°˜ν™˜ν•˜λŠ” 방법을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œ ν”νžˆ μ‚¬μš©ν•˜λŠ” νŒ¨ν„΄μœΌλ‘œ, 객체의 λΆˆλ³€μ„±μ„ μœ μ§€ν•˜λ©΄μ„œ ν•„μš”ν•œ 변경을 κ°€λŠ₯ν•˜κ²Œ ν•©λ‹ˆλ‹€.

μ˜ˆμ‹œ: Animal ν΄λž˜μŠ€μ— λ³€κ²½ λ©”μ„œλ“œ μΆ”κ°€

public final class Animal {

    private final String type;

    public Animal(final String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }

    // μƒˆλ‘œμš΄ νƒ€μž…μ˜ Animal 객체λ₯Ό μƒμ„±ν•˜μ—¬ λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œ
    public Animal withType(String newType) {
        return new Animal(newType);
    }
}

μ‚¬μš© 방법

public class Main {

    public static void main(String[] args) {
        Animal animal = new Animal("Cat");

        // κΈ°μ‘΄ 객체λ₯Ό μˆ˜μ •ν•˜λŠ” 것이 μ•„λ‹ˆλΌ μƒˆλ‘œμš΄ 객체λ₯Ό 생성
        Animal newAnimal = animal.withType("Dog");

        System.out.println(animal.getType());      // 좜λ ₯: Cat
        System.out.println(newAnimal.getType());   // 좜λ ₯: Dog
    }
}

μœ„ μ˜ˆμ‹œμ—μ„œ animal κ°μ²΄λŠ” μ—¬μ „νžˆ "Cat" νƒ€μž…μ„ μœ μ§€ν•˜λ©°, newAnimal은 "Dog" νƒ€μž…μ„ κ°–λŠ” μƒˆλ‘œμš΄ κ°μ²΄μž…λ‹ˆλ‹€. 이처럼 λΆˆλ³€ 객체λ₯Ό μ‚¬μš©ν•˜λ©΄ 원본 객체의 μƒνƒœλ₯Ό λ³€κ²½ν•˜μ§€ μ•ŠμœΌλ©΄μ„œλ„ μ›ν•˜λŠ” 값을 가진 객체λ₯Ό 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.

λΆˆλ³€ ν΄λž˜μŠ€λŠ” λ‹€μŒκ³Ό 같은 이유둜 μœ μš©ν•©λ‹ˆλ‹€:

  • μ•ˆμ „ν•˜κ³  μ‹ λ’°ν•  수 μžˆλŠ” μ½”λ“œ μž‘μ„±: μƒνƒœ 변경에 λ”°λ₯Έ 예기치 μ•Šμ€ 버그λ₯Ό 쀄일 수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ©€ν‹°μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œμ˜ μ•ˆμ „μ„± 확보: 동기화 없이도 μŠ€λ ˆλ“œ μ•ˆμ „μ„±μ„ 보μž₯ν•©λ‹ˆλ‹€.
  • μœ μ§€λ³΄μˆ˜ μš©μ΄μ„±: 객체의 μƒνƒœκ°€ λ³€ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ μ½”λ“œμ˜ λ³΅μž‘μ„±μ΄ κ°μ†Œν•©λ‹ˆλ‹€.

{% hint style="danger" %} κ²°κ΅­ μ—¬λŸ¬κ°œμ˜ 객체λ₯Ό μƒμ„±ν•˜λŠ” 건 λ©”λͺ¨λ¦¬λ₯Ό 많이 μ°¨μ§€ν•˜λŠ” 것과도 연관이 μžˆλŠ”κ±° μ•„λ‹Œκ°€? {% endhint %}

λ‹΅λ³€ : λ§žμŠ΅λ‹ˆλ‹€. λΆˆλ³€ 객체λ₯Ό μ‚¬μš©ν•  λ•Œ λ³€κ²½ν•  λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄ 객체λ₯Ό μƒμ„±ν•˜λ―€λ‘œ, λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ΄ 증가할 수 μžˆλ‹€λŠ” μš°λ €κ°€ μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” λΆˆλ³€ 클래슀의 단점 쀑 ν•˜λ‚˜λ‘œ μ–ΈκΈ‰λ˜κΈ°λ„ ν•©λ‹ˆλ‹€. ν•˜μ§€λ§Œ μ‹€μ œλ‘œλŠ” μ΄λŸ¬ν•œ λ©”λͺ¨λ¦¬ μ‚¬μš© 증가가 μ‹¬κ°ν•œ 문제λ₯Ό μΌμœΌν‚€λŠ” κ²½μš°λŠ” λ“œλ­…λ‹ˆλ‹€.

  • λΆˆλ³€ 객체 μ‚¬μš©μœΌλ‘œ μΈν•œ λ©”λͺ¨λ¦¬ μ‚¬μš© μ¦κ°€λŠ” μ‘΄μž¬ν•˜μ§€λ§Œ, ν˜„λŒ€ JVM의 μ΅œμ ν™”μ™€ ν”„λ‘œκ·Έλž˜λ° 기법을 톡해 κ·Έ 영ν–₯을 μ΅œμ†Œν™”ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • λΆˆλ³€ 클래슀의 μž₯점인 μ•ˆμ •μ„±, μŠ€λ ˆλ“œ μ•ˆμ „μ„±, μœ μ§€λ³΄μˆ˜μ„± ν–₯상은 λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰ μ¦κ°€λ‘œ μΈν•œ 단점을 μƒμ‡„ν•˜κ³ λ„ λ‚¨μŠ΅λ‹ˆλ‹€.
  • μ‹€μ œ κ°œλ°œμ—μ„œλŠ” λΆˆλ³€ 객체λ₯Ό μš°μ„ μ μœΌλ‘œ μ‚¬μš©ν•˜κ³ , λ©”λͺ¨λ¦¬ μ‚¬μš©μ΄ λ¬Έμ œκ°€ λ˜λŠ” 뢀뢄에 ν•œν•΄ μ΅œμ ν™” 기법을 μ μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.
  1. public 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•˜λŠ” 방법

λͺ¨λ“  μƒμ„±μžλ₯Ό private ν˜Ήμ€ package-private(default)둜 λ§Œλ“€ ν›„, public 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•˜λŠ” 방법

public final class Animal {

    private final String type;

    // private μƒμ„±μž: μ™ΈλΆ€μ—μ„œ 직접 μΈμŠ€ν„΄μŠ€ 생성 λΆˆκ°€
    private Animal(final String type) {
        this.type = type;
    }

    // public 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œ
    public static Animal of(String type) {
        return new Animal(type);
    }

    public String getType() {
        return type;
    }
}

package μ™ΈλΆ€μ—μ„œλŠ” 사싀상 final ν΄λž˜μŠ€μ™€ λ™μΌν•˜κ²Œ λ™μž‘ν•  λΏλ”λŸ¬, λ‚΄λΆ€μ—μ„œλŠ” ν•΄λ‹Ή 클래슀λ₯Ό μƒμ†ν•˜μ—¬ μ—¬λŸ¬ 클래슀λ₯Ό 생성할 수 있기 λ•Œλ¬Έμ— 훨씬 μœ μ—°ν•œ 방법

μ‚¬μš©λ²•

public class Main {

    public static void main(String[] args) {
        Animal cat = Animal.of("Cat");
        Animal dog = Animal.of("Dog");

        System.out.println(cat.getType()); // 좜λ ₯: Cat
        System.out.println(dog.getType()); // 좜λ ₯: Dog
    }
}

🌱 μΈμŠ€ν„΄μŠ€ 캐싱을 ν†΅ν•œ λ©”λͺ¨λ¦¬ 효율 κ°œμ„ 

정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ 캐싱을 톡해 λ™μΌν•œ 값을 가진 객체의 쀑볡 생성을 방지할 수 μžˆλ‹€.

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public final class Animal {

    private static final Map<String, Animal> CACHE = new ConcurrentHashMap<>();

    private final String type;

    private Animal(final String type) {
        this.type = type;
    }

    public static Animal of(String type) {
        // 이미 ν•΄λ‹Ή νƒ€μž…μ˜ 객체가 μ‘΄μž¬ν•˜λ©΄ μž¬μ‚¬μš©
        return CACHE.computeIfAbsent(type, t -> new Animal(t));
    }

    public String getType() {
        return type;
    }
}

μ„€λͺ…

  • CACHE 맡:
    • ConcurrentHashMap을 μ‚¬μš©ν•˜μ—¬ μŠ€λ ˆλ“œ μ•ˆμ „ν•˜κ²Œ 캐싱을 κ΅¬ν˜„ν•œλ‹€.
    • ν‚€λŠ” type λ¬Έμžμ—΄μ΄κ³ , 값은 ν•΄λ‹Ή Animal 객체이닀.
  • computeIfAbsent λ©”μ„œλ“œ:
    • CACHE에 ν•΄λ‹Ή type의 객체가 없을 κ²½μš°μ—λ§Œ μƒˆλ‘œμš΄ 객체λ₯Ό μƒμ„±ν•˜κ³ , 이미 μ‘΄μž¬ν•˜λ©΄ κ·Έ 객체λ₯Ό λ°˜ν™˜ν•œλ‹€.
  • λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰ κ°μ†Œ:
    • λ™μΌν•œ type을 가진 객체λ₯Ό μž¬μ‚¬μš©ν•˜λ―€λ‘œ λΆˆν•„μš”ν•œ 객체 생성을 쀄여 λ©”λͺ¨λ¦¬ νš¨μœ¨μ„ 높일 수 μžˆλ‹€.

μΊμ‹±λœ 객체 μ‚¬μš© μ˜ˆμ‹œ

public class Main {

    public static void main(String[] args) {
        Animal animal1 = Animal.of("Dog");
        Animal animal2 = Animal.of("Dog");

        System.out.println(animal1 == animal2); // 좜λ ₯: true (같은 μΈμŠ€ν„΄μŠ€)
    }
}
  • animal1κ³Ό animal2λŠ” λ™μΌν•œ μΈμŠ€ν„΄μŠ€λ₯Ό μ°Έμ‘°ν•˜λ―€λ‘œ == μ—°μ‚° κ²°κ³Όκ°€ true이닀.
  • μ΄λŠ” λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ„ 쀄이고 객체 비ꡐ μ‹œ νš¨μœ¨μ„±μ„ 높인닀.

{% hint style="danger" %} κ·Έλž˜μ„œ LocalDate ν΄λž˜μŠ€λŠ” final 클래슀둜 μ„ μ–Έν•˜μ—¬ 상속을 λ°©μ§€ν•˜κ³  μžˆλ‹€. {% endhint %}

image

3) λͺ¨λ“  ν•„λ“œλ₯Ό final둜 μ„ μ–Έν•œλ‹€. μ‹œμŠ€ν…œμ΄ κ°•μ œν•˜λŠ” μˆ˜λ‹¨μ„ μ΄μš©ν•΄ μ„€κ³„μž 의 μ˜λ„λ₯Ό λͺ…ν™•νžˆ λ“œλŸ¬λ‚΄λŠ” 방법이닀.

μžλ°” μ–Έμ–΄μ—μ„œ final을 μ‚¬μš©ν•˜λ©΄ 값을 λ°”κΏ€ 수 μ—†κΈ° λ•Œλ¬Έμ—, λͺ…μ‹œμ μœΌλ‘œ 값을 λΆˆλ³€μœΌλ‘œ λ§Œλ“€ 수 μžˆλ‹€.

μƒˆλ‘œ μƒμ„±λœ μΈμŠ€ν„΄μŠ€λ₯Ό 동기화 없이 λ‹€λ₯Έ μŠ€λ ˆλ“œμ— μ „λ‹¬ν•˜λ”λΌλ„ λ¬Έμ œμ—†μ΄ λ™μž‘ν•˜κ²Œλ” λ™μž‘ν•˜λŠ” 데에도 ν•„μš”ν•˜λ‹€.

λ©€ν‹° μŠ€λ ˆλ“œμ—μ„œλ„ μ•ˆμ „ν•˜λ‹€κ³ ν•¨

Date 클래슀 λ‚΄λΆ€μ μœΌλ‘œ CalendarDate λΌλŠ” ν΄λž˜μŠ€κ°€ μ‚¬μš©λ˜λŠ”λ°..

CalendarDate 클래슀의 경우 λͺ¨λ“  ν•„λ“œκ°€ λ³€κ²½ κ°€λŠ₯ν•œ ν•„λ“œμΈλ° λ°˜ν•΄, LocalDate λŠ” λͺ¨λ‘ final 이 λΆ™μ–΄μžˆλ‹€.

image

4) λͺ¨λ“  ν•„λ“œλ₯Ό private으둜 μ„ μ–Έν•œλ‹€.

ν•„λ“œκ°€ μ°Έμ‘°ν•˜λŠ” κ°€λ³€ 객체λ₯Ό ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ 직접 μ ‘κ·Όν•΄ μˆ˜μ •ν•˜λŠ” 일을 막아쀀닀. κΈ°μˆ μ μœΌλ‘œλŠ” κΈ°λ³Έ νƒ€μž… ν•„λ“œ λ‚˜ λΆˆλ³€ 객체λ₯Ό μ°Έμ‘°ν•˜λŠ” ν•„λ“œλ₯Ό public final둜만 선언해도 λΆˆλ³€ 객체가 λ˜μ§€λ§Œ, μ΄λ ‡κ²Œ ν•˜λ©΄ λ‹€μŒ λ¦΄λ¦¬μŠ€μ—μ„œ λ‚΄λΆ€ ν‘œν˜„μ„ 바꾸지 λͺ»ν•˜λ―€λ‘œ κΆŒν•˜μ§€λŠ” μ•ŠλŠ”λ‹€κ³  함

5) μžμ‹  μ™Έμ—λŠ” λ‚΄λΆ€μ˜ κ°€λ³€ μ»΄ν¬λ„ŒνŠΈμ— μ ‘κ·Όν•  수 없도둝 ν•œλ‹€.

ν΄λž˜μŠ€μ— κ°€λ³€ 객체λ₯Ό μ°Έμ‘°ν•˜λŠ” ν•„λ“œκ°€ ν•˜λ‚˜λΌλ„ μžˆλ‹€λ©΄ ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ κ·Έ 객체의 μ°Έμ‘°λ₯Ό 얻을 수 없도둝 ν•΄μ•Ό ν•œλ‹€.

이런 ν•„λ“œλŠ” μ ˆλŒ€ ν΄λΌμ΄μ–ΈνŠΈκ°€ μ œκ³΅ν•œ 객 체 μ°Έμ‘°λ₯Ό κ°€λ¦¬ν‚€κ²Œ ν•΄μ„œλŠ” μ•ˆ 되며, μ ‘κ·Όμž λ©”μ„œλ“œκ°€ κ·Έ ν•„λ“œλ₯Ό κ·ΈλŒ€λ‘œ λ°˜ν™˜ν•΄μ„œλ„ μ•ˆ λœλ‹€. μƒμ„±μž, μ ‘κ·Όμž, readObject λ©”μ„œλ“œ λͺ¨λ‘μ—μ„œ 방어적 볡사λ₯Ό μˆ˜ν–‰ν•΄μ•Ό ν•œλ‹€.

// Date.setDate λ©”μ„œλ“œ
public void setDate(int date) {
  getCalendarDate().setDayOfMonth(date);
}

// CalendarDate.setDayOfMonth λ©”μ„œλ“œ
public CalendarDate setDayOfMonth(int date) {
    if (dayOfMonth != date) {
        dayOfMonth = date;
        normalized = false;
    }
    return this;
}

// ---------------------------------------------------------------------------

// LocalDate.plusDays λ©”μ„œλ“œ
public LocalDate plusDays(long daysToAdd) {
    if (daysToAdd == 0) {
    return this;
  }
  long dom = day + daysToAdd;
  if (dom > 0) {
    if (dom <= 28) {
      return new LocalDate(year, month, (int) dom);
    } else if (dom <= 59) { // 59th Jan is 28th Feb, 59th Feb is 31st Mar
      long monthLen = lengthOfMonth();
      if (dom <= monthLen) {
        return new LocalDate(year, month, (int) dom);
      } else if (month < 12) {
        return new LocalDate(year, month + 1, (int) (dom - monthLen));
      } else {
        YEAR.checkValidValue(year + 1);
        return new LocalDate(year + 1, 1, (int) (dom - monthLen));
      }
    }
  }

  long mjDay = Math.addExact(toEpochDay(), daysToAdd);
  return LocalDate.ofEpochDay(mjDay);
}

// LocalDate.ofEpochDay λ©”μ„œλ“œ
public static LocalDate ofEpochDay(long epochDay) {
  ... μƒλž΅ 
  return new LocalDate(year, month, dom);
}

Date ν΄λž˜μŠ€λŠ” λ‚΄λΆ€ μΊ˜λ¦°λ” 객체의 λ‚ μ§œ ν•„λ“œλ₯Ό λ³€κ²½ν•˜λŠ” λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•œλ‹€.

λ°˜λ©΄μ— LocalDate 의 κ²½μš°μ—λŠ” return λ¬Έμ—μ„œ 항상 μƒˆλ‘œμš΄ 객체λ₯Ό μƒμ„±ν•˜μ—¬ λ°˜ν™˜ν•œλ‹€.

3. 이 λͺ¨λ“  것을 μ§€ν‚€λŠ” 클래슀 예제

import java.util.Date;

public final class Person {
    // 1) λͺ¨λ“  ν•„λ“œλ₯Ό private final둜 μ„ μ–Έ
    private final String name;
    private final Date birthDate; // DateλŠ” κ°€λ³€ κ°μ²΄μ΄λ―€λ‘œ 방어적 볡사 ν•„μš”

    // 2) μƒμ„±μžλ₯Ό 톡해 ν•„λ“œλ₯Ό μ΄ˆκΈ°ν™”ν•˜κ³ , κ°€λ³€ κ°μ²΄λŠ” 방어적 볡사 μˆ˜ν–‰
    public Person(String name, Date birthDate) {
        this.name = name;
        // 방어적 볡사 (μ™ΈλΆ€μ—μ„œ μ „λ‹¬λœ birthDate의 μ°Έμ‘°λ₯Ό 직접 μ‚¬μš©ν•˜μ§€ μ•ŠμŒ)
        this.birthDate = new Date(birthDate.getTime());
    }

    // 3) getter λ©”μ„œλ“œλ„ κ°€λ³€ 객체에 λŒ€ν•΄ 방어적 볡사λ₯Ό μˆ˜ν–‰
    public String getName() {
        return name; // String은 λΆˆλ³€ 객체라 방어적 볡사 ν•„μš” μ—†μŒ
    }

    public Date getBirthDate() {
        // 방어적 볡사 (λ‚΄λΆ€μ˜ Date 객체λ₯Ό 외뢀에 κ·ΈλŒ€λ‘œ λ°˜ν™˜ν•˜μ§€ μ•ŠμŒ)
        return new Date(birthDate.getTime());
    }

    // 4) setter λ©”μ„œλ“œ μ œκ³΅ν•˜μ§€ μ•ŠμŒ (객체 μƒνƒœ λ³€κ²½ λΆˆκ°€)
    // 이 ν΄λž˜μŠ€μ—λŠ” μƒνƒœλ₯Ό λ³€κ²½ν•˜λŠ” λ©”μ„œλ“œκ°€ μ—†μŒ
    
    // 5) 이 ν΄λž˜μŠ€λŠ” 상속할 수 없도둝 final둜 선언됨
}

1) λͺ¨λ“  ν•„λ“œλ₯Ό private final둜 μ„ μ–Έ

  • ν•„λ“œ 접근을 private둜 μ œν•œν•˜μ—¬ μ™ΈλΆ€μ—μ„œ 직접 μ ‘κ·Όν•˜μ§€ λͺ»ν•˜κ²Œ ν•˜κ³ , final둜 μ„ μ–Έν•΄ μ΄ˆκΈ°ν™” ν›„ ν•„λ“œμ˜ 값을 λ³€κ²½ν•  수 없도둝 ν–ˆλ‹€.

2) μƒμ„±μžμ—μ„œ 방어적 볡사 μˆ˜ν–‰

  • μƒμ„±μžμ—μ„œ μ™ΈλΆ€μ—μ„œ μ „λ‹¬λœ Date 객체가 원본 κ·ΈλŒ€λ‘œ μ €μž₯λ˜μ§€ μ•Šλ„λ‘ 볡사본을 λ§Œλ“€μ–΄ μ €μž₯ν–ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ μ™ΈλΆ€μ—μ„œ μ „λ‹¬ν•œ Date 객체λ₯Ό μˆ˜μ •ν•˜λ”λΌλ„ Person 객체의 birthDate ν•„λ“œμ— 영ν–₯을 쀄 수 μ—†λ‹€.

3) getter λ©”μ„œλ“œμ—μ„œ 방어적 볡사 μˆ˜ν–‰

  • getBirthDate λ©”μ„œλ“œμ—μ„œλŠ” 원본 birthDate 객체 λŒ€μ‹  볡사본을 λ°˜ν™˜ν•œλ‹€. 이λ₯Ό 톡해 μ™ΈλΆ€ μ½”λ“œμ—μ„œ λ°˜ν™˜λœ Date 객체λ₯Ό λ³€κ²½ν•˜λ”λΌλ„, Person 객체의 ν•„λ“œμ—λŠ” 영ν–₯을 주지 μ•ŠλŠ”λ‹€.

4) setter λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•˜μ§€ μ•ŠμŒ

  • λ³€κ²½μž(setter) λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•˜μ§€ μ•Šμ•„μ„œ, 객체가 μƒμ„±λœ ν›„ ν•„λ“œ 값을 λ³€κ²½ν•  수 없도둝 ν–ˆλ‹€.

5) 상속 λΆˆκ°€ (final 클래슀)

  • 이 ν΄λž˜μŠ€λŠ” final둜 μ„ μ–Έλ˜μ–΄ μžˆμ–΄, 상속을 톡해 ν™•μž₯ν•  수 없도둝 ν–ˆλ‹€. 이λ₯Ό 톡해 ν•˜μœ„ ν΄λž˜μŠ€κ°€ λΆ€λͺ¨ 클래슀의 λΆˆλ³€μ„±μ„ ν•΄μΉ˜κ±°λ‚˜ μƒνƒœλ₯Ό λ³€κ²½ν•˜λŠ” 것을 λ°©μ§€ν–ˆλ‹€.

4. λΆˆλ³€ 객체의 μž₯단점

1) λΆˆλ³€ 객체의 μž₯점

  • μŠ€λ ˆλ“œ μ•ˆμ „μ„±: λΆˆλ³€ κ°μ²΄λŠ” μŠ€λ ˆλ“œ 간에 μ•ˆμ „ν•˜κ²Œ 곡유될 수 μžˆμœΌλ―€λ‘œ 동기화 μž‘μ—… 없이도 μ•ˆμ „ν•˜κ²Œ μ‚¬μš©ν•  수 μžˆλ‹€.
  • μ•ˆμ •μ„±: λΆˆλ³€ κ°μ²΄λŠ” μƒνƒœκ°€ λ³€ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ μ•ˆμ •μ μ΄κ³  예츑 κ°€λŠ₯ν•œ λ™μž‘μ„ ν•©λ‹ˆλ‹€. ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œ 자주 μ‚¬μš©ν•˜λŠ” νŒ¨ν„΄μ΄λ‹€.
  • 캐싱 및 μž¬μ‚¬μš© κ°€λŠ₯μ„±: λΆˆλ³€ κ°μ²΄λŠ” 곡유 및 μž¬μ‚¬μš©μ΄ κ°€λŠ₯ν•˜λ‹€. 예λ₯Ό λ“€μ–΄ ZERO, ONE, I와 같은 μƒμˆ˜λ₯Ό μž¬μ‚¬μš©ν•˜μ—¬ λ©”λͺ¨λ¦¬ μ‚¬μš©μ„ μ΅œμ ν™”ν•  수 μžˆλ‹€.

2) λΆˆλ³€ 객체의 단점

  • κ°’ λ³€κ²½ μ‹œ μƒˆ 객체 생성: λΆˆλ³€ κ°μ²΄λŠ” 값을 λ³€κ²½ν•˜λ €λ©΄ 항상 μƒˆλ‘œμš΄ 객체λ₯Ό 생성해야 ν•œλ‹€. 이둜 인해 μ„±λŠ₯ μ €ν•˜λ‚˜ λ©”λͺ¨λ¦¬ μ‚¬μš© 증가가 λ°œμƒν•  수 μžˆλ‹€.
    • 예λ₯Ό λ“€μ–΄, Complex κ°μ²΄μ—μ„œ λ§μ…ˆμ΄λ‚˜ λΊ„μ…ˆμ„ μˆ˜ν–‰ν•  λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄ Complex 객체가 μƒμ„±λ˜λ©°, μ›λž˜ κ°μ²΄λŠ” κ·ΈλŒ€λ‘œ λ‚¨λŠ”λ‹€.
    • 큰 κ°μ²΄λ‚˜ 닀단계 연산을 μˆ˜ν–‰ν•  λ•ŒλŠ” λΆˆλ³€ 객체의 μ„±λŠ₯ λ¬Έμ œκ°€ λ‘λ“œλŸ¬μ§ˆ 수 μžˆλ‹€. 이런 경우 κ°€λ³€ λ™λ°˜ 클래슀λ₯Ό μ‚¬μš©ν•˜κ±°λ‚˜ 닀단계 연산을 μœ„ν•œ μ΅œμ ν™”λ₯Ό μ μš©ν•  수 μžˆλ‹€.

5. 뢈편 클래슀의 νŠΉμ§•

  • λΆˆλ³€ κ°μ²΄λŠ” λ‹¨μˆœν•¨
  • λ©€ν‹° μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œλ„ 동기화할 ν•„μš”κ°€ μ—†μŒ
  • μ›μžμ„±μ„ 제곡
  • 값이 달라지면, λ³„λ„μ˜ 객체λ₯Ό λ§Œλ“€μ–΄μ•Ό ν•œλ‹€λŠ” 것
  • 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό ν†΅ν•œ μΈμŠ€ν„΄μŠ€ 캐싱

1) λΆˆλ³€ κ°μ²΄λŠ” λ‹¨μˆœν•¨

λΆˆλ³€ κ°μ²΄λŠ” μƒμ„±λœ μ‹œμ μ˜ μƒνƒœλ₯Ό 파괴될 λ•ŒκΉŒμ§€ κ·ΈλŒ€λ‘œ κ°„μ§ν•œλ‹€.

λͺ¨λ“  μƒμ„±μžκ°€ 클래슀 λΆˆλ³€μ‹ 보μž₯ν•œλ‹€λ©΄ κ·Έ 클래슀λ₯Ό μ‚¬μš©ν•˜λŠ” ν”„λ‘œκ·Έλž˜λ¨Έκ°€ λ‹€λ₯Έ λ…Έλ ₯을 듀이지 μ•Šλ”λΌλ„ μ˜μ›νžˆ λΆˆλ³€μœΌλ‘œ λ‚¨λŠ”λ‹€. 반면 κ°€λ³€ κ°μ²΄λŠ” μž„μ˜μ˜ λ³΅μž‘ν•œ μƒνƒœ 에 놓일 수 μžˆλ‹€. λ³€κ²½μž λ©”μ„œλ“œκ°€ μΌμœΌν‚€λŠ” μƒνƒœ 전이λ₯Ό μ •λ°€ν•˜κ²Œ λ¬Έμ„œλ‘œ 남겨놓지 μ•Šμ€ κ°€λ³€ ν΄λž˜μŠ€λŠ” λ―Ώκ³  μ‚¬μš©ν•˜κΈ° μ–΄λ €μšΈ μˆ˜λ„ μžˆλ‹€.

2) λ©€ν‹° μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œλ„ 동기화할 ν•„μš”κ°€ μ—†μŒ

λΆˆλ³€ κ°μ²΄λŠ” 값이 λ³€κ²½λ˜λ©΄ μƒˆλ‘œμš΄ 객체둜 λ°˜ν™˜ν•˜κΈ° λ•Œλ¬Έμ— λ©€ν‹° μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œλ„ μ•ˆμ „ν•˜λ‹€.

κ°€λ³€ 객체(private int x;)의 κ²½μš°μ—λŠ” λ©€ν‹° μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œ getter, setter μ—μ„œ race condition 1이 λ°œμƒν•  수 μžˆλ‹€.

λ°˜λ©΄μ— λΆˆλ³€κ°μ²΄(private final int x;) 의 κ²½μš°μ—λŠ” setter μ—μ„œ 항상 λ‹€λ₯Έ 객체λ₯Ό λ°˜ν™˜ν•œλ‹€.

κ·Έλž˜μ„œ λ©€ν‹° μŠ€λ ˆλ“œ ν™˜κ²½μ—μ„œλ„ μ•ˆμ „ν•˜κ²Œ 객체λ₯Ό λ‹€λ£° 수 μžˆλ‹€.

3) 값이 달라지면, λ³„λ„μ˜ 객체λ₯Ό λ§Œλ“€μ–΄μ•Ό 함

λΆˆλ³€κ°μ²΄λŠ” setter 에 μ˜ν•΄ μƒνƒœκ°€ λ³€κ²½λ˜λ©΄, μƒˆλ‘œμš΄ 객체λ₯Ό μƒμ„±ν•΄μ„œ λ°˜ν™˜νžŒλ‹€.

{% hint style="danger" %} μƒˆλ‘œμš΄ 객체 μƒμ„±μ˜ λΉ„μš©μ˜ 단점 {% endhint %}

  • 값이 변경될 λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄ 객체λ₯Ό 생성해야 ν•˜λ―€λ‘œ λ©”λͺ¨λ¦¬μ™€ μ„±λŠ₯ λΉ„μš©μ΄ 증가할 수 μžˆλ‹€.

  • 예λ₯Ό λ“€μ–΄, 큰 BigIntegerμ—μ„œ λΉ„νŠΈ ν•˜λ‚˜λ₯Ό λ°”κΎΈλ©΄ 전체λ₯Ό 볡사해야 ν•œλ‹€.

    BigInteger moby = ...;
    moby = moby.flipBit(0);

{% hint style="success" %} μ„±λŠ₯ 문제 ν•΄κ²° λ°©μ•ˆ {% endhint %}

  • 닀단계 연산을 κΈ°λ³Έ κΈ°λŠ₯으둜 μ œκ³΅ν•˜μ—¬ 쀑간에 λΆˆν•„μš”ν•œ 객체 생성을 쀄인닀.
  • κ°€λ³€ λ™λ°˜ 클래슀λ₯Ό μ œκ³΅ν•˜μ—¬ μ„±λŠ₯을 ν–₯μƒμ‹œν‚¬ 수 μžˆλ‹€.
    • 예: String ←→ StringBuilder, StringBuffer BigInteger ←→ MutableBigInteger

BigInteger 의 λ‚΄λΆ€ μ—°μ‚° κ³Όμ •μ—μ„œ 값이 μ–΄λ €λ²ˆ λ°”λ€ŒλŠ” κ²½μš°κ°€ μžˆλ‹€.

이런 κ²½μš°μ— MutableBigInteger λ₯Ό μ΄μš©ν•΄μ„œ μƒˆλ‘œμš΄ 객체λ₯Ό μƒμ„±ν•˜μ§€ μ•Šκ³  값을 λ³€κ²½ν•œ λ‹€μŒ

μ΅œμ’… κ²°κ³Όλ₯Ό λ‹€μ‹œ BigInteger 둜 λ³€ν™˜ν•˜μ—¬ λ°˜ν™˜ν•˜κΈ°λ„ ν•œλ‹€.

4) κ·Έ μ™Έ μž₯점

  1. 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό ν†΅ν•œ μΈμŠ€ν„΄μŠ€ 캐싱:
  • λΆˆλ³€ ν΄λž˜μŠ€λŠ” 자주 μ‚¬μš©λ˜λŠ” μΈμŠ€ν„΄μŠ€λ₯Ό μΊμ‹±ν•˜μ—¬ 쀑볡 생성을 λ°©μ§€ν•˜κ³  λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰κ³Ό 가비지 μ»¬λ ‰μ…˜ λΉ„μš©μ„ 쀄일 수 μžˆλ‹€.

  • 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ ν΄λΌμ΄μ–ΈνŠΈλ₯Ό μˆ˜μ •ν•˜μ§€ μ•Šκ³ λ„ 캐싱 κΈ°λŠ₯을 μΆ”κ°€ν•  수 μžˆμ–΄ μ„±λŠ₯ ν–₯상에 도움이 λœλ‹€.

  • μ˜ˆμ‹œ:

    public class Complex {
        private final double re;
        private final double im;
    
        private Complex(double re, double im) {
            this.re = re;
            this.im = im;
        }
    
        public static Complex valueOf(double re, double im) {
            return new Complex(re, im);
        }
        // λ‚˜λ¨Έμ§€ μ½”λ“œ μƒλž΅
    }
  1. 방어적 볡사 λΆˆν•„μš”:
  • λΆˆλ³€ κ°μ²΄λŠ” μƒνƒœκ°€ λ³€κ²½λ˜μ§€ μ•ŠμœΌλ―€λ‘œ 자유둭게 κ³΅μœ ν•  수 있으며, 방어적 볡사가 ν•„μš” μ—†λ‹€.
  • λ”°λΌμ„œ clone λ©”μ„œλ“œλ‚˜ 볡사 μƒμ„±μžλ₯Ό μ œκ³΅ν•˜μ§€ μ•ŠλŠ” 것이 μ’‹λ‹€.
  1. λ‚΄λΆ€ λ°μ΄ν„°μ˜ 곡유 κ°€λŠ₯:
  • λΆˆλ³€ κ°μ²΄λŠ” λ‚΄λΆ€μ μœΌλ‘œ κ°€λ³€ 객체λ₯Ό κ³΅μœ ν•  수 μžˆλ‹€.
  • 예λ₯Ό λ“€μ–΄, BigInteger의 negate λ©”μ„œλ“œλŠ” λ‚΄λΆ€ 배열을 λ³΅μ‚¬ν•˜μ§€ μ•Šκ³  원본과 κ³΅μœ ν•˜μ—¬ μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•œλ‹€.
  1. λ³΅μž‘ν•œ 객체의 λΆˆλ³€μ‹ μœ μ§€ 용이:
  • λ‹€λ₯Έ λΆˆλ³€ 객체λ₯Ό ꡬ성 μš”μ†Œλ‘œ μ‚¬μš©ν•˜λ©΄ λ³΅μž‘ν•œ 객체의 λΆˆλ³€μ‹μ„ μœ μ§€ν•˜κΈ° 쉽닀.
  • λΆˆλ³€ κ°μ²΄λŠ” 맡의 ν‚€λ‚˜ 집합(Set)의 μ›μ†Œλ‘œ μ‚¬μš©ν•˜κΈ°μ— μ ν•©ν•˜λ‹€.
  1. μ‹€νŒ¨ μ›μžμ„± 제곡:
  • λΆˆλ³€ κ°μ²΄λŠ” μƒνƒœκ°€ λ³€ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ μ˜ˆμ™Έ λ°œμƒ μ‹œμ—λ„ 객체의 μœ νš¨ν•œ μƒνƒœκ°€ μœ μ§€λœλ‹€.

5) λΆˆλ³€ 클래슀λ₯Ό λ§Œλ“œλŠ” 좔가적인 섀계 방법

  1. 상속을 κΈˆμ§€ν•˜λŠ” 방법:
    • 클래슀λ₯Ό final둜 μ„ μ–Έν•˜μ—¬ 상속을 막을 수 μžˆμ§€λ§Œ, 더 μœ μ—°ν•œ 방법은 μƒμ„±μžλ₯Ό private λ˜λŠ” νŒ¨ν‚€μ§€ μ „μš©μœΌλ‘œ λ§Œλ“€κ³  public 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•˜λŠ” 것이닀.
    • μ΄λ ‡κ²Œ ν•˜λ©΄ μ™ΈλΆ€μ—μ„œλŠ” 클래슀λ₯Ό ν™•μž₯ν•  수 μ—†μœΌλ©°, λ‚΄λΆ€μ μœΌλ‘œλŠ” μ—¬λŸ¬ κ΅¬ν˜„ 클래슀λ₯Ό ν™œμš©ν•  수 μžˆλ‹€.
  2. 객체 캐싱과 μ„±λŠ₯ ν–₯상:
    • 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ λ‚˜μ€‘μ— 객체 캐싱 κΈ°λŠ₯을 μΆ”κ°€ν•˜μ—¬ μ„±λŠ₯을 ν–₯μƒμ‹œν‚¬ 수 μžˆλ‹€.
    • λ™μΌν•œ 값을 κ°–λŠ” λΆˆλ³€ 객체λ₯Ό μΊμ‹±ν•˜μ—¬ λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ„ 쀄일 수 μžˆλ‹€.
  3. ν•„λ“œμ˜ final μ—¬λΆ€:
    • μ„±λŠ₯을 μœ„ν•΄ λͺ¨λ“  ν•„λ“œλ₯Ό final둜 μ„ μ–Έν•˜μ§€ μ•Šκ³ , 외뢀에 λ³΄μ΄λŠ” μƒνƒœλ§Œ λΆˆλ³€μœΌλ‘œ μœ μ§€ν•  수 μžˆλ‹€.
    • λ‚΄λΆ€μ μœΌλ‘œ 계산 λΉ„μš©μ΄ 큰 값을 지연 μ΄ˆκΈ°ν™”ν•˜μ—¬ 캐싱할 λ•Œ μ‚¬μš©ν•œλ‹€.

μ£Όμ˜μ‚¬ν•­

  1. 직렬화 μ‹œ 주의점:
  • Serializable을 κ΅¬ν˜„ν•˜λŠ” λΆˆλ³€ ν΄λž˜μŠ€μ—μ„œ κ°€λ³€ 객체λ₯Ό μ°Έμ‘°ν•˜λŠ” ν•„λ“œκ°€ μžˆλ‹€λ©΄ readObjectλ‚˜ readResolve λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•΄μ•Ό ν•œλ‹€.

  • readObject λ©”μ„œλ“œμ—μ„œ 방어적 볡사와 μœ νš¨μ„± 검사 μˆ˜ν–‰:

    private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
        s.defaultReadObject();
    
        // κ°€λ³€ ν•„λ“œμ— λŒ€ν•΄ 방어적 볡사 μˆ˜ν–‰
        Date startCopy = new Date(start.getTime());
        Date endCopy = new Date(end.getTime());
    
        // μœ νš¨μ„± 검사
        if (startCopy.compareTo(endCopy) > 0)
            throw new InvalidObjectException("μ‹œμž‘ λ‚ μ§œκ°€ μ’…λ£Œ λ‚ μ§œλ³΄λ‹€ μ΄ν›„μž…λ‹ˆλ‹€.");
    
        // ν•„λ“œμ— 볡사본 μ €μž₯
        this.start = startCopy;
        this.end = endCopy;
    }
    • 방어적 볡사λ₯Ό 톡해 μ™ΈλΆ€μ—μ„œ μ „λ‹¬λœ κ°€λ³€ 객체의 μ°Έμ‘°λ₯Ό 끊고, λ‚΄λΆ€μ—μ„œλ§Œ μ‚¬μš©ν•˜λŠ” 볡사본을 λ§Œλ“­λ‹ˆλ‹€.
    • μœ νš¨μ„± 검사λ₯Ό λ‹€μ‹œ μˆ˜ν–‰ν•˜μ—¬ 객체의 일관성을 ν™•μΈν•œλ‹€.
  • readResolve λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ λΆˆλ³€μ„± μœ μ§€:

    private Object readResolve() {
        // μƒˆλ‘­κ²Œ μƒμ„±λœ μ•ˆμ „ν•œ 객체λ₯Ό λ°˜ν™˜
        return new Period(start, end);
    }
    • μ—­μ§λ ¬ν™”λœ 객체λ₯Ό μƒˆλ‘œ μƒμ„±λœ 객체둜 λŒ€μ²΄ν•˜μ—¬ λΆˆλ³€μ„±μ„ μœ μ§€ν•œλ‹€.
    • λΆˆλ³€ 객체λ₯Ό μ‚¬μš©: Date λŒ€μ‹  λΆˆλ³€ 클래슀인 Instant λ˜λŠ” LocalDateTime 등을 μ‚¬μš©ν•œλ‹€.
    import java.time.Instant;
    
    public final class Period implements Serializable {
    
        private final Instant start;
        private final Instant end;
    
        public Period(Instant start, Instant end) {
            if (start.compareTo(end) > 0)
                throw new IllegalArgumentException("μ‹œμž‘ μ‹œκ°μ΄ μ’…λ£Œ μ‹œκ°λ³΄λ‹€ μ΄ν›„μž…λ‹ˆλ‹€.");
            this.start = start;
            this.end = end;
        }
    
        public Instant getStart() {
            return start;
        }
    
        public Instant getEnd() {
            return end;
        }
    }

{% hint style="danger" %} 그렇지 μ•ŠμœΌλ©΄ λ³΄μ•ˆ λ¬Έμ œκ°€ λ°œμƒν•  수 μžˆλ‹€. {% endhint %}

λ³΄μ•ˆ 문제의 핡심은 역직렬화 κ³Όμ •μ—μ„œ λΆˆλ³€ 클래슀의 λΆˆλ³€μ„±μ΄ 깨질 수 μžˆλ‹€λŠ” 점이닀. 이둜 인해 μ˜ˆμƒμΉ˜ λͺ»ν•œ 객체 μƒνƒœ λ³€κ²½μ΄λ‚˜ μ•…μ˜μ μΈ 객체 μ£Όμž…μ΄ κ°€λŠ₯ν•΄μ Έ ν”„λ‘œκ·Έλž¨μ˜ λ³΄μ•ˆκ³Ό μ•ˆμ •μ„±μ΄ μœ„ν˜‘λ°›μ„ 수 μžˆλ‹€.

λ³΄μ•ˆ λ¬Έμ œκ°€ λ°œμƒν•˜λŠ” 이유:

  1. 역직렬화 μ‹œ μƒμ„±μž 미호좜:
    • μžλ°”μ˜ 직렬화 κ³Όμ •μ—μ„œ 객체λ₯Ό 역직렬화할 λ•Œ μƒμ„±μžκ°€ ν˜ΈμΆœλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
    • λ”°λΌμ„œ μƒμ„±μžμ—μ„œ μˆ˜ν–‰ν•˜λ˜ 방어적 λ³΅μ‚¬λ‚˜ μœ νš¨μ„± 검사가 λ¬΄μ‹œλ©λ‹ˆλ‹€.
    • 이둜 인해 μ™ΈλΆ€μ—μ„œ μ‘°μž‘λœ 데이터가 객체의 λ‚΄λΆ€λ‘œ μ£Όμž…λ  수 μžˆλ‹€.
  2. κ°€λ³€ 객체의 직접 μ°Έμ‘° λ…ΈμΆœ:
    • λΆˆλ³€ 클래슀 λ‚΄λΆ€μ—μ„œ κ°€λ³€ 객체λ₯Ό μ°Έμ‘°ν•˜κ³  있으면, 역직렬화 μ‹œ κ°€λ³€ 객체에 λŒ€ν•œ 직접 μ°Έμ‘°κ°€ 외뢀에 λ…ΈμΆœλ  수 μžˆλ‹€.
    • κ³΅κ²©μžλŠ” 이 κ°€λ³€ 객체λ₯Ό μˆ˜μ •ν•˜μ—¬ λΆˆλ³€ 클래슀의 λ‚΄λΆ€ μƒνƒœλ₯Ό λ³€κ²½ν•  수 μžˆλ‹€.
  3. 역직렬화 곡격(Deserialization Attack):
    • κ³΅κ²©μžλŠ” μ§λ ¬ν™”λœ 데이터λ₯Ό μ‘°μž‘ν•˜μ—¬ μ•…μ˜μ μΈ κ°μ²΄λ‚˜ 데이터λ₯Ό μ£Όμž…ν•  수 μžˆλ‹€.
    • 이λ₯Ό 톡해 클래슀의 λΆˆλ³€μ„±μ„ 깨뜨리고, ν”„λ‘œκ·Έλž¨μ˜ λ™μž‘μ„ λ³€κ²½ν•˜κ±°λ‚˜ λ³΄μ•ˆ 취약점을 λ…ΈμΆœμ‹œν‚¬ 수 μžˆλ‹€.

정리

PhoneNumber와 Complex 같은 λ‹¨μˆœν•œ κ°’ κ°μ²΄λŠ” 항상 λΆˆλ³€μœΌλ‘œ λ§Œλ“€μžοΌˆμžλ°” ν”Œλž«νΌμ—μ„œλ„ 원 λž˜λŠ” λΆˆλ³€μ΄μ–΄μ•Ό ν–ˆμ§€λ§Œ 그렇지 μ•Šκ²Œ λ§Œλ“€μ–΄μ§„ 객체가 λͺ‡ 개 μžˆλ‹€. java.util. Date와 java.awt.Pointκ°€ κ·Έλ ‡λ‹€οΌ‰Stringκ³Ό Biginteger처 럼 무거운 κ°’ 객체도 λΆˆλ³€μœΌλ‘œ λ§Œλ“€ 수 μžˆλŠ”μ§€ 고심해야 ν•œλ‹€.

  1. Getterκ°€ μžˆλ‹€κ³  ν•΄μ„œ 무쑰건 Setterλ₯Ό λ§Œλ“€μ§€ 말자. ν΄λž˜μŠ€κ°€ κΌ­ ν•„μš”ν•œ κ²½μš°κ°€ μ•„λ‹ˆλΌλ©΄ λΆˆλ³€μž„μ„ 보μž₯ν•˜λŠ” 것이 섀계적 κ΄€μ μ—μ„œ λ°”λžŒμ§ν•˜λ‹€.
  2. λͺ¨λ“  클래슀λ₯Ό λΆˆλ³€μœΌλ‘œ λ§Œλ“€ 수 μ—†λ‹€. ν•˜μ§€λ§Œ κ°€λ³€ ν΄λž˜μŠ€λ”λΌλ„ λ³€κ²½ν•  수 μžˆλŠ” 뢀뢄은 μ΅œμ†Œν•œμœΌλ‘œ μ€„μ΄μž. κ·Έλ ‡κ²Œ 되면 객체λ₯Ό μ˜ˆμΈ‘ν•˜κΈ° μ‰¬μ›Œμ§€κ³  였λ₯˜ λ°œμƒ κ°€λŠ₯성이 쀄어든닀.(변경이 μ—†κ²Œ λ§Œλ“  ν›„ λͺ¨λ‘ final 선언을 ν•˜κ³ , νŠΉλ³„ν•œ μ΄μœ κ°€ μ—†λ‹€λ©΄ λͺ¨λ‘ private final 선언을 ν•˜μž.)
  3. μƒμ„±μžλŠ” λΆˆλ³€μ‹ 섀정이 λͺ¨λ‘ μ™„λ£Œλœ μƒνƒœμ˜ 객체λ₯Ό 생성해야 ν•œλ‹€. 즉, ν™•μ‹€ν•œ μ΄μœ κ°€ μ—†λ‹€λ©΄ μƒμ„±μžμ™€ 정적 νŒ©ν„°λ¦¬ μ™Έμ—λŠ” κ·Έ μ–΄λ–€ μ΄ˆκΈ°ν™” λ©”μ„œλ“œλ„ public으둜 μ œκ³΅ν•΄μ„œλŠ” μ•ˆλœλ‹€. 객체λ₯Ό μž¬μ‚¬μš©ν•  λͺ©μ μœΌλ‘œ μƒνƒœλ₯Ό λ‹€μ‹œ μ΄ˆκΈ°ν™”ν•˜λŠ” λ©”μ„œλ“œλ„ μ˜¬λ°”λ₯΄μ§€ μ•Šλ‹€.
  4. μ„±λŠ₯μƒμ˜ 이유둜 λΆˆλ³€ 클래슀λ₯Ό μ‚¬μš©ν•  수 μ—†λ‹€λ©΄, λΆˆλ³€ ν΄λž˜μŠ€μ™€ μŒμ„ μ΄λ£¨λŠ” κ°€λ³€ λ™λ°˜ 클래슀λ₯Ό μ œκ³΅ν•˜λŠ” 것을 고렀해라.

μ°Έκ³  κΈ€ : https://ttl-blog.tistory.com/1205#%F0%9F%A7%90%20%EB%B6%88%EB%B3%80%20%EA%B0%9D%EC%B2%B4%20%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0-1

이미지 좜처 : https://jwkim96.tistory.com/302

Footnotes

  1. 레이슀 μ»¨λ””μ…˜(Race Condition)μ΄λž€ λ¬΄μ—‡μΈκ°€μš”?

    레이슀 μ»¨λ””μ…˜μ€ λ©€ν‹°μŠ€λ ˆλ“œ λ˜λŠ” λ©€ν‹°ν”„λ‘œμ„ΈμŠ€ ν™˜κ²½μ—μ„œ 두 개 μ΄μƒμ˜ μŠ€λ ˆλ“œλ‚˜ ν”„λ‘œμ„ΈμŠ€κ°€ λ™μ‹œμ— 곡유 μžμ›μ— μ ‘κ·Όν•˜κ±°λ‚˜ μ‘°μž‘ν•˜λ €κ³  ν•  λ•Œ λ°œμƒν•˜λŠ” 문제λ₯Ό λ§ν•©λ‹ˆλ‹€. 이둜 인해 ν”„λ‘œκ·Έλž¨μ˜ μ‹€ν–‰ κ²°κ³Όκ°€ μ˜λ„μΉ˜ μ•Šκ²Œ λ‹¬λΌμ§€κ±°λ‚˜ 예기치 μ•Šμ€ 버그가 λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

    μ™œ λ°œμƒν•˜λ‚˜μš”?

    • 동기화 λΆ€μ‘±: 곡유 μžμ›μ— λŒ€ν•œ 접근이 적절히 λ™κΈ°ν™”λ˜μ§€ μ•ŠμœΌλ©΄, μ—¬λŸ¬ μŠ€λ ˆλ“œκ°€ λ™μ‹œμ— μžμ›μ— μ ‘κ·Όν•˜μ—¬ μƒνƒœλ₯Ό λ³€κ²½ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • 비결정적 μ‹€ν–‰ μˆœμ„œ: μŠ€λ ˆλ“œμ˜ μ‹€ν–‰ μˆœμ„œλŠ” 운영체제의 μŠ€μΌ€μ€„λ§μ— 따라 κ²°μ •λ˜λ©°, μ˜ˆμΈ‘ν•  수 μ—†μŠ΅λ‹ˆλ‹€. 이둜 인해 μ‹€ν–‰ κ²°κ³Όκ°€ 맀번 λ‹¬λΌμ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.
    ↩