불변 객체란?
불변 객체란, 객체 생성 이후 내부의 상태가 변하지 않는 객체입니다. 내부의 상태를 아예 공개하지 않거나, getter 메소드만을 제공하며, 참조타입인 변수를 제공할 때는 방어적 복사를 통해 제공합니다.
더 자세히 얘기하면 불변 객체를 생성하기 위해 먼저, primitive
타입은 private final
제어자를 사용하고, setter
메소드는 구현하지 않습니다.
reference
타입은 추가적인 작업이 필요합니다.
일반 객체 참조변수인 경우, 일반 객체도 불변 객체로 만들어야 합니다.
배열이나 List인 참조변수인 경우, 생성자에서 copy 해서 받아오고 복사해서 반환하는 방어적 복사를 이용합니다.
final
final 키워드는 크게 변수, 메서드, 클래스 에 사용할 수 있습니다. 변수에 final 을 사용하면, 해당 변수는 더이상 '재할당' 할 수 없게 됩니다. 하지만, List나 객체 참조는 내부의 상태가 변할 수 있으므로 앞서 추가적인 작업이 필요합니다.
메서드에 사용하면, 해당 메서드는 자식클래스에서 '오버라이드' 될 수 없습니다. 자식클래스에서 핵심 내용의 변경을 원치않을때 사용합니다.
마지막으로 클래스에 사용되면, 해당 클래스는 '상속' 할 수 없습니다. 대표적으로 String 클래스는 확장을 막기 위해 final Class로 사용됩니다.
불변객체를 사용하는 이유
- Thread-Safe 하여 동기화를 고려하지 않아도 됩니다
- 불변객체는 Cache나 Map 또는 Set 요소로 사용하기에 적합합니다. 왜냐하면 한 번 데이터가 저장된 이후에 변경사항이 없기 때문에 다시 갱신하는 등의 부가적인 작업이 필요하지가 않습니다
- 객체의 상태가 다른 코드에 의해 변경될 가능성이 없기 때문에 불안함을 줄이고 유지보수성이 높은 코드를 작성할 수 있습니다.
String 객체가 불변인 이유
String Constant Pool
에서 문자열 객체는 여러 참조를 가질 가능성이 높습니다. 왜냐하면 String Constant Pool
특징이 같은 문자열이면 새로운 객체를 생성하는 것이 아닌
기존에 존재하던 같은 내용의 문자열을 참조하도록 하기 때문입니다.
그런데, 만약 String 객체가 불변이 아니면 멀티스레드 환경에서 다른 스레드에 의해 해당 문자열이 변경될 수 있기 때문에 추가적인 동기화 작업이 필요합니다. 하지만 String 객체는 불변이기 때문에 thread-safe 하여 동기화 작업을 제거해도 됩니다.
또 다른 이유로는 클래스 로딩할 때 인수로 문자열을 전달합니다. 만약 변경이 가능하면 잘못된 클래스가 로드될 수 있기 때문입니다.
마지막으로 보안면에서, 데이터베이스 URL, 사용자 이름/비밀번호를 문자열로 매개변수에 전달할 때 변경이 가능하면 위험에 노출될 수 있기 때문입니다.
Reference
https://mangkyu.tistory.com/131
https://velog.io/@conatuseus/Java-Immutable-Object%EB%B6%88%EB%B3%80%EA%B0%9D%EC%B2%B4