티스토리 뷰
포스팅에 앞서 내용이 틀릴 수 있습니다.
해당 부분 지적 감사히 받습니다.
이번에는 String class 2부다.
String class1부에서 2부로 바로 넘어오지 않고 중간에 불변 객체라는 것에 대해 먼저 배웠다.
그렇다면 왜 불변객체를 먼저 배웠을까?
바로 String Class가 불변객체이기 때문이다.
String 이 한번 선언되면 내부의 값을 변경할 수 없고, 새로운 String 객체를 생성해서 값을 넣어줘야 한다.
그렇다면 String은 왜 불변객체로 만들어졌을까?
바로 String Pool의 존재가 그 이유이다.
String Pool의 공간에서 특정 참조값의 value가 바뀌어 버리면, 이를 참조하는 나머지 부분들도 사이드 이펙트가 생기기 때문이다.
String Pool : 문자열이 저장된 공간
공간자체는 힙영역에 위치해 있다.
String Pool은 코드가 컴파일될 때, 리터럴로 선언된 String의 값들을 자바가 String Pool이라는 곳에 넣어둔다.
그리고 해당 문자열에 접근할 때, String Pool에서 일치하는 문자열이 있으면 해당 참조값을 반환해 준다.
동일성 비교를 통해 이를 증명해 볼 수 있다.
확인해 보자
코드
String str1 = "HELLO";
String str2 = "HELLO";
System.out.println("동일성 = " + (str1 == str2));
결과

