Java programming

μžλ°” final ν‚€μ›Œλ“œ (feat. String 클래슀)

ν”„λ‘œκ·Έλž˜λ¨Έ μ˜€μ›” 2024. 7. 30.

final  ν‚€μ›Œλ“œλŠ” λΆˆλ³€μ„±μ„ λœ»ν•œλ‹€.

이 final ν‚€μ›Œλ“œλŠ” μ–΄λ–€ 곳에 μ‚¬μš©λ˜λƒμ— λΆˆλ³€μ˜ νŠΉμ§• 쀑 μ‘°κΈˆμ”© 살짝 λ‹€λ₯Έ 의미λ₯Ό κ°€μ§„λ‹€. final ν‚€μ›Œλ“œλŠ” λ³€μˆ˜(variable), λ©”μ†Œλ“œ(method), λ˜λŠ” 클래슀(class)에 μ‚¬μš©λ  수 μžˆλ‹€.  

 

πŸ“final Class

final ν‚€μ›Œλ“œλ₯Ό ν΄λž˜μŠ€μ— 뢙이면 상속 λΆˆκ°€λŠ₯ ν΄λž˜μŠ€κ°€ λœλ‹€. 즉, λ‹€λ₯Έ ν΄λž˜μŠ€μ—μ„œ μƒμ†ν•˜μ—¬ μž¬μ •μ˜λ₯Ό ν•  수 μ—†λŠ” 것이닀.

μ΄λŠ” 클래슀λ₯Ό λΆˆλ³€μœΌλ‘œ λ§Œλ“€μ–΄μ„œ 상속에 μ˜ν•œ 변경을 λ°©μ§€ν•œλ‹€. λŒ€ν‘œμ μΈ 클래슀둜 Integer와 같은 랩퍼(Wrapper) 클래슀, String ν΄λž˜μŠ€κ°€ μžˆλ‹€. 클래슀 μ„€κ³„μ‹œ μž¬μ •μ˜ μ—¬λΆ€λ₯Ό μƒκ°ν•΄μ„œ μž¬μ •μ˜ λΆˆκ°€λŠ₯ν•˜κ²Œ μ‚¬μš©ν•˜κ³  μ‹Άλ‹€λ©΄ final둜 λ“±λ‘ν•˜λŠ”κ²Œ μΆ”ν›„ μœ μ§€λ³΄μˆ˜μ°¨μ›μ—μ„œ μ’‹λ‹€.

 

πŸ“final Method

λ©”μ„œλ“œμ— final을 뢙이면 μž¬μ •μ˜(override)λ₯Ό μ œν•œν•˜κ²Œ λœλ‹€. 클래슀λ₯Ό μƒμ†ν•˜κ²Œ 되면 μš°λ¦¬λŠ” ν•΄λ‹Ή 클래슀의 protected, public의 μ ‘κ·Ό μ œμ–΄μž(access-modifier)λ₯Ό κ°€μ§„ λ©”μ„œλ“œλ₯Ό μž¬μ •μ˜(override) 톡해 μž¬κ΅¬ν˜„μ„ ν•  수 μžˆλ‹€. 즉 변경이 κ°€λŠ₯ν•˜λŠ” 것이닀.

ν•˜μ§€λ§Œ final ν‚€μ›Œλ“œλ₯Ό λΆ™μ—¬ λ©”μ†Œλ“œλ₯Ό μ •μ˜ν•œλ‹€λ©΄ μ„œλΈŒ ν΄λž˜μŠ€μ—μ„œ μž¬μ •μ˜λ₯Ό μ œν•œν•˜κ²Œ λΌ λΆˆλ³€μ„±μ„ μ§€ν‚€κ²Œ λœλ‹€.

 

πŸ“final Variable

λ³€μˆ˜μ— final을 λΆ™μ΄λ©΄ μ΄ λ³€μˆ˜λŠ” μˆ˜μ •ν•  μˆ˜ μ—†λ‹€λŠ” λœ»μ΄ λœλ‹€. μˆ˜μ •될 μˆ˜ μ—†κΈ° λ•Œλ¬Έμ— μ΄ˆκΈ°ν™” κ°’은 ν•„μˆ˜μ μ΄λ‹€.

