문제

그래서 나는 늦게 내 Java 기술을 닦았으며 이전에 알지 못했던 몇 비트의 기능을 발견했습니다. 정적 및 인스턴스 이니셜 라이저는 이러한 두 가지 기술입니다.

내 질문은 언제 생성자에 코드를 포함하는 대신 이니셜 라이저를 사용합니까? 나는 몇 가지 명백한 가능성을 생각했습니다.

  • 정적/인스턴스 이니셜 라이저를 사용하여 "최종"정적/인스턴스 변수의 값을 설정하는 반면 생성자는 할 수 없습니다.

  • 정적 이니셜 라이저를 사용하여 클래스에서 정적 변수의 값을 설정하는 데 사용될 수 있으며, "if (somestaticVar == null) // 스테 물건"블록이있는 것보다 더 효율적이어야합니다.

이 두 경우 모두 이러한 변수를 설정하는 데 필요한 코드가 단순히 "var = value"보다 더 복잡하다고 가정합니다. 그렇지 않으면 변수를 선언 할 때 단순히 값을 설정하는 대신 이니셜 라이저를 사용해야 할 이유가없는 것 같습니다.

그러나 이들은 사소한 이익 (특히 최종 변수를 설정하는 능력)이 아니지만, 이니셜 라이저를 사용해야하는 상황이 제한적인 것으로 보입니다.

생성자에서 수행되는 많은 일에 대한 초기화를 확실히 사용할 수는 있지만 실제로 그렇게해야 할 이유는 없습니다. 클래스의 모든 생성자가 많은 양의 코드를 공유하더라도 개인 초기화 () 함수를 사용하는 것은 새를 쓸 때 해당 코드를 실행하는 데 잠그지 않기 때문에 초기화를 사용하는 것보다 나에게 더 적합 해 보입니다. 건설자.

내가 뭔가를 놓치고 있습니까? 이니셜 라이저를 사용해야하는 다른 상황이 많이 있습니까? 아니면 매우 구체적인 상황에서 사용되는 다소 제한된 도구입니까?

도움이 되었습니까?

해결책

정적 초기화기는 언급 한 Cletus와 같이 유용하며 동일한 방식으로 사용합니다. 클래스가로드 될 때 초기화 할 정적 변수가있는 경우, 정적 이니셜 라이저는 특히 복잡한 초기화를 수행하고 여전히 정적 변수를 유지할 수 있으므로 이동하는 방법입니다. final. 이것은 큰 승리입니다.

나는 "if (somestaticvar == null) // 물건을 행하고있다"는 지저분하고 오류가 발생하기 쉬운 것을 발견했다. 정적으로 초기화되고 선언 된 경우 final, 당신은 그것이 가능성을 피합니다 null.

그러나 나는 당신이 말할 때 혼란스러워합니다.

정적/인스턴스 이니셜 라이저를 사용하여 "최종"정적/인스턴스 변수의 값을 설정하는 반면 생성자는 할 수 없습니다.

나는 당신이 둘 다 말하고 있다고 가정합니다.

  • 정적 초기화기는 "최종"정적 변수의 값을 설정하는 데 사용될 수 있지만 생성자는 할 수 없습니다.
  • 인스턴스 이니셜 라이저를 사용하여 "최종"인스턴스 변수의 값을 설정할 수 있지만 생성자는 할 수 없습니다.

그리고 당신은 첫 번째 요점에서 맞습니다. 두 번째 지점은 잘못입니다. 예를 들어 다음과 같이 할 수 있습니다.

class MyClass {
    private final int counter;
    public MyClass(final int counter) {
        this.counter = counter;
    }
}

또한 생성자간에 많은 코드가 공유되면이를 처리하는 가장 좋은 방법 중 하나는 체인 생성자에 대한 기본값을 제공하는 것입니다. 이것은 무엇을하고 있는지 분명합니다.

class MyClass {
    private final int counter;
    public MyClass() {
        this(0);
    }
    public MyClass(final int counter) {
        this.counter = counter;
    }
}

다른 팁

익명의 내부 클래스는 생성자를 가질 수 없으므로 (익명으로) 예를 들어 초기화 자에게는 상당히 자연 스럽습니다.

나는 종종 최종 정적 데이터, 특히 컬렉션을 설정하기 위해 정적 이니셜 라이저 블록을 사용합니다. 예를 들어:

public class Deck {
  private final static List<String> SUITS;

  static {
    List<String> list = new ArrayList<String>();
    list.add("Clubs");
    list.add("Spades");
    list.add("Hearts");
    list.add("Diamonds");
    SUITS = Collections.unmodifiableList(list);
  }

  ...
}

이제이 예제는 한 줄의 코드로 수행 할 수 있습니다.

private final static List<String> SUITS =
  Collections.unmodifiableList(
    Arrays.asList("Clubs", "Spades", "Hearts", "Diamonds")
  );

그러나 정적 버전은 특히 항목이 초기화하기가 쉽지 않은 경우에는 훨씬 깔끔 할 수 있습니다.

순진한 구현은 또한 수정할 수없는 목록을 만들지 않을 수 있으며, 이는 잠재적 인 실수입니다. 위의 내용은 공개 방법 등에서 행복하게 돌아올 수있는 불변의 데이터 구조를 만듭니다.

여기에 이미 훌륭한 포인트를 추가하기 위해. 정적 이니셜 라이저는 스레드 안전합니다. 클래스가로드 될 때 실행되므로 생성자를 사용하는 것보다 더 간단한 정적 데이터 초기화를 만듭니다. 여기서 정적 데이터가 초기화되는지 확인한 다음 실제로 초기화하는지 확인하려면 동기화 된 블록이 필요합니다.

public class MyClass {

    static private Properties propTable;

    static
    {
        try 
        {
            propTable.load(new FileInputStream("/data/user.prop"));
        } 
        catch (Exception e) 
        {
            propTable.put("user", System.getProperty("user"));
            propTable.put("password", System.getProperty("password"));
        }
    }

~ 대

public class MyClass 
{
    public MyClass()
    {
        synchronized (MyClass.class) 
        {
            if (propTable == null)
            {
                try 
                {
                    propTable.load(new FileInputStream("/data/user.prop"));
                } 
                catch (Exception e) 
                {
                    propTable.put("user", System.getProperty("user"));
                    propTable.put("password", System.getProperty("password"));
                }
            }
        }
    }

잊지 마십시오. 이제 인스턴스 레벨이 아닌 클래스에서 동기화해야합니다. 이는 클래스가로드 될 때 한 번의 비용 대신 구성된 모든 인스턴스 비용이 발생합니다. 게다가, 그것은 못 생겼어 ;-)

나는 초기화의 초기 순서 대 생성자에 대한 답을 찾는 전체 기사를 읽었습니다. 나는 그것을 찾지 못했기 때문에 이해를 확인하기 위해 몇 가지 코드를 썼습니다. 나는이 작은 데모를 의견으로 추가 할 것이라고 생각했다. 이해를 테스트하려면 맨 아래에서 답을 읽기 전에 답을 예측할 수 있는지 확인하십시오.

/**
 * Demonstrate order of initialization in Java.
 * @author Daniel S. Wilkerson
 */
public class CtorOrder {
  public static void main(String[] args) {
    B a = new B();
  }
}

class A {
  A() {
    System.out.println("A ctor");
  }
}

class B extends A {

  int x = initX();

  int initX() {
    System.out.println("B initX");
    return 1;
  }

  B() {
    super();
    System.out.println("B ctor");
  }

}

산출:

java CtorOrder
A ctor
B initX
B ctor

정적 이니셜 라이저는 정적 컨텍스트에서 생성자와 동일합니다. 인스턴스 이니셜 라이저보다 더 자주 볼 수 있습니다. 때로는 정적 환경을 설정하려면 코드를 실행해야합니다.

일반적으로 인스턴스 이탈 라이저는 익명의 내부 클래스에 가장 적합합니다. 보세요 Jmock의 요리 책 코드를보다 읽기 쉽게 만드는 혁신적인 방법을 보려면.

때로는 생성자를 가로 지르는 체인에 복잡한 논리가있는 경우 (서브 클래싱 중이며 Super () 호출해야하기 때문에이 ()를 호출 할 수 없음) 인스턴스에서 일반적인 작업을 수행하여 복제를 피할 수 있습니다. 초기화. 그러나 인스턴스 이탈 라이저는 너무 드물기 때문에 많은 사람들에게 놀라운 구문이므로 피하고 제작자 동작이 필요한 경우 클래스 콘크리트를 콘크리트로 만들려고합니다.

Jmock은 프레임 워크가 사용되는 방식이기 때문에 예외입니다.

당신이 선택한 경우 고려해야 할 중요한 측면은 다음과 같습니다.

이니셜 라이저 블록은 멤버입니다 클래스/객체의 생성자는 아닙니다. 이것은 고려할 때 중요합니다 확장/서브 클래싱:

