목록분류 전체보기 (115)
도당탕탕
Serialization Proxy Pattern enclosing class의 logical state를 정확히 나타내는 nested class를 정의하는 방법 public final class Period implements Serializable { private final Date start; private final Date end; public Period(Date start, Date end) { this.start = new Date(start.getTime()); this.end = new Date(end.getTime()); if (this.start.compareTo(this.end) > 0) throw new IllegalArgumentException(start + " after ..
싱글턴 패턴 예제 public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis() { ... } public void leaveTheBuilding() { ... } } 위와 같이 아이템 3에서 싱글턴 예제를 보았다. 이 클래스는 바깥에서 생성자를 호출하지 못하게 막는 방식으로 인스턴스가 오직 하나만 만들어짐을 보장했다. 하지만 이 클래스는 implement Serializable을 선언하는 순간 더 이상 싱글턴이 아니게 된다. 기본 직렬화를 쓰지 않더라도, 그리고 명시적인 readObject를 제공하더라도 소용없다. 어떤 readObject를 사용하든 이 클래스가 초기화될 때 만들어진 인스턴스와는 별개인 인스턴스..
예제코드 public final class Period { private final Date start; private final Date end; /** * @param start the beginning of the period * @param end the end of the period; must not precede start * @throws IllegalArgumentException if start is after end * @throws NullPointerException if start or end is null */ public Period(Date start, Date end) { this.start = new Date(start.getTime()); this.end = new D..
클래스가 Serializable을 구현하고 기본 직렬화 형태를 사용하면 다음 릴리스 때 고치거나 버리려 할 때 발목을 잡을 확률이 크다. 즉 기본 직렬화 형태를 버릴 수 없게 돼버려 이와 같은 문제를 낳게 된다. 실제로 BigInteger 같은 일부 자바 클래스가 이 문제에 시달리고 있다. 따라서 먼저 고민해보고 괜찮다고 판단될 때만 기본 직렬화 형태를 사용하라. 기본 직렬화 형태는 유연성, 성능, 정확성 측면에서 신중히 고민한 후 합당할 때만 사용해야 한다. 직렬화에 적합한 예 기본 직렬화 형태는 객체가 포함한 데이터뿐만 아니라 그 객체를 시작으로 접근할 수 있는 모든 객체와 객체들의 연결된 정보까지 나타낸다. 이상적인 직렬화 형태라면 물리적인 모습과 독립된 논리적인 모습만을 표현해야 한다. 객체의 물..
이유 Serializable을 구현시 다음과 같은 비용이 발생 클래스 구현을 바꾸기 어려움 구현을 변경하면 byte-stream encoding이 변경되고 클라이언트 코드가 영향을 받는다. 버그와 보안적 결함을 가질 가능성 증가 명시적으로 생성자를 호출하지 않기 때문에, 초기화과정을 잊거나 공격자 코드가 호출되도록 둘 수 있음. 테스트 비용증가 버전들 사이에 serialize, deserialize 과정의 호환성이 테스트 되어야 한다. 추가적으로 주의할것 상속할 수 있으면서 Serializable을 구현하는 클래스를 정의할 때는 더 주의하기 클라이언트 코드에서 finalize 를 정의하여 finalizer attack을 수행할 수 있다. Inner class들은 Serializable을 구현하면 안 됨 ..
직렬화는 프로그래머가 어렵지 않게 분산 객체를 만들 수 있다고 해도, API와 구현 사이의 모호해진 경계, 잠재적인 정확성 문제, 성능, 보안, 유지보수 등 직렬화는 다양한 위험성을 내포하고 있다. 대표적인 사례를 보자면 샌프란시스코 시영 교통국이 랜섬웨어 공격을 받아 요금 징수 시스템이 이틀간 마비되는 사태를 겪었다고 한다. 그렇기 때문에 직렬화는 되도록 피하는 것이 좋다. 직렬화의 근본적인 문제는 *공격 범위가 너무 넓고 지속적으로 더 넓어져 방어하기 어렵다는 점이다. * 예를 들면 ObjectInputStream의 readObject 메서드를 호출하면서 객체 그래프가 역직렬화를 한다. readObject 메소드는메서드는 클래스패스 안의 거의 모든 타입의 객체를 만들어 낼 수 있기 때문에, 바이트 스..
이유 운영체제마다 스레드 스케줄링 방식이 달라서, 모든 운영체제에서 같게 동작한다는 보장이 없다. 해결방법 runnable thread의 평균수를 프로세서 수 보다 너무 많게 설정하지 않기 busy waiting인 스레드를 줄이기 public class SlowCountDownLatch { private int count; public SlowCountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException(count + " < 0"); this.count = count; } public void await() { while (true) { synchronized(this) { if (count == 0) return; } } } ..
지연 초기화란 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법이다. 해당 클래스의 인스턴스 중 그 필드를 사용하는 인스턴스의 비율이 낮은 반면, 그 필드를 초기화하는 비용이 크다면 유용하다. 그러나 대부분의 상황에서는 final로 필드를 선언하는 일반적인 초기화가 지연 초기화보다 낫다. 다음 예를 보자 지연 초기화 예제 private FieldType field; private synchronized FieldType getField() { if (field == null) field = computeFieldValue(); return field; } 위와 같이 synchronized 키워드를 사용해서 초기화를 할 수 있다. 하지만 성능 때문에 정적 필드로 두어 해당 필드를 초기화해야 한..