μˆ˜μ • ν•  수 μ—†λ‹€λŠ” λ²”μœ„λŠ” κ·Έ λ³€μˆ˜μ˜ 값에 ν•œμ •λœλ‹€. 즉, λ‹€λ₯Έ 객체λ₯Ό μ°Έμ‘°ν•˜κ±°λ‚˜ ν•  λ•Œ μ°Έμ‘°ν•˜λŠ” 객체의 λ‚΄λΆ€μ˜ 값은 λ³€κ²½ν•  수 μžˆλ‹€λΌλŠ” μ˜λ―Έμ΄λ‹€.

μ•„λž˜ μ˜ˆμ‹œμ—μ„œ pizza λ³€μˆ˜λ₯Ό μƒˆλ‘œμš΄ Pizza 객체둜 ν• λ‹Ήν•˜λŠ” 것은 λΆˆλ³€μ„±μ„ μ–΄κΈ°λ―€λ‘œ  컴파일 μ—λŸ¬κ°€ μƒκΈ°μ§€λ§Œ, Pizza 객체 μ•ˆμ˜ ν•„λ“œλ“€μ„ λ³€κ²½ν•˜λŠ” 것은 κ°€λŠ₯ν•˜λ‹€.

class Solution19 {
    public static void main(String[] args) {
        final Pizza pizza = new Pizza(12000, "뢈고기 ν”Όμž");

        //pizza = new Pizza(20000, "ν•˜μ™€μ΄μ–Έ ν”Όμž"); 컴파일 μ—λŸ¬ - 값이 직접 변함

        pizza.setPrice(1000);  //μ°Έμ‘°ν•˜λŠ” 객체의 λ‚΄λΆ€μ˜ 값은 λ³€κ²½ν•  수 μžˆλ‹€
        pizza.setName("ν•˜μ™€μ΄μ–Έ ν”Όμž");
    }
}
class Pizza {
    int price;
    String name;

    //μƒμ„±μžμ™€ setter μƒλž΅
}

 

final μ§€μ—­ λ³€μˆ˜

final ν‚€μ›Œλ“œκ°€ λΆ™μ€ λ³€μˆ˜λŠ” μ΄ˆκΈ°ν™”λ₯Ό ν•΄μ€˜μ•Όν•˜λŠ”λ°, μ—¬λŸ¬κ°€μ§€ λ°©λ²•이 μžˆλ‹€.

1. μ„ μ–Έμ‹œ μ΄ˆκΈ°ν™” 
2. μ‚¬μš© μ „에 μ΄ˆκΈ°ν™”

 

final μΈμŠ€ν„΄μŠ€ λ³€μˆ˜

1. μ„ μ–Έμ‹œ μ΄ˆκΈ°ν™”

2. μƒμ„±μžλ₯Ό μ΄μš©ν•œ μ΄ˆκΈ°ν™”

3. μ΄ˆκΈ°ν™” block을 μ΄μš©ν•œ μ΄ˆκΈ°ν™”

 

μ•žμ„  String ν΄λž˜μŠ€μ—μ„œλ„ final ν•„λ“œλ₯Ό κ°–λŠ”λ°, λͺ¨λ‘ μƒμ„±μžμ—μ„œ μ΄ˆκΈ°ν™” μ‹œμΌœμ£Όκ³  μžˆλ‹€.

 

 

πŸ”  String 클래슀

String ν΄λž˜μŠ€λŠ” μ•žμ„œ λ§ν–ˆλ‹€μ‹œν”Ό λΆˆλ³€ ν΄λž˜μŠ€μ΄λ‹€. μžλ°”μ—μ„œ λ¬Έμžμ—΄(String)은 λΆˆλ³€(immutable) 객체이닀. μ΄λŠ” ν•œ 번 μƒμ„±λœ λ¬Έμžμ—΄ 객체의 λ‚΄μš©μ€ λ³€κ²½ν•  수 μ—†λ‹€λŠ” 것을 μ˜λ―Έν•œλ‹€. λ¬Έμžμ—΄μ„ λ³€κ²½ν•˜λŠ” μž‘μ—…(예: λ¬Έμžμ—΄ μ—°κ²°)은 항상 μƒˆλ‘œμš΄ λ¬Έμžμ—΄ 객체λ₯Ό μƒμ„±ν•˜κ²Œ λœλ‹€. μ΄λž˜μ„œ λ¬Έμžμ—΄ μˆ˜μ •μ€ 쑰심해야 ν•œλ‹€.