  1. 초기화기가 상속됩니다 서브 클래스로. (하지만 그림자를받을 수 있습니다)
    이는 기본적으로 서브 클래스가 상위 클래스의 의도대로 초기화된다는 것을 의미합니다.
  2. 생성자입니다 ~ 아니다 상속, 그렇지만. (그들은 단지 전화합니다 super() 즉, 매개 변수 없음] 암시 적으로 또는 구체적으로 만들어야합니다. super(...) 수동으로 전화하십시오.)
    이것은 암시 적 또는 독점이 가능하다는 것을 의미합니다. super(...) 호출은 상위 클래스의 의도대로 서브 클래스를 초기화하지 않을 수 있습니다.

이 초기화 블록 의이 예를 고려하십시오.

class ParentWithInitializer {
    protected final String aFieldToInitialize;

    {
        aFieldToInitialize = "init";
        System.out.println("initializing in initializer block of: " 
            + this.getClass().getSimpleName());
    }
}

class ChildOfParentWithInitializer extends ParentWithInitializer{
    public static void main(String... args){
        System.out.println(new ChildOfParentWithInitializer().aFieldToInitialize);
    }
}

산출:
initializing in initializer block of: ChildOfParentWithInitializer init
-> 서브 클래스 구현이 어떤 생성자에 있든 필드가 초기화됩니다.

이제이 예제를 생성자로 고려하십시오.

class ParentWithConstructor {
    protected final String aFieldToInitialize;

    // different constructors initialize the value differently:
    ParentWithConstructor(){
        //init a null object
        aFieldToInitialize = null;
        System.out.println("Constructor of " 
            + this.getClass().getSimpleName() + " inits to null");
    }

    ParentWithConstructor(String... params) {
        //init all fields to intended values
        aFieldToInitialize = "intended init Value";
        System.out.println("initializing in parameterized constructor of:" 
            + this.getClass().getSimpleName());
    }
}

class ChildOfParentWithConstructor extends ParentWithConstructor{
    public static void main (String... args){
        System.out.println(new ChildOfParentWithConstructor().aFieldToInitialize);
    }
}

산출:
Constructor of ChildOfParentWithConstructor inits to null null
-> 이것은 필드를 초기화합니다 null 기본적으로 원하는 결과가 아닐 수도 있습니다.

또한 위의 멋진 답변과 함께 한 점을 추가하고 싶습니다. Class.forname ( "")을 사용하여 JDBC에 드라이버를로드하면 클래스로드가 발생하고 드라이버 클래스의 정적 이니셜 라이저가 발사되고 IT 내부의 코드는 드라이버를 드라이버 관리자에게 등록합니다. 이것은 정적 코드 블록의 중요한 사용 중 하나입니다.

당신이 언급했듯이, 그것은 많은 경우에 유용하지 않으며, 덜 사용되지 않은 구문과 마찬가지로, 당신은 아마도 코드를보고있는 다음 사람이 30 초를 소비하는 것을 막기 위해 그것을 피하고 싶을 것입니다.

반면에, 그것은 몇 가지 일을하는 유일한 방법입니다 (나는 당신이 거의 다루었습니다).

정적 변수 자체는 어쨌든 다소 피해야합니다. 그러나 항상 아님은 아니지만 많은 것을 사용하거나 한 클래스에서 많이 사용하는 경우 다른 접근 방식을 찾을 수 있습니다. 미래의 자아가 감사 할 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top