꾸준한 스터디
article thumbnail
item90.직렬화된 인스턴스 대신 직렬화 프록시 사용을 검토하라
개인룸/도윤 2023. 11. 28. 00:55

직렬화 프록시 패턴(serialization proxy pattern) Serializable을 구현하기로 결정한 순간 언어의 정상 메커니즘인 생성자 이외의 방법으로 인스턴스를 생성할 수 있게된다. 버그와 보안 문제가 일어날 가능성이 커진다는 뜻이다. 하지만 이 위험을 크게 줄여줄 기법이 바로 직렬화 프록시 패턴이다. 직렬화 프록시 패턴은 바깥 클래스의 논리적 상태를 정밀하게 표현하는 중첩 클래스를 설계해 private static으로 선언한다. 이 중첩 클래스가 바로 바깥 클래스의 직렬화 프록시다. 중첩 클래스의 생성자는 단 하나여야 하며, 바깥 클래스를 매개변수로 받아야 한다. 이 생성자는 단순히 인수로 넘어온 인스턴스의 데이터를 복사한다. 일관성 검사나 방어적 복사도 필요없다. 설계상, 직렬화 프록..

article thumbnail
item89. 인스턴스 수를 통제해야 한다면 readResolver보다는 열거 타입을 사용하라
개인룸/도윤 2023. 11. 27. 01:32

public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis() {...} public void leaveTheBuilding() {...} } 바깥에서 생성자를 호출하지 못하게 막는 방식으로 인스턴스가 오직 하나만 만들어짐을 보장하는 싱글턴 패턴의 예이다. 하지만 implements Serializable을 추가하는 순간 역직렬화시 readObject 사용하며 이 클래스가 초기화될 때 만들어진 인스턴스와는 별개인 인스턴스를 반환하게 된다. readResolve 기능을 이용하여 역직렬화 후 새로 생성된 객체를 인수로 이 메서드가 호출되고, 이 메서드가 원래 1개만 만들어져 있는 객체를 반환해 새로 생성된 객체는 참..

article thumbnail
Item.88 readObject 메서드는 방어적으로 작성하라
개인룸/도윤 2023. 11. 16. 21:07

아이템 50에서 불변인 날짜 범위 클래스를 만드는데 불변식을 지키고 불변을 유지하기 위해 생성자와 접근자 메서드에서 가변객체인 Date 객체를 방어적으로 복사하느라 코드가 상당히 길어졌다. 이 클래스를 직렬화 하기로 했을 때 Period 객체의 물리적 표현이 논리적 표현과 부합하므로 기본 직렬화 형태를 사용한다고 하면 이 클래스의 주요한 불변식을 더는 보장하지 못하게 된다. 문제는 readObject 메서드가 바이트 스트림을 받는 실질적 또다른 public 생성자이기 때문인데 불변식을 깨뜨릴 의도로 임의 생성한 바이트 스트림을 건네면 정상적인 생성자로 만들어낼 수 없는 객체를 생성해낼 수 있다. 실행하면 Period가 ClassNotFoundException가 나는데.. 모쪼록 원래 결과 값은 Fri J..

article thumbnail
Item87. 커스텀 직렬화 형태를 고려해보라
개인룸/도윤 2023. 11. 15. 20:46

클래스가 Serializable을 구현하고 기본 직렬화 형태를 사용하면 다음 릴리스 때 버리려 한 현재의 구현에 종속된다. 즉, 기본 직렬화 형태를 버릴 수 없게 된다. 실제로 BigInteger 같은 일부 자바 클래스가 이 문제에 시달리고 있다. 먼저 고민해보고 괜찮다고 판단될 때만 기본 직렬화 형태를 사용하라 기본 직렬화형태는 유연성, 성능, 정확성 측면에서 신중히 고민한 후 합당할 때만 사용해야 한다. 직접 설계하더라도 기본 직렬화 형태와 거의 같은 결과가 나올 경우에만 기본 형태를 써야 한다. 어떤 객체의 기본 직렬화 형태는 그 객체를 루트로 하는 객체그래프의 물리적 모습을 나름 효율적으로 인코딩한다. 객체가 포함한 데이터들과 그 객체에서 시작해 접근할 수 있는 모든 객체를 담아내며, 심지어 이 ..