str1과 str2를 리터럴로 선언했기에, 자바에서는 이를 String Pool에 관리하여 메모리 효율을 올렸다.
추가적으로 이 String Pool에 접근하여 값을 가져올 때는 해쉬 알고리즘을 통해 가져온다. (추후 설명 예정)
그렇다면 리터럴의 비교에서는 앞으로 == 만 사용하여도 되는 걸까?
그래도 equals() 쓰자 메서드는 뭘 매개변수로 받을지 모른다.
String은 불변 객체라고 하였다.
신규 문자열 변환 시, 메서드를 사용할 때 신규 객체 참조를 반환하여 사용하여야 한다.
아래 코드 예시를 보자
String str1 = "HELLO";
String str2 = "HELLO";
str1.concat(str2);
System.out.println("결과 = " + str1);
사진 잘라 넣는 거보다 이게 훨씬 편하네
결과 = HELLO
마찬가지로 String 객체 내부에는 여러 가지 메서드가 오버라이딩 되어있는데 이는 모두 객체의 참조값을 반환한다.
위와 같은 경우 반환된 참조값을 사용하지 않아 아마 GC 당할 것이다.
제대로 concat()을 사용하고 싶으면 아래처럼 사용하면 된다.
String str3 = str1.concat(str2);
System.out.println("결과 = " + str3);
결과
결과 = HELLOHELLO
근데 여기서 String 문자열의 조합이 길어질 때 String Class는 굉장히 불편하고 비효율 적이라는 생각이 들어야 한다.
객체를 만들었다가 GC 하고 하는 거보단 그래도 그냥 하나의 참조에서 값 수정하고 싶은데 어떻게 못하나..?
있다 그런 거
StringBuilder
얘는 가변형이다.
즉 객체를 생성하지 않고 String의 값을 수정할 수 있다.
StringBuilder는 내부에 byte []를 통해 값을 저장한다
그리고 append() 메서드를 사용할 수 있다.
코드
StringBuilder sb =new StringBuilder("사과는");
sb.append("맛있어");
System.out.println("결과 = " + sb);
결과
결과 = 사과는맛있어
굿
자바 컴파일러는 String에 진심이다.
문자열 리터럴을 더하는 부분을 자동으로 합쳐준다.
String string = "Hello" + "Myfiend" + "monkey";
String string2 = new StringBuilder("Hello").append("Myfiend").append("monkey").toString();
string을 string2로 자바에서 자동으로 최적화해 준다.
다만 루프절에서의 문자열 리터럴 + 계산은 최적화가 되지 않는데, 이는 각 계산이 순차적으로 일어나기 때문에 컴파일러가 어떻게 최적화해야 더 빠른지 모르기 때문이다.
그런데 여기서 조금 생소해 보이는 코드가 나왔다.
바로 이 부분이다.
String string2 = new StringBuilder("Hello").append("Myfiend").append("monkey").toString();
이는 메서드 체이닝(Method Chaining)이라고 불리는 방법인데, StringBuilder 내부의 append() 메서드에서 반환을 자기 자신의 참조값을 반환하게 하였다. ( return this; )
조금 더 풀어서 설명해 보면 처음 new StringBuilder("Hello")가 생성이 되며 참조값을 반환한다.
궁금증 : 아니 생성자는 반환값이 없다며..
답 : 자바에서는 생성자에 반환값이 없는것은 맞지만, 생성이 완료되면 해당 객체의 참조가 호출된 곳으로 전달된다.
머 그래서 참조값 반환 되었고 그렇다면 위 코드는 아래와 같은 형태로 변하게 된다.
String string2 = 참조값.append("Myfiend").append("monkey").toString();
이제는 참조값의 메서드 append("Myfriend")가 실행될 차례이다.
public StringBuilder append(String str) {
super.append(str);
return this;
}
이건 자바에서 기본으로 제공하는 StringBuilder 클래스의 append() 메서드이다.
여기에서 return this; 를 반환하는 것을 볼 수 있다.
따라서 계속 문자열을 추가하고 자기 자신의 참조값을 반환하게 된다.
이렇게 꼬리에 꼬리를 물게끔 메서드를 설계하는 것이 메서드 체이닝이라고 한다.
메서드 체이닝을 사용하는 이유는 바로 코드를 간결하고 읽기 쉽게 만들어 주기 때문이다.
추후 유지보수에 장점이 있겠다.
추가적으로 상속받은 부모 자식 클래스에서, 자식 객체 생성 후 자식을 통해 부모로 접근,
값 수정 후 자식 참조값 반환해도 부모 값이 변경되어 있다. 당연한 건데 직관적으로 이해가 안 되어 캐시 메모리에서 날아가기 전에 기록.
또한 StringBuffer라는 것도 있다.
이 친구는 내부 동기화가 되어 있어 멀티스레드에서의 안정성을 제공하지만, 동기화 오버헤드로 인한 성능 저하가 있다.
즉 멀티스레드 쓸 때 사용.
3시간밖에 못 잤더니 너무 피곤하다
다음 포스팅에선 래퍼 클래스에 대해 알아보자 (Wrapper Class) : 기본형을 객체화시킨 것
아니 분명 자바에 대해 공부를 하면 더 잘 알게 될 거라 생각했는데 공부를 할수록 모르겠다..
이게 맞나..?
'기술스택 > 자바(Spring)' 카테고리의 다른 글
자바 타입 안전 열거형 + Enum (1) | 2025.02.16 |
---|---|
자바 래퍼 클래스 (Wrapper Class) (1) | 2025.02.15 |
자바 불변 객체 (with String Class) (1) | 2025.02.13 |
자바 String Class (1부) (0) | 2025.02.12 |
자바 최상위 Class Object (3) | 2025.02.11 |
- Total
- Today
- Yesterday
- java
- 김영한 실전 자바 중급
- 코딩테스트 준비
- 프로그래머스 상품을 구매한 회원 비율 구하기
- 프로그래머스 상품을 구매한 회원 비율 구하기 파이썬
- 코딩테스트
- 상속
- 김영한
- 백준 피보나치
- static
- 프로그래머스
- Los
- 백준
- extends
- lord of sql
- webhacking.kr
- samron
- spring
- 자바
- 김영한 실전 자바 기본
- los 15
- 스프링
- 백준 피보나치 수열
- 김영한 실전 자바 기초
- zixem
- los 15단계
- 상품을 구매한 회원 비율 구하기 파이썬
- samron3
- 기술스택
- ys.k
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |