목록책 내용 요약/Effective Java 3판 (9)
윤개발
중첩 클래스 중첩 클래스란 다른 클래스 안에 정의된 클래스를 말한다. 중첩 클래스는 자신을 감싼 바깥 클래스에서만 쓰여야 하며, 그외의 쓰임새가 있다면 톱레벨 클래스로 만들어야 한다. 아래 4가지 클래스는 중첩클래스의 종류들 이다. class A { int a = 10; public void run() { System.out.println("Run A"); B.run(); C c = new C(); c.run(); } // 정적 멤버 클래스 public static class B { public static void run() { System.out.println("Run B"); } } // 비정적 멤버 클래스 public class C { public void run() { // 정규화된 this를 ..
아래 코드는 두 가지 이상의 의미를 표현할 수 있으며 현재 표현하려는 의미를 태그값으로 알려주는 클래스이다. class Figure { enum Shape { RECTANGLE, CIRCLE }; final Shape shape; // 태그 필드 - 현재 모양을 나타낸다. // 다음 필드들은 모양이 사각형(RECTANGLE)일 때만 쓰인다. double length; double width; // 다음 필드느 모양이 원(CIRCLE)일 때만 쓰인다. double radius; // 원용 생성자 Figure(double radius) { shape = Shape.CIRCLE; this.radius = radius; } // 사각형용 생성자 Figure(double length, double width) {..
class Point{ public double x; public double y; } 이처럼 인스턴스 필드들을 모아둔 목적없는 퇴보한 클래스를 작성하려 할 때가 있다. 이러한 클래스는 직접 접근이 가능하여 캡슐화의 이점을 제공하지 못하고, 불변을 보장할 수 없다. 객체지향에서는 이런 클래스를 상당히 싫어해서 필드를 모두 private로 바꾸고 public 접근자 getter를 추가한다. 하지만 package-private(패키지에서만 접근할 수 있도록) 클래스 혹은 private 중첩 클래스(클래스 안에 클래스)라면 데이터 필드를 노출한다 해도 하등의 문제가 없다. 정리 public 클래스는 절대 가변 필드를 직접 노출해서는 안된다. 하지만 pacakge-private 클래스나 private 중첩 클래..
재정의를 하지 않는다. equals 메서드는 재정의하기 쉬워보이지만 곳곳에 함정이 있어 큰 문제가 될 수 있다. 가장 쉬운 길은 아예 재정의를 하지 않는 것이다. 아래 상황에 해당한다면 재정의 하지 않는것이 최선이다. 각 인스턴스가 본질적으로 고유하다. 값을 표현하는게 아니라 개체를 표현하는 클래스가 해당된다. 인스턴스의 논리적 동치성을 검사할 일이 없다. 상위 클래스에서 재정의한 equals가 하위 클래스에도 딱 들어맞는다. 클래스가 private 여서 equals를 호출할 일이 없다. 실수로라도 호출되는 걸 막고싶다면 예외처리로 throw new AssertionError()를 발생시키자.. 재정의 하는 상황 논리적 동치성을 비교해야할 때 재정의하면 좋다. 객체가 아니라 값이 같은지를 알고싶을때 Ma..
자바 라이브러리에는 close 메서드를 호출해 직접 닫아줘야 하는 자원이 많다. 상당수가 안전망으로 finalizer를 이용하지만 믿을만 하지 못하다.(item8) 전통적으로 자원을 닫힘을 보장하는 수단으로서 아래와 같이 try-finally가 쓰였다. try { OutputStream out = new FileOutputStream("filePath"); // do something } finally { out.close(); } 하지만 2개의 자원을 닫아야 하는 구간에서는 예외가 무시될 수 있으며 코드의 가독성도 떨어진다. public void copy(String src, String dst) throws IOException { InputStream in = new FileInputStream(s..
자바는 두 가지 객체 소멸자인 finalizer와 cleaner를 제공한다. finalizer는 예측할 수 없고, 상황에 따라 위험하여 쓰지 말아야한다. 또한 cleaner도 여전히 예측할 수 없고 느리며 불필요하다. 사용을 피해야 하는 이유 언제 실행될 지 알 수 없다. 성능 저하가 발생한다. finalize에서 발생한 예외는 무시되어 알 수 없다. finalizer와 cleaner 를 쓰는 곳 자원 반납에 쓸 close 메소드를 클라이언트가 호출하지 않았다면 사용할 만 하다. finalizer나 Cleaner가 호출될지 안될지 언제 호출될지도 모른지만 안하는 것 보다는 나으니 사용할 만 하다. 자바에서 제공하는 FileInputStream, FileOutputStream, ThreadPoolExecu..
가비지 컬렉션 언어에서는 메모리 누수를 찾기가 아주 까다롭다. 때문에 해당 참조를 다 사용한 후 null 처리하면 된다. Stack에서 메모리를 직접 관리하는 예제 public class Stack { private Object[] elements; ... public Object pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; // 문제점 } ... } 위 코드는 pop() 메소드에서 size를 감소시키지만 해당 값은 그대로 있어 메모리 누수가 발생하는 것을 볼 수 있다. 스택에서 pop을 할 경우 null 처리를 해준 코드 Object result = elements[--size]; elements[siz..
생성자에 매개변수가 많다면 빌더를 고려하라. 정적 팩터리 메서드나 생성자는 매개변수가 많을 때 적절히 대응하기 어렵다. 일반적으로 사용하는 다음의 2가지 경우를 보자. 기존 패턴 1.점층적 생성자 패턴 - 필수 생성자, 선택 매개변수 1개를 받는 생성자, 선택 매개변수 2개를 받는 생성자, ..., n개를 받는 생성자 단점: 매개변수가 많아지면 클라이언트가 코드를 작성하기 어렵고 읽기도 어렵다. 2.자바빈즈 패턴 - 매개변수가 없는 생성자로 객체를 만든 후 세터를 이용해서 결정하는 방식 단점: 객체가 완성되기 전까지 일관성이 무너진 상태에 놓이게 된다. 빌더 패턴 클라이언트는 필수 생성자를 호출해 빌더 객체를 얻고 빌더 객체의 세터메소드를 사용하여 원하는 선택 매개변수를 설정한다. 객체 선언 class ..
생성자 대신 정적 팩터리 메서드를 고려하라. 클래스의 인스턴스를 얻는 전통적인 방법은 public 생성자다. 하지만 클래스는 생성자와 별도로 정적 팩토리 메서드를 제공할 수 있다. 생성자보다 정적 팩토리 메소드를 제공하는 방식에는 장단점이 있다. 장점 1.이름을 가질 수 있다. 생성자에게 넘기는 매개변수와 생성자로는 객체의 특성을 제대로 설명하지 못한다. BigInteger(int,int,Random) // 생성자 BigInteger.probablePrime(int bitLength, Random rnd) //정적 팩토리 메소드 다음 두 코드 중에서 어느 쪽이 "소수인 BigInteger 인스턴스를 반환한다"는 의미를 더 잘 설명할까? 또한 매개변수의 순서를 다르게 하여 생성자를 추가하는 방식을 정적 팩..