이 글은 Effective Java 3/E의 내용을 요약한 글입니다. 자세한 내용은 책을 참고하시기 바랍니다.
똑같은 기능의 객체를 매번 생성하기보다 하나의 객체를 재사용하는 것이 나을 떄가 많다.
1. new String()은 사용하지 말아라
String 파헤치기 에서 학습을 했듯이
String string1 = new String("abc");
와 같이 생성자를 통해 string1을 만들 경우 코드가 실행될 때마다 새로운 인스턴스가 생성됩니다.
반면 String string2 = "abc";
와 같은 코드는 반복된다면 스트링 풀에 위치한 하나의 주소를 공유하는 식으로 진행이 됩니다.
2. 정적 팩터리 메서드를 통해 인스턴스를 재사용할 수 있다.
정적 팩터리 메서드에서도 Effective Java 1. 생성자 대신 정적 팩터리 메서드를 고려하라 에서 호출될 때 인스턴스를 새로 생성하지 않아도 됩니다. 또한 불변이 아닌 가변 객체라도 사용중에 변경되지 않는다면 재사용이 가능합니다.
3. 생성 비용이 비싼 객체는 캐싱을 하여 재사용을 하라.
String.matches
에 사용되는 Pattern
는 인스턴스 생성비용이 큽니다. 이러한 클래스의 경우
반복되어 사용할 때 한번 쓰고 버리기보다는 정적 초기화(static final)를 통한 재사용을 하여야 한다.
재사을 할 경우 프로그램의 속도도 개선될 것입니다.
📌 해당 클래스가 한번도 호출하지 않을 경우를 대비해 지연 초기화(Lazy initialization)을 할 수도 있지만 지연초기화는 코드를 복잡하게 만들지만 성능은 크게 개선하지 못하는 경우가 많아 추천하지 않는다
4. 어댑터는 뒷단 객체 하나당 하나씩 만드는거로 충분하다.
어댑터 뷰의 경우 어댑터의 실제 작업은 뒷단에 위입하고 본인은 인터페이스 역할을 합니다. 이러한 경우 어댑터는 뒷단 객체 외에는 관리할 상태가 없으므로 뒷단 객체 하나당 어댑터 하나씩 만들면 충분하다.
5. wrapper class보다는 primitive type을 사용하자
primitive type과 wrapper class를 섞어서 사용할 때는 오토 박싱(auto boxing)을 통해 자동으로 둘 사이의 변환이 이루어집니다. 하지만 오토박싱이 기본타입과 그에 대응하는 박싱된 기본 타입의 구분을 흐려주지만, 완전히 없애주는 것은 아닙니다. 그리하여 wrapper class보다는 primitive type을 사용하고, 의도치 않은 오토박싱이 숨어들지 않도록 주의해야합니다.
정리
생성하는데 비용이 많이드는 무거운 객체의 경우 인스턴스의 재사용을 하는 것이 좋습니다. 하지만 그 외에는 불필요한 객체를 생성하고 회수하는 일은 JVM이 크게 부담을 갖지 않아 무리해서 인스턴스의 재사용을 할 필요는 없습니다. 오히려 프로그래밍의 명확성, 간결성, 기능을 위해서 객체를 추가로 생성하는 일이라면 일반적으로 좋은 일이기 때문입니다.
단순히 객체 생성을 피하고자 객체 풀(Pool)을 만드는 것도 좋지 않습니다. DB연결의 경우 생성비용이 비싸 객체 풀을 통해 재사용을 하는 것이 좋지만 일반적으로 객체 풀은 코드를 헷갈리게 만들고 메모리 사용량을 늘리고 성능을 떨어뜨립니다.
그리고 요즘 GC(Garbage Collection)는 최적화가 잘 되어 가벼운 객체를 생성하는게 직접 만든 객체풀보다 훨씬 빠릅니다.