article thumbnail
Item86.Serializable을 구현할지는 신중히 결정하라
개인룸/도윤 2023. 11. 9. 18:25

어떤 클래스의 인스턴스를 직렬화 하려면 클래스 선언에 implements Serializable만 덧 붙이면 된다. 너무 쉽게 적용할 수 있기 때문에 직렬화를 지원하기란 짧게 보면 손쉬워 보이지만, 길게 보면 아주 값비싼 일이다. Serializable의 문제점 Serializable을 구현하면 릴리스한 뒤에는 수정하기 어렵다. 클래스가 Serializable을 구현하면 직렬화된 바이트 스트림 인코딩(직렬화 형태)도 하나의 공개 API가 된다. 이 클래스가 널리 퍼진다면 그 직렬화 형태도 공개 API와 마찬가지로 영원히 지원해야 한다. 커스텀 직렬화 형태를 설계하지 않고 자바의 기본 방식을 사용하면 직렬화 형태는 클래스의 private과 package-private 인스턴스 필드마저 API로 공개되어 캡..

article thumbnail
Item85.자바 직렬화의 대안을 찾으라
개인룸/도윤 2023. 11. 9. 02:22

직렬화(serialization)란 객체를 데이터 스트림으로 만드는 것을 뜻한다. 다시 말해 객체에 저장된 데이터를 스트림에 쓰기(write)위해 연속적인 데이터로 변환하는 것을 말한다. 객체는 참조타입 인스턴스들로 현재 참조되고 있는 메모리 주소를 전달할 수 없기때문에 Byte 형태로 기본형 데이터들로 변환되어 전송된다. 반대로 스트림으로부터 데이터를 읽어서 객체를 만드는 것을 역직렬화(deserialization)라고 한다. 이를 시스템적으로 살펴보면 JVM의 힙(Heap) 혹은 스택(Stack) 메모리에 상주하고 있는 객체 데이터를 직렬화를 통해 바이트 형태로 변환하여 데이터베이스나 파일과 같은 외부 저장소에 저장해두고, 다른 컴퓨터에서 이 파일을 가져와 역직렬화를 통해 자바 객체로 변환해서 JVM..

article thumbnail
Item84. 프로그램의 동작을 스레드 스케줄러에 기대지 말라
개인룸/도윤 2023. 10. 29. 16:15

여러 스레드가 실행 중이면 운영체제의 스레드 스케줄러가 어떤 스레드를 얼마나 오래 실행할지 정한다. 정상적인 운영체제라면 이 작업을 공정하게 수행하지만 구체적인 스케줄링 정책은 운영체제마다 다를 수 있다. 따라서 잘 작성된 프로그램이라면 이 정책에 좌지우지돼서는 안된다. 정확성이나 성능이 스레드 스케줄러에 따라 달라지는 프로그램이라면 다른 플랫폼에 이식하기 어렵다. 스케줄링이란 메모리에 적재된 프로그램을 CPU가 실행할 수 있도록 운영체제로 하여금 프로세스나 스레드에 CPU를 할당하는 것으로 스케줄러는 제한된 자원을 여러 프로세스가 효율적으로 운영하도록 다양한 정책을 가지고 CPU를 할당하게 되는데, 정책이란 어떤 기준 / 순서로 CPU를 할당하는지 결정하는 방법이다. 스케줄러는 프로세스 선택 기준을 정..

Item83. 지연 초기화는 신중히 사용하라
개인룸/도윤 2023. 10. 29. 16:15

지연 초기화(lazy initialization)는 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법이다. 그래서 값이 전혀 쓰이지 않으면 초기화도 결코 일어나지 않는다. 이 기법은 정적 필드와 인스턴스 필드 모두 사용할 수 있다. 지연 초기화는 주로 최적화 용도로 쓰이지만, 클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제를 해결하는 효과도 있다. 지연 초기화는 클래스 혹은 인스턴스 생성시의 초기화 비용은 줄지만 지연 초기화하는 필드에 접근하는 비용은 커진다. 지연 초기화하려는 필드 중 결국 초기화가 이뤄지는 비율에 따라, 실제 초기화에 드는 비용에 따라, 초기화된 각 필드를 얼마나 빈번히 호출하느냐에 따라 지연 초기화가 실제 성능을 느려지게 할 수 있으므로 필요할 때까지는 웬만하면 ..