-
private/public, static, final공부 이야기/JAVA 2024. 1. 22. 14:23
<기본 개념>
1. private : 선언한 클래스 내부에서만 사용할 수 있도록 접근을 제어
2. public : 선언한 클래스 외부에서도 사용할 수 있도록 개방
3. static : 변수 혹은 객체에 대해 한 번만 생성할 수 있도록 함
4. final : final로 선언된 변수, 객체에 대해서 값 변경을 할 수 없도록 제어 (Immutable)
<싱글톤과 적용>
동일한 내용에 대해 빈번하게 참조되는 객체가 있다고 가정했을 때 참조할 때마다 객체를 생성하는 것은 비효율적일 수도 있고 더 나아가서 성능을 떨어트릴 수도 있다.
이 때 static 키워드를 사용하면 단 한 번의 인스턴스 생성이 이뤄지며 구조적 비효율을 개선하고 성능을 최적화할 수 있다.
하지만 싱글톤 패턴을 사용했을 때 발생할 수 있는 Thread-safety 문제가 존재한다.
위 코드에서 동시에 여러 Thread가 null 여부를 확인할 때 의도치 않게 두 개의 객체를 반환할 수 있다.
그렇다고 인스턴스 선언만 해둔 상태인 DownState 객체를 new 키워드로 생성까지 한다면 초기 Memory-load에 부하를 일으킬 수 있다.
아마도 저 code는 lazy-load를 의도한 것 같다.
그렇다면 lazy-load와 Thread-safe 모두 만족시킬 수 있는 방법은 무엇일까?
DownState 클래스 내부에 private static으로 인스턴스 반환용 내부 클래스를 생성한 후
private static final으로 선언한 객체를 new 키워드로 생성한다.
그리고 null 체크하는 대신에 내부 클래스의 static으로 선언한 객체를 리턴해주면 된다.
/* 내부 클래스 생성 */
private static StaticHolder {
private static final DownState INSTANCE = new DownState();
}
/* 새로 만든 getInstance() 메소드 */
public static DownState getInstance() {
return StaticHolder.INSTANCE;
}
< Spring Bean >
Spring에서 Bean으로 관리하는 객체들은 기본적으로 싱글톤으로 구현되어 있기 때문에 Thread-unsafety 한 점에 대해 알고 있어야 하며
Bean으로 등록된 객체들은 상태가 변할 수 있는 내부 변수를 갖지 않도록 설계해야한다.
앞서 언급한 방식처럼 내부 변수(혹은 객체)를 final로 선언한 후, 직접 참조하지 않고 getter를 이용해서 return해야 한다.
생성자 방식으로 의존성을 주입할 때도 의존할 객체를 선언할 때 앞에 private final 키워드가 붙는 이유기도 하다.
<getter/setter>
참조할 클래스의 멤버 변수에 접근할 때 . 키워드를 사용하지 않고 getter/setter를 사용하는 이유는 뭘까
참조할 클래스가 다른 곳에서도 사용될 경우 의도치 않게 수정되어 난장판이 발생할 수 있기 때문이라고 한다.
근데 사실 사용할 클래스에서 별도의 변수나 DTO, VO와 같은 객체를 사용할 수 있음에도 불구하고 말이다.
쉽게 말해서 조심스럽게 사용하면 되는 부분이 아닌가는 생각이 들 수는 있다.
그러나 직관적이지 않고 기능 분리가 명확히 구분되지 않은 코드는 실수를 범할 수도 있고 설계 누락의 위험을 가진다.
또한, 개방/폐쇄 원칙이랑 연관이 깊은데 이론적으로만 접근했을 경우 와닿지가 않을 수 있다.
이 문제는 마치 코드를 하나의 클래스에서 쭉 늘여서 작성하는 것보다 패키지로 관리해서 명확히 구분하는 것과 같은 이치로 생각해볼 수 있다.
암튼 결론은 변수는 모두 private으로 선언하여 직접적인 접근을 허용하지 않도록 한 후,
getter/setter에 대해서만 public으로 선언해서 사용할 수 있도록 설계해야 한다는 점이다.
'공부 이야기 > JAVA' 카테고리의 다른 글
equals() 메소드 내부 동작 (0) 2021.09.05 인터페이스식 프로그래밍 (0) 2021.09.02 JAVA 8 - 람다식 (0) 2020.11.08