예λ₯Ό λ“€μ–΄ λ‹€μŒκ³Ό 같은 상황이 μžˆλ‹€.

public static void main(String[] args) {
    String str = "string";
    for (int i = 0; i < 10; i++) {
        str = str + i;
    }
}

 

λ¬Έμžμ—΄ λ³€κ²½ κ³Όμ •

  1. 초기 μƒνƒœ
    • str은 "string"을 μ°Έμ‘°ν•œλ‹€.
  2. 첫 번째 반볡 (i = 0)
    • str + iλŠ” μƒˆλ‘œμš΄ λ¬Έμžμ—΄ "string0"을 μƒμ„±ν•œλ‹€.
    • str은 이제 "string0"을 μ°Έμ‘°ν•œλ‹€.
  3. 두 번째 반볡 (i = 1)
    • str + iλŠ” μƒˆλ‘œμš΄ λ¬Έμžμ—΄ "string01"을 μƒμ„±ν•œλ‹€.
    • str은 이제 "string01"을 μ°Έμ‘°ν•œλ‹€.

이 과정은 루프가 끝날 λ•ŒκΉŒμ§€ κ³„μ†λœλ‹€.

 

λ©”λͺ¨λ¦¬ ν• λ‹Ή μ˜μ—­

  1. νž™ μ˜μ—­ (Heap)
    • λ°˜λ³΅λ¬Έμ—μ„œ μƒμ„±λ˜λŠ” λͺ¨λ“  λ¬Έμžμ—΄ κ°μ²΄λŠ” νž™ μ˜μ—­μ— ν• λ‹Ήλœλ‹€. μƒˆλ‘œμš΄ λ¬Έμžμ—΄ 객체가 생성될 λ•Œλ§ˆλ‹€ νž™ λ©”λͺ¨λ¦¬λ₯Ό μ‚¬μš©ν•˜κ²Œ λœλ‹€.
    • 예λ₯Ό λ“€μ–΄, "string0", "string01" λ“± λͺ¨λ“  λ¬Έμžμ—΄ κ°μ²΄λŠ” νž™μ— μ €μž₯λœλ‹€.
  2. μŠ€νƒ μ˜μ—­ (Stack)
    • 둜컬 λ³€μˆ˜ strλŠ” μŠ€νƒμ— ν• λ‹Ήλœλ‹€.
    • str은 μŠ€νƒμ— μ €μž₯된 μ°Έμ‘° λ³€μˆ˜λ‘œ, νž™μ— μžˆλŠ” μ‹€μ œ λ¬Έμžμ—΄ 객체λ₯Ό μ°Έμ‘°ν•œλ‹€.
  3. λ©”μ†Œλ“œ μ˜μ—­ (Method Area)
    • λ©”μ†Œλ“œ μ˜μ—­μ€ 클래슀 ꡬ쑰(메타데이터), 정적 ν•„λ“œ, 정적 λ©”μ„œλ“œλ₯Ό ν¬ν•¨ν•œλ‹€. strκ³Ό 같은 λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄μ€ μƒμˆ˜ 풀에 μ €μž₯될 수 μžˆλ‹€.
    • λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄ "string"은 μƒμˆ˜ ν’€(constant pool)에 μ €μž₯λ˜μ§€λ§Œ, 루프 λ‚΄μ—μ„œ μƒμ„±λœ μƒˆλ‘œμš΄ λ¬Έμžμ—΄μ€ νž™ μ˜μ—­μ— μ €μž₯ λœλ‹€.

즉, 처음 "string" λ¦¬ν„°λŸ΄μ€ μƒμˆ˜ 풀에 μ €μž₯ λœλ‹€. 그리고 반볡문이 μ‹œμž‘λ˜κ³  λ‚˜μ„œ 각 λ°˜λ³΅λ§ˆλ‹€ μƒˆλ‘œμš΄ λ¬Έμžμ—΄ 객체("string0", "string01", ..., "string0123456789")κ°€ νž™μ— 생성 λœλ‹€.

