멋쟁이사자처럼_부트캠프/Java

[멋쟁이사자처럼 부트캠프 TIL 회고] 백엔드 부트캠프 13기: Java 7일차 헷갈릴수 있는 코드 정리, 클래스 내부 필드와 문자열 처리 방식의 차이점, String

모건_Morgan 2024. 12. 11. 01:06

헷갈릴수 있는 코드 정리.

package day07;

public class Pen {
    protected String color = "빨강";
    public void write(){
        System.out.println("Pen이 씁니다.");
    }
    public void write(String msg){

    }
    public void write(String msg, int count){

    }

    @Override
    public String toString() {
        return "나는 펜이예요.";
    }

    public void print(){
        System.out.println("a");
        System.out.println("b");
        System.out.println("c");
        System.out.println("d");
    }
}
package day07;

public class BallPen extends Pen{
    public int BallPen(){ //리턴타입을 가지면 메소드로 인지한다.  생성자 아님!!!
        return 0;
    }

    public BallPen(){   //생성자로 인지함.

    }
    String color = "검정";
    //부모가 가진 메소드, 필드를 자식이 똑같이 다시 정의 하는 것!!  오버라이드이다.
    public void write(){
        //부모가 이미 구현한 write() 메소드에서

        System.out.println("BallPen이 씁니다. 색:"+color);
    }

    public void print(){
        //생성자는 객체가 생성될때 한 번 불려쓰이기때문에
        //생성자에서만 호출이 가능하다.
//        super();
//        this();

//        System.out.println("a");
//        System.out.println("b");
//        System.out.println("c");
//        System.out.println("d");

        super.print();
        System.out.println("e");
        System.out.println(super.color); //접근제한자가 가능하면 접근가능
    }

    public static void main(String[] args) {
//        BallPen ballPen = new BallPen();
        BallPen ballPen = new BallPen();
        System.out.println(ballPen.color);
        ballPen.write();
        ballPen.write("test");
        ballPen.write("test",4);

        ballPen.print();

    }
}

 

1. 코드의 구조

클래스 정의

  1. BallPen extends Pen
    • BallPen은 Pen 클래스를 상속받음.
    • 부모 클래스(Pen)의 필드와 메서드를 자식 클래스(BallPen)에서 사용할 수 있음.
  2. 생성자
    • BallPen 클래스에는 두 개의 생성자가 작성됨:
      • public int BallPen(): 리턴 타입이 있으므로 생성자가 아닌 메서드로 인식됨.
      • public BallPen(): 생성자로 인식됨. 객체 생성 시 실행됨.
  3. 메서드
    • write(): 부모 클래스의 메서드를 오버라이딩함.
      • System.out.println("BallPen이 씁니다. 색:" + color); 출력.
    • print(): 부모 클래스의 print() 메서드를 호출(super.print())한 후, 추가적으로 동작 수행.

필드

  • color: 자식 클래스 BallPen에서 정의됨. 기본값은 "검정".

2. 주요 코드 요소 정리

생성자와 메서드의 차이

  • public int BallPen():
    리턴 타입이 int이므로, 생성자가 아닌 일반 메서드로 인식됨.
  • public BallPen():
    리턴 타입이 없고 클래스 이름과 같으므로 생성자로 인식됨.

오버라이딩

  • write() 메서드:
    부모 클래스의 메서드와 동일한 이름과 시그니처를 가지며, 재정의된 메서드.
    • 부모 메서드 대신 자식 클래스의 write() 메서드가 호출됨.

super 키워드

  • super.print():
    부모 클래스의 print() 메서드를 호출함.
  • super.color:
    부모 클래스의 color 필드에 접근(단, 접근 제한자가 허용해야 함).

 

3. 출력 결과

코드 실행 흐름

1. 객체 생성

 
BallPen ballPen = new BallPen();
  • BallPen의 생성자가 호출됨.

2. 필드 출력

System.out.println(ballPen.color);
 
  • color 필드는 자식 클래스 BallPen의 필드값 "검정"을 출력.

3. 메서드 호출

  • write() 호출:
    BallPen이 씁니다. 색:검정
  • print() 호출:
    부모 클래스의 print() 메서드를 호출한 후, 추가적으로 "e"

예상 출력

검정
BallPen이 씁니다. 색:검정
e
부모 클래스에서 정의된 print() 내용
super.color의 값 출력 (부모 클래스의 color 값)

※ 정확한 출력은 부모 클래스 Pen의 print() 메서드 구현에 따라 달라짐.


4. 코드 정리 후 내용

이 코드는 상속, 오버라이딩, 생성자, super 키워드 등 객체지향의 주요 개념을 다룸.
특히 생성자와 메서드의 차이, 필드 접근 시 부모와 자식의 구분 등 중요한 점을 포함하고 있음.

 

 


클래스 내부 필드와 문자열 처리 방식의 차이점(복습)

package day07;

public class FieldEntity {

    private String name;

    public String concat(String word){
        name = name + word;
        return name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public static void main(String[] args) {
        FieldEntity c1 = new FieldEntity();
        c1.setName("Kim");

        System.out.println(c1.getName());

        c1.concat(" Dong-gyu");

        System.out.println(c1.getName());

        String str1 = "Kim";

        str1 = str1.concat(" Dong-gyu");

        System.out.println(str1);
    }

}

 

코드의 주요 설명

1. FieldEntity의 구조

