- [Java 정리] String, final, Immutable2022년 08월 05일
- starryeye
- 작성자
- 2022.08.05.:55
자바 기본 객체인 String은 immutable 객체이다.
immutable객체?
불변 객체라는 뜻이다. 한번 객체에 값이 할당되면 더 이상 데이터가 변하지 않는 속성을 가지고 있다.
대표적으로 String, Integer, Boolean 등이 있다.
장점 : 값이 변하지 않기 때문에 신뢰도가 높다. 멀티 쓰레드 환경에서 자주 쓰인다. (thread-safe)
단점 : 값을 변경해야 할때마다 새로운 객체를 생성해내야 하므로, 성능 문제가 생긴다.
String a = "a";
a += "b";위 코드에서 변경이 일어난거 같은데?
사실 참조자가 변한 것이다..
참조자가 변했다고?
java의 String 클래스는 두가지로 선언 할 수 있다.
String a = "a";
String b = new String("b");1번째는 문자열 리터럴이라 부르고 String Constant Pool 이라는 영역에 할당된다.
2번째는 new 연산자를 이용하여 일반 객체와 같이 메모리의 Heap 영역에 할당된다.
String Constant Pool의 위치는?
JDK 7 이전에는 PermGen(Permanent Generation) 영역에 위치하였으나, JDK 7 부터 Heap 영역에 위치하게 되었다.
PermGen 영역은 GC 대상이 아니였고 Heap으로 이사하면서 대상이 되었다.
(이유는 PermGen 영역이 64MB로 제한된 공간이기 때문에 더 넓은 영역인 Heap으로 이사함)
String Constant Pool(Heap에 위치)에 할당 되던가 new를 사용하여 Heap에 할당되던간에
독립적이다. 값이 동일하더라도 주소값 다름.
주소 비교
str1 == str2
-> true
str3 == str4
-> false
값 비교
str3.equals(str4)
-> true
따라서..
처음 봤던 아래의 경우..
String a = "a";
a += "b";"a" 라는 리터럴 문자열을 버리고.. "ab"라는 리터럴 문자열을 새로 생성하여 그 참조를 a가 가지게 된 것이다.
<참고>
자바의 모든 객체는 참조형 변수로 받는 것이다. (주소 값을 가짐)
지역변수인 primitive type 변수는 stack 영역에 할당되며 내부에 값을 가진다.
C++의 call-by-value, call-by-reference 와 연관지어 생각해보자.
final에 대해 잠시 알아보자..
final은 총 3가지 유형으로 사용가능하다.
변수에 사용할 경우..
변수에 final을 붙이면 해당 변수는 초기화 이후 수정할 수 없다.
(초기화는 선언과 함께 바로 초기화 or 생성자로 초기화 or 초기화 block 사용)
(참고로 static 키워드가 함께 붙는다면.. 생성자로 초기화는 제한된다.)
수정할 수 없다는 범위는 값에 한정한다.
final int value = 2;
value = 3; // 컴파일 에러value의 값을 바꾸려 하자 에러가 난다.
final tempObject temp = new tempObject();
temp = new tempObject(); // 컴파일 에러tempObject 객체의 인스턴스를 받는 참조형 변수 temp는 새로운 인스턴스로 바꾸려 하자 에러가 난다.
(참고로.. tempObeject 내부에 필드값은 final 범위를 벗어나므로 변경 가능하다.)
메서드에 사용할 경우..
오버라이드를 제한한다.
클래스에 사용할 경우..
상속이 불가능해진다..
(Integer 클래스가 이 경우에 해당된다.)
Immutable Object
예시 코드를 통하여 본격적으로 Immutable Object에 대해 알아보자.
위 객체는 Immutable 객체가 아니다. setValue 메서드를 통해 value 필드 데이터를
수정 가능하기 때문이다.
위 코드에서는 value 필드에 final 키워드를 붙여서..
setValue 메서드를 통한 값 변경은 불가능 하다.
그런데.. testObject의 필드에 참조 변수가 있다면..?
참조 변수가 일반 객체를 가르킬 경우..
단순히 final 키워드를 참조변수(testObject2)에 붙이는 것 만으로
해당 객체의 값이 불변한다를 보장 할 수 없다.
왜냐하면.. final 의 범위는 해당 참조 변수의 값(인스턴스)이 변경하는 것만을 제한 하기 때문이다.
인스턴스 내부의 값은 여전히 변경 가능 할 수 있다.
(testObject2의 value 필드가 final 이 아니고 setter 함수가 있으면 변경 가능해지는 것이다..)
따라서.. 참조 변수도 Immutable 객체여야 한다.
참조 변수가 배열을 가르킬 경우..
참조 변수 arr에 final 키워드를 사용하여 한번 초기화 되면 더이상 가르키는 대상을 변경 불가능하도록 하였다.
초기화때는 java.util의 copyof라는 복사 메서드를 이용하여 참조를 그대로 받는 것을 방지
배열을 리턴할때도 clone 메서드를 사용하여 복사후 리턴
(위처럼 복사하지 않을 경우 call-by-reference로 같이 변경되어버림)
참조 변수가 List를 가르킬 경우..
기본 개념은 배열을 가르킬때와 동일하다.
참조 변수 list는 final 키워드를 사용하여 초기화 이후 가르키는 대상을 변경 불가능 하도록 하였다.
초기화는 참조를 그대로 받지않고 new로 복사 생성하여 초기화 한다.
Get 메서드는 참조자를 그대로 반환하지만..
java.util.Collections의 unmodifiableList 메서드로 반환 받는 쪽에서
read-only로 사용하도록 제한한다.
List에 담기는 객체인 testObject2 또한 Immutable 객체로 만든다.
참고
'Java' 카테고리의 다른 글
[Java 정리] Thread 2 (0) 2022.08.13 [Java 정리] Thread 1 (0) 2022.08.13 [Java 정리] Garbage Collector 1 (0) 2022.07.30 [Java 정리] 내부 클래스 (0) 2022.07.28 [Java 정리] Exception, Error (0) 2022.07.15 다음글이전글이전 글이 없습니다.댓글