str λ³€μˆ˜λŠ” μŠ€νƒμ— μ €μž₯λ˜μ–΄ 각 λ°˜λ³΅λ§ˆλ‹€ νž™μ˜ μƒˆλ‘œμš΄ λ¬Έμžμ—΄ 객체λ₯Ό μ°Έμ‘°ν•œλ‹€.

 

이유 - 내뢀적 λ™μž‘ 원리

μœ„ μ½”λ“œμ—μ„œ str = str + iλŠ” 각 λ°˜λ³΅λ§ˆλ‹€ μƒˆλ‘œμš΄ String 객체λ₯Ό μƒμ„±ν•œλ‹€. μ΄λŠ” 각 μ—°κ²° κ²°κ³Όκ°€ μƒˆλ‘œμš΄ λ¬Έμžμ—΄ 객체λ₯Ό μƒμ„±ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

μžλ°”μ—μ„œ λ¬Έμžμ—΄ μ—°κ²° μ—°μ‚°μž(+)λŠ” λ‚΄λΆ€μ μœΌλ‘œ StringBuilderλ₯Ό μ‚¬μš©ν•˜μ—¬ λ¬Έμžμ—΄μ„ μ—°κ²°ν•œ ν›„, μ΅œμ’… κ²°κ³Όλ₯Ό String 객체둜 λ³€ν™˜ν•œλ‹€. κ²°κ΅­  이 객체듀은 λͺ¨λ‘ νž™ λ©”λͺ¨λ¦¬μ— ν• λ‹Ήλœλ‹€. 

μœ„ μ½”λ“œλŠ” λ‚΄λΆ€μ μœΌλ‘œ μ•„λž˜μ™€ 같이 λŒμ•„κ°„λ‹€.

String str = "string";
for (int i = 0; i < 10; i++) {
    StringBuilder sb = new StringBuilder(str);
    sb.append(i);
    str = sb.toString();
}

 

  • StringBuilder μ‚¬μš©:
    • λ¬Έμžμ—΄ μ—°κ²° μ‹œ StringBuilder 객체가 μƒμ„±λ˜κ³ , 각 λΆ€λΆ„ λ¬Έμžμ—΄μ΄ StringBuilder에 μΆ”κ°€λœλ‹€.
  • toString() 호좜:
    • μ΅œμ’… μ—°κ²°λœ λ¬Έμžμ—΄μ€ StringBuilder의 toString() λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ String 객체둜 λ³€ν™˜λœλ‹€. 이 λ³€ν™˜λœ String κ°μ²΄λŠ” νž™ λ©”λͺ¨λ¦¬μ— ν• λ‹Ήλœλ‹€.

 

 

μœ„μ™€ 같은 이유둜 μƒˆλ‘œμš΄ 객체가 νž™μ˜μ—­μ— 계속 μƒκΈ°κ²Œ λ˜λŠ” 것이닀. λ¬Έμžμ—΄μ„ λ‹€λ£¨λŠ” μ½”λ”©ν…ŒμŠ€νŠΈ λ¬Έμ œμ—μ„œ λ©”λͺ¨λ¦¬ 초과 λ¬Έμ œκ°€ λ‚˜νƒ€λ‚˜λŠ” μ΄μœ λ„ μ΄λŸ¬ν•œ 이유 λ•Œλ¬Έμ΄λ‹€. λ”°λΌμ„œ λ¬Έμžμ—΄μ„ λ‹€λ£° λ•ŒλŠ” μž¬μ„ μ–Έ ν•˜λŠ” 것이 μ•„λ‹Œ, StringBuilder 클래슀λ₯Ό μ΄μš©ν•˜μ—¬ λ‹€λ£¨λŠ” 것이 λ©”λͺ¨λ¦¬λ₯Ό 아끼고 μ„±λŠ₯λ©΄μ—μ„œλ„ μ’‹λ‹€.

	StringBuilder str = new StringBuilder("string");
        for (int i = 0; i < 10; i++) {
            str.append(i);
        }

 

λŒ“κΈ€