  • name 필드: private 접근 제한자를 사용하여 외부에서 직접 접근하지 못하도록 설정.
  • setName과 getName 메서드: name 필드의 값을 설정(set)하거나 가져오는(get) 데 사용.
  • concat 메서드: name 필드에 입력받은 문자열(word)을 이어붙이고, 이어붙인 결과를 반환.

2. main 메서드 설명

  • 객체 필드와 메서드 사용:
    1. FieldEntity 객체 c1 생성.
    2. setName을 통해 name 필드에 "Kim"을 설정.
    3. concat 메서드를 호출하여 name 필드 값에 " Dong-gyu"를 추가.
    4. 결과적으로 name 필드는 "Kim  Dong-gyu"로 변경됨.
  • String의 불변성(Immutability):
    • String 객체 str1에 " Kim"을 저장.
    • str1.concat(" Dong-gyu")는 새로운 문자열 객체 "Kim Dong-gyu"를 생성.
    • str1 변수는 새로운 문자열 객체를 참조하게 됨.
    • 기존 문자열 Kim은 변경되지 않음.

코드 설명의 핵심

  1. 객체 필드 수정과 문자열 처리:
    • FieldEntity의 concat 메서드는 name 필드의 값을 직접 변경.
    • String 클래스의 concat 메서드는 새로운 문자열 객체를 반환할 뿐, 기존 문자열은 변경되지 않음.
  2. String 클래스의 불변성:
    • String은 자바에서 불변(Immutable) 객체임. 문자열을 수정하려면 새로운 객체를 생성.
  3. 객체 필드와 지역 변수의 차이:
    • FieldEntity 는 name 필드를 객체의 속성으로 유지하고 관리.
    • String은 지역 변수 str1을 통해 값이 변하는 것처럼 보이지만, 실제로는 새로운 객체를 생성해 참조.

실행 결과 설명

  1. 첫 번째 출력: setName("Kim") 호출 후 name 필드 값을 출력.
  2. 두 번째 출력: concat(" Dong-gyu") 호출 후 name 필드 값 변경된 결과 출력.
  3. 세 번째 출력: String.concat("Kim") 호출 후 새로운 문자열 반환, 기존 str1 값은 변하지 않음.

 

 


String

  • String은 조금 독특한 객체다. 메모리상에서 문자열이 너무 많이 생성되므로, 더 효율적으로 관리할 방법을 제공한다.

1. String 클래스가 final인 이유

String 클래스는 불변성(Immutable)을 유지하도록 설계되어 있음. final로 선언된 이유는 다음과 같음:

1.1 불변성 유지

  • String은 한 번 생성되면 값이 변경되지 않음.
  • 불변성을 통해 동기화 문제를 피할 수 있음:
    • 멀티스레드 환경에서도 안전하게 사용할 수 있음.

1.2 보안(Security)

  • 암호 키, 네트워크 주소 등 중요 데이터가 String으로 자주 처리됨.
  • String이 불변하지 않으면, 데이터 조작 및 보안 문제가 발생할 가능성이 있음.

1.3 해싱(Hashing) 최적화

  • String은 equals와 hashCode를 오버라이딩하여 효율적인 해싱을 지원.
  • 불변성을 유지함으로써 캐시(HashMap, Set 등)에서 안전하고 빠른 검색 가능.

1.4 리터럴 풀(String Literal Pool) 활용

  • 같은 문자열 값은 메모리에서 공유됨.
  • 새로운 객체를 생성하지 않고 기존 객체를 재사용하여 메모리 효율성 증가.

2. StringBuffer 클래스

StringBuffer는 가변(Mutable) 객체로, 문자열을 동적으로 변경 가능함.

2.1 특징

  • 문자열 내용을 수정하거나 추가할 수 있음.
  • 불변성을 가지지 않으며, 내부적으로 배열을 사용해 문자열을 저장.
  • 스레드에 안전함: 모든 메서드가 synchronized로 구현되어 있음.

2.2 주요 메서드

  • append(String s): 문자열 뒤에 새로운 문자열 추가.
  • insert(int offset, String s): 특정 위치에 문자열 삽입.
  • replace(int start, int end, String s): 문자열 일부를 교체.
  • reverse(): 문자열을 뒤집음.

2.3 사용 예시

public class StringBufferExample {
    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("Hello");
        sb.append(" World");
        System.out.println(sb); // Hello World

        sb.insert(6, "Java ");
        System.out.println(sb); // Hello Java World

        sb.replace(6, 10, "Kotlin");
        System.out.println(sb); // Hello Kotlin World

        sb.reverse();
        System.out.println(sb); // dlroW niltoK olleH
    }
}

 

3. StringStringBuffer 비교

특성 String StringBuffer
가변성 불변 (Immutable) 가변 (Mutable)
스레드 안전성 안전하지 않음 안전 (synchronized 사용)
성능 문자열 수정 시 새로운 객체 생성 문자열 수정 시 기존 객체에서 처리
사용 목적 변경이 거의 없는 문자열 문자열을 자주 수정해야 하는 경우

 

결론

  • String: 값이 변경되지 않는 문자열이 필요하거나, 메모리 효율을 위해 리터럴 풀을 활용하는 경우 사용.
  • StringBuffer: 멀티스레드 환경에서 문자열을 자주 수정해야 할 때 사용.