Effective Java
  • 직렬화 프록시 패턴(serialization proxy pattern) 바깥 클래스의 논리적 상태를 정밀하게 표현하는 중첩 클래스를 설계하여 private static으로 선언한다. 이 중첩 클래스가 바로 바깥 클래스의 직렬화 프록시다. 중첩 클래스의 생성자는 단 하나여야 하며, 바깥 클래스를 매개변수로 받아야한다. 이 생성자는 단순히 인수로 넘어온 인스턴스의 데이터를 복사한다. 일관성 검사나 방어적 복사도 필요없다. 설계상 직렬화 프록시의 기본 직렬화 형태는 바깥 클래스의 직렬화 형태로 쓰기에 이상적이다. 그리고 바깥 클래스와 직렬화 프록시 모두 Serializable을 구현한다고 선언해야한다. Period 클래스용 직렬화 프록시 package com.java.effective.item90; impor..

    Read more
  • 싱글턴 패턴의 직렬화 이 클래스는 바깥에서 생성자를 호출하지 못하게 막는 방식으로, 인스턴스가 오직 하나만 만들어짐을 보장했다. public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis() { } public void leaveTheBuilding() { ... } } 이 클래스는 선언에 implements Serializable을 추가하는 순간 더이상 싱글턴이 아니게된다. 기본 직렬화를 쓰지 않더라도, 그리고 명시적인 readObject를 제공하더라도 이 클래스가 초기화될 때 만들어진 인스턴스와는 별개인 인스턴스를 반환하게된다. readResolve 기능 readResolve 기능을 이용하면 readObjec..

    Read more
  • 방어적 복사를 사용하는 불변 클래스 아이템 50에서 불변인 날짜 범위 클래스를 만드는데 가변인 Date 필드를 사용했었다. 그래서 불벼식을 지키고 불변을 유지하기 위해 생성자와 접근자에서 Date 객체를 방어적으로 복사하느라 코드가 길어졌다. https://devfunny.tistory.com/613?category=895441 [교재 EffectiveJava] 아이템 50. 적시에 방어적 복사본을 만들라 불변식 깨뜨리기 자바는 메모리 충돌 오류에서 안전한 언어다. 자바로 작성한 클래스는 시스템의 다른 부분에서 무슨 짓을 하든 그 불변식이 지켜진다. 하지만 아무리 자바라고 해도 다른 클래 devfunny.tistory.com 방어적 복사를 사용하는 불변 클래스 package com.java.effecti..

    Read more
  • 커스텀 직렬화 형태를 고려 Serializable을 구현하고 기본 직렬화 형태를 사용한다면 다음 릴리스 때 버리려한 현재의 구현에 영원히 발이 묶이게된다. 기본 직렬화 형태를 버릴 수 없게된다. 실제로도 BigInteger 같은 일부 자바 클래스가 이 문제에 시달리고있다. 먼저 고민해보고 괜찮다고 판단될 때만 기본 직렬화 형태를 사용하라. 기본 직렬화 형태는 유연성, 성능, 정확성 측면에서 신중히 고민한 후 합당할 때만 사용해야한다. 일반적으로 직접 설계하더라도 기본 직렬화 형태와 거의 같은 결과가 나올 경우에만 기본 형태를 써야한다. 기본 직렬화 형태 객체의 물리적 표현과 논리적 내용이 같다면 기본 직렬화 형태라도 무방하다. 기본 직렬화 형태에 적합한 후보 public class Name impleme..

    Read more
  • Serializable 어떤 클래스의 인스턴스를 직렬화할 수 있게 하려면 클래스 선언에 implements Serializable만 덧붙히면 된다. 너무 쉽게 적용할 수 있는것에 비해, 직렬화는 아주 값비싼 일이다. Serializable을 구현하면 릴리스한 뒤에는 수정하기 어렵다. 클래스가 Serializable을 구현하면 직렬화된 바이트 스트림 인코딩(직렬화 형태)도 하나의 공개 API가 된다. 그래서 이 클래스가 널리 퍼진다면 그 직렬화 형태도 영원히 지원해야한다. 커스텀 직렬화 형태를 설계하지 않고 자바의 기본 방식을 사용하면 직렬화 형태는 최소 적용 당시 클래스의 내부 구현 방식에 영원히 묶여버린다. 기본 직렬화 형태에서는 클래스의 private, package-private 인스턴스 필드들마저..

    Read more
  • 직렬화의 위험성 직렬화는 공격 범위가 너무 넓고, 지속적으로 넓어져 방어하기 어렵다. ObjectInputStream의 readObject 메서드를 호출하면서 객체 그래프가 역직렬화되기 때문이다. readObject 메서드는 (Serializable 인터페이스를 구현했다면) 클래스패스 안의 거의 모든 타입의 객체를 만들어낼 수 있다. 바이트 스트림을 역직렬화하는 과정에서 이 메서드는 그 타입들 안의 모든 코드를 수행할 수 있다. 이는 즉, 그 타입들의 코드 전체가 공격 범위에 들어간다는 뜻이다. 자바의 역직렬화는 명백하고 현존하는 위험이다. 신뢰할 수 없는 스트림을 역직렬화하면 원격 코드 실행, 서비스 거부 등의 공격으로 이어질 수 있다. 역직렬화 폭탄 역직렬화에 시간이 오래 걸리는 짧은 스트림을 역직렬..

    Read more
  • 스레드 스케줄러(Thread Scheduler) 여러 스레드가 실행 중이면 운영체제의 스레드 스케줄러가 어떤 스레드를 얼마나 오래 실행할지 정한다. 정상적인 운영체제라면 이 작업을 공정하게 수행하지만 구체적인 스케줄링 정책은 운영체제마다 다를 수 있다. 따라서 잘 작성된 프로그램이라면 이 정책에 좌지우지돼서는 안된다. 정확성이나 성능이 스레드 스케줄러에 따라 달라지는 프로그램이라면 다른 플랫폼에 이식하기 어렵다. 실행 가능한 스레드의 평균적인 수를 프로세서 수보다 지나치게 많아지지 않도록 하는것이 중요하다. 그래야 스레드 스케줄러가 고민할 거리가 줄어든다. 실행 준비가 된 스레드들은 맡은 작업을 완료할때까지 계속 실행되도록 만들자. 여기서 실행 가능한 스레드의 수와 전체 스레드 수는 구분해야한다. 전체 ..

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

    Read more
  • API 문서에 synchronized 한정자 한 메서드를 여러 스레드가 동시에 호출할때 그 메서드가 어떻게 동작하느냐는 해당 클래스와 이를 사용하는 클라이언트 사이의 중요한 계약과 같다. API 문서에서 아무런 언급이 없으면 그 클래스 사용자는 나름의 가정을 해야만한다. 그 가정이 틀리면 클라이언트 프로그램은 동기화를 충분히 하지 못하거나, 너무 지나치게 하게되어 심각한 오류가 발생할 수 있다. "API 문서에 synchronized 한정자가 보이는 메서드는 스레드 안전하다."라는 말은 몇가지 면에서 틀렸다. 자바독이 기본 옵션에서 생성한 API 문서에는 synchronized 한정자가 포함되지 않는다. 메서드 선언에 synchronized 한정자를 선언할지는 구현 이슈일뿐 API에 속하지 않는다. 따라..

    Read more
  • wait와 notify wait와 notify는 올바르게 사용하기가 아주 까다로우니, java.util.concurrent의 고수준 동시성 유틸리티를 사용하자. ▶ wait(), notify(), notifyAll() 포스팅 바로가기 https://devfunny.tistory.com/855 wait()과 notify(), notifyAll() wait(), notify(), notifyAll() synchronized로 동기화해서 공유 데이터를 보호할때 특정 스레드가 객체의 락을 가진 상태로 오랜 시간을 보내지 않도록 하는것도 중요하다. 락을 오랜시간 보유하게되면, 다 devfunny.tistory.com 1) 실행자 프레임워크 https://devfunny.tistory.com/807?category..

    Read more
  • 들어가기전 아이템 80에서 나오는 '실행자 프레임워크'에 대해 아래의 포스팅으로 공부하자. https://devfunny.tistory.com/807?category=957918 [JAVA8 병렬프로그래밍] Executors 클래스, ExecutorService 인터페이스 Executors 클래스 - Executor 인터페이스 : 컨커런트 API의 핵심 인터페이스다. 이 인터페이스를 구현한 여러 종류의 클래스를 기본으로 제공한다. - 스레드 풀 : 스레드를 관리하기 위한 풀이다. 병렬 devfunny.tistory.com java.util.concurrent 패키지의 등장 java.util.concurrent 패키지는 실행자 프레임워크(Executor Framework)라고 하는 인터페이스 기반의 유연한..

    Read more
  • 들어가기전 https://devfunny.tistory.com/669?category=895441 [교재 EffectiveJava] 아이템 78. 공유 중인 가변 데이터는 동기화해 사용하라 synchronized 키워드 해당 메서드나 블록을 한번에 한 스레드씩 수행하도록 보장한다. 많은 프로그래머가 동기화를 배타적 실행, 즉 한 스레드가 변경하는 중이라서 상태가 일관되지 않은 순간의 devfunny.tistory.com 이번 아이템은 아이템 78의 반대 상황을 다룬다. 아이템 78에서 충분하지 못한 동기화의 피해를 다뤘다면, 이번 아이템에서는 과도한 동기화에 대한 문제점을 다룬다. 과도한 동기화 과도한 동기화는 성능을 떨어뜨리고, 교착상태에 빠뜨리고, 예측할 수 없는 동작을 일으킬 수 있다. 응답 불가와..

    Read more
  • synchronized 키워드 해당 메서드나 블록을 한번에 한 스레드씩 수행하도록 보장한다. 많은 프로그래머가 동기화를 배타적 실행, 즉 한 스레드가 변경하는 중이라서 상태가 일관되지 않은 순간의 객체를 다른 스레드가 보지 못하게 막는 용도로만 생각한다. 한 객체가 일관된 상태를 가지고 생성되고, 이 객체에 접근하는 메서드는 그 객체에 락(lock)을 건다. 락을 건 메서드는 객체의 상태를 확인하고 필요하면 수정한다. 즉, 객체를 하나의 일관된 상태에서 다른 일관된 상태로 변화시킨다. 동기화를 제대로 사용하면 어떤 메서드도 이 객체의 상태가 일관되지 않은 순간을 볼 수 없을 것이다. 여기에 중요한 기능이 한가지 더 있다. 동기화 없이는 한 스레드가 만든 변화를 다른 스레드에서 확인하지 못할 수도 있다. ..

    Read more
  • 예외 무시 API 설계자가 메서드 선언에 예외를 명시하는 까닭은 그 메서드를 사용할때 적절한 조치를 취해달라고 말하는 것이다. 하지만 이 예외를 무시하기란 아주 쉽다. 해당 메서드 호출을 try~catch를 사용하여 무시할 수 있기 때문이다. 예외 무시 // catch 블록을 비워두면 예외가 무시된다. try { ... } catch (SomeException ignored) {} 예외는 문제 상황에 잘 대처하기 위해 존재하는데 catch 블록을 비워두면 예외가 존재할 이유가 사라진다. 예외를 무시해야할 경우 예외를 무시해야 할때도 있다. 예를들어 FileInputStream을 닫을때, 파일의 상태를 변경하지 않았으니 복구할 것이 없으며, 필요한 정보는 다 읽었으므로 남은 작업을 중단할 이유도 없다. 혹..

    Read more
  • 실패 원자적 작업 도중 예외가 발생해도 그 객체는 여전히 정상적으로 사용할 수 있는 상태라면 코드가 더 유용해진다. 검사 예외를 던진 경우라면 호출자가 오류 상태를 복구할 수 있을테니 특히 더 유용할 것이다. 호출된 메서드가 실패하더라도, 해당 객체는 메서드 호출 전 상태를 유지해야한다. 이러한 특성을 '실패 원자적(failure-atomic)' 이라고 한다. 실패 원자적으로 만드는 방법 메서드를 실패 원자적으로 만드는 방법은 다양하다. 1) 불변 객체로 설계한다. 불변 객체는 태생적으로 실패 원자적이다. 메서드가 실패하면 새로운 객체가 만들어지지는 않을 수 있으나, 기존 객체가 불안정한 상태에 빠지는 일은 결코 없다. 불변 객체의 상태는 생성 시점에 고정되어 절대 변하지 않기 때문이다. 2) 가변 객체..

    Read more
  • Copyright 2024. GRAVITY all rights reserved