정적 및 비 정적 초기화 코드 블록의 차이점


357

내 질문은 정적 키워드의 특정 사용법에 관한 것입니다. static키워드 를 사용 하여 어떤 함수에 속하지 않은 클래스 내의 코드 블록을 커버 할 수 있습니다. 예를 들어 다음 코드는 컴파일됩니다.

public class Test {
    private static final int a;    
    static {
        a = 5;
        doSomething(a);
    }
    private static int doSomething(int x) {
        return (x+5);
    }
}

static키워드 를 제거 하면 변수 a가 이므로 불평합니다 final. 그러나 모두 제거 할 수 finalstatic키워드를하고 컴파일합니다.

두 가지 방법으로 나를 혼란스럽게합니다. 어떤 메소드에 속하지 않은 코드 섹션이 있어야합니까? 어떻게 호출 할 수 있습니까? 일반적으로이 사용법의 목적은 무엇입니까? 아니면 이것에 관한 문서를 어디서 찾을 수 있습니까?

답변:


403

정적 수정자를 사용하는 코드 블록은 클래스 이니셜 라이저를 나타냅니다 . 정적 수정자가 없으면 코드 블록은 인스턴스 이니셜 라이저입니다.

클래스 이니셜 라이저는 클래스가로드 될 때 (실제로, 해결 될 때 기술이지만) 정의 된 순서대로 실행됩니다 (단순 변수 이니셜 라이저와 마찬가지로 하향식).

인스턴스 이니셜 라이저는 클래스 생성시 생성자 코드가 실행되기 직전, 수퍼 생성자 호출 직후에 정의 된 순서대로 실행됩니다.

static에서 제거하면 int a변수가 인스턴스 변수가되어 정적 초기화 블록에서 액세스 할 수 없습니다. "정적이 아닌 변수 a는 정적 컨텍스트에서 참조 할 수 없습니다"라는 오류로 컴파일에 실패합니다.

static이니셜 라이저 블록에서 제거 하면 인스턴스 이니셜 int a라이저가되어 구성시 초기화됩니다.


정적 초기화 프로그램은 실제로 클래스가로드되고 링크 된 후에 클래스가 초기화 될 때 나중에 호출됩니다. 클래스의 객체를 인스턴스화하거나 클래스의 정적 변수 또는 메서드에 액세스 할 때 발생합니다. 실제로 정적 이니셜 라이저와 메소드가있는 클래스가있는 public static void staticMethod(){}경우 실행 TestStatic.class.getMethod("staticMethod");합니다. 정적 이니셜 라이저가 호출되지 않습니다. 자세한 정보는 여기 docs.oracle.com/javase/specs/jvms/se10/html/…
Totò

@ Totò : 그렇습니다. 그것은 클래스의 해상도가 수반하는 것입니다. (적어도 그들은 "해상도"로 링크 + 이니 트로 참조 할 때 사용했습니다) 리플렉션을 사용 하여 클래스를 해결하지 않고도 클래스 에 관한 것들을 발견 할 수 있다는 것은 놀라운 일이 아닙니다 .
Lawrence Dol

166

어이! 정적 이니셜 라이저 란 무엇입니까?

정적 이니셜 라이저는 static {}java 클래스 내부의 코드 블록이며 생성자 또는 main 메소드가 호출되기 전에 한 번만 실행됩니다.

확인! 더 말 해주세요...

  • static { ... }Java 클래스 내부 의 코드 블록입니다 . 클래스가 호출 될 때 가상 머신에 의해 실행됩니다.
  • return지원되는 진술이 없습니다 .
  • 지원되는 인수가 없습니다.
  • 아니요 this또는 super지원됩니다.

흠, 어디서 사용할 수 있습니까?

:) 간단하다고 느끼는 곳이라면 어디에서나 사용할 수 있습니다. 그러나 데이터베이스 연결, API 초기화, 로깅 등을 수행 할 때 대부분의 시간이 사용됩니다.

껍질을 벗기지 마십시오! 예는 어디에 있습니까?

package com.example.learnjava;

import java.util.ArrayList;

public class Fruit {

    static {
        System.out.println("Inside Static Initializer.");

        // fruits array
        ArrayList<String> fruits = new ArrayList<>();
        fruits.add("Apple");
        fruits.add("Orange");
        fruits.add("Pear");

        // print fruits
        for (String fruit : fruits) {
            System.out.println(fruit);
        }
        System.out.println("End Static Initializer.\n");
    }

    public static void main(String[] args) {
        System.out.println("Inside Main Method.");
    }
}

산출???

내부 정적 이니셜 라이저.

사과

주황색

정적 이니셜 라이저를 종료하십시오.

내부 주요 방법.

도움이 되었기를 바랍니다!


감사합니다 Madan! 정적 블록 대신 사용할 수 afterPropertiesSet()InitializingBean?
Alexander Suraphel 2016 년

3
그래 넌 할수있어! 정적 초기화는 jvm에 의해 클래스가로드 될 때 호출됩니다. 코드가 실행되는 첫 단계입니다. 당신이 너무 생성자가있는 경우, 순서는 다음과 같습니다 정적 초기화, 생성자, afterPropertiesSet
마틴 바움 가르트너

57

static블록은 "static 초기화"이다.

클래스가로드 될 때 자동으로 호출되며 다른 방법으로 리플렉션을 통해 호출 할 수 없습니다.

JNI 코드를 작성할 때 개인적으로 만 사용했습니다.

class JNIGlue {
    static {
        System.loadLibrary("foo");
    }
}

6
아니, 그것을 명시 적으로 호출 할 수있는 방법은 없습니다. 클래스 이니셜 라이저는 Method인스턴스로 표현되지 않지만 Java 가상 머신에서만 호출됩니다.
Rafael Winterhalter 2016 년

46

이것은 http://www.programcreek.com/2011/10/java-class-instance-initializers/ 에서 직접 가져온 것입니다 .

1. 실행 순서

다음 수업을 살펴보십시오. 먼저 어느 수업이 먼저 실행되는지 알고 있습니까?

public class Foo {

    //instance variable initializer
    String s = "abc";

    //constructor
    public Foo() {
        System.out.println("constructor called");
    }

    //static initializer
    static {
        System.out.println("static initializer called");
    }

    //instance initializer
    {
        System.out.println("instance initializer called");
    }

    public static void main(String[] args) {
        new Foo();
        new Foo();
    }
}

산출:

정적 이니셜 라이저 호출

인스턴스 이니셜 라이저 호출

생성자 호출

인스턴스 이니셜 라이저 호출

생성자 호출

2. Java 인스턴스 이니셜 라이저는 어떻게 작동합니까?

위의 인스턴스 이니셜 라이저에는 println 문이 포함되어 있습니다. 작동 방식을 이해하기 위해 변수 할당 문으로 취급 할 수 있습니다 (예 :) b = 0. 이것은 이해하기 더 명확하게 만들 수 있습니다.

대신에

int b = 0, 당신은 쓸 수 있습니다

int b;
b = 0;

따라서 인스턴스 이니셜 라이저와 인스턴스 변수 이니셜 라이저는 거의 동일합니다.

3. 인스턴스 이니셜 라이저는 언제 유용합니까?

인스턴스 이니셜 라이저를 사용하는 경우는 드물지만 다음과 같은 경우 인스턴스 변수 이니셜 라이저를 대체 할 수 있습니다.

  1. 이니셜 라이저 코드는 예외를 처리해야합니다
  2. 인스턴스 변수 이니셜 라이저로 표현할 수없는 계산을 수행하십시오.

물론 이러한 코드는 생성자에서 작성할 수 있습니다. 그러나 클래스에 생성자가 여러 개인 경우 각 생성자에서 코드를 반복해야합니다.

인스턴스 이니셜 라이저를 사용하면 코드를 한 번만 작성할 수 있으며 객체를 생성하는 데 사용 된 생성자에 관계없이 실행됩니다. (이것은 단지 개념 일 뿐이며 자주 사용되지는 않습니다.)

인스턴스 이니셜 라이저가 유용한 또 다른 경우는 생성자를 전혀 선언 할 수없는 익명 내부 클래스입니다. (이것은 로깅 기능을 배치하기에 좋은 장소입니까?)

Derhein에게 감사합니다.

또한 인터페이스 [1]을 구현하는 익명 클래스에는 생성자가 없습니다. 따라서 생성시 모든 종류의 표현식을 실행하려면 인스턴스 이니셜 라이저가 필요합니다.


12

"final"은 객체 이니셜 라이저 코드가 끝나기 전에 변수를 초기화해야합니다. 마찬가지로 "정적 최종"은 클래스 초기화 코드의 끝에서 변수가 초기화되도록합니다. 초기화 코드에서 "정적"을 생략하면 객체 초기화 코드로 바뀝니다. 따라서 변수가 더 이상 보장을 만족시키지 않습니다.


8

프로그램의 어느 곳에서나 호출해야하는 정적 블록에 코드를 쓰지 않습니다. 코드의 목적이 호출되는 경우 메소드에 배치해야합니다.

클래스가로드 될 때 정적 변수를 초기화하기 위해 정적 초기화 블록을 작성할 수 있지만이 코드는 더 복잡 할 수 있습니다.

정적 이니셜 라이저 블록은 이름, 인수 및 반환 유형이없는 메서드처럼 보입니다. 전화하지 않기 때문에 이름이 필요하지 않습니다. 가상 머신이 클래스를로드 할 때만 호출됩니다.


6

개발자가 초기화 블록을 사용하면 Java 컴파일러는 초기화를 현재 클래스의 각 생성자에 복사합니다.

예:

다음 코드 :

class MyClass {

    private int myField = 3;
    {
        myField = myField + 2;
        //myField is worth 5 for all instance
    }

    public MyClass() {
        myField = myField * 4;
        //myField is worth 20 for all instance initialized with this construtor
    }

    public MyClass(int _myParam) {
        if (_myParam > 0) {
            myField = myField * 4;
            //myField is worth 20 for all instance initialized with this construtor
            //if _myParam is greater than 0
        } else {
            myField = myField + 5;
            //myField is worth 10 for all instance initialized with this construtor
            //if _myParam is lower than 0 or if _myParam is worth 0
        }
    }

    public void setMyField(int _myField) {
        myField = _myField;
    }


    public int getMyField() {
        return myField;
    }
}

public class MainClass{

    public static void main(String[] args) {
        MyClass myFirstInstance_ = new MyClass();
        System.out.println(myFirstInstance_.getMyField());//20
        MyClass mySecondInstance_ = new MyClass(1);
        System.out.println(mySecondInstance_.getMyField());//20
        MyClass myThirdInstance_ = new MyClass(-1);
        System.out.println(myThirdInstance_.getMyField());//10
    }
}

다음과 같습니다.

class MyClass {

    private int myField = 3;

    public MyClass() {
        myField = myField + 2;
        myField = myField * 4;
        //myField is worth 20 for all instance initialized with this construtor
    }

    public MyClass(int _myParam) {
        myField = myField + 2;
        if (_myParam > 0) {
            myField = myField * 4;
            //myField is worth 20 for all instance initialized with this construtor
            //if _myParam is greater than 0
        } else {
            myField = myField + 5;
            //myField is worth 10 for all instance initialized with this construtor
            //if _myParam is lower than 0 or if _myParam is worth 0
        }
    }

    public void setMyField(int _myField) {
        myField = _myField;
    }


    public int getMyField() {
        return myField;
    }
}

public class MainClass{

    public static void main(String[] args) {
        MyClass myFirstInstance_ = new MyClass();
        System.out.println(myFirstInstance_.getMyField());//20
        MyClass mySecondInstance_ = new MyClass(1);
        System.out.println(mySecondInstance_.getMyField());//20
        MyClass myThirdInstance_ = new MyClass(-1);
        System.out.println(myThirdInstance_.getMyField());//10
    }
}

개발자가 내 모범을 이해하기를 바랍니다.


4

정적 코드 블록을 사용하여 클래스 변수를 인스턴스화하거나 초기화 할 수 있습니다 (객체 변수가 아닌). 따라서 "a"정적을 선언한다는 것은 모든 Test 객체가 하나만 공유한다는 의미이며, 정적 코드 블록은 Test 클래스가 처음로드 될 때 생성 된 Test 객체 수에 관계없이 "a"를 한 번만 초기화합니다.


후속 조치로 객체의 인스턴스를 만들지 않고 대신 공개 정적 함수를 호출합니다. 이 공용 함수 호출 전에이 블록이 실행된다는 것을 의미합니까? 감사.
Szere Dyeri

클래스의 공개 정적 함수를 호출하면 클래스를 먼저로드해야하므로 예, 정적 초기화 프로그램이 먼저 실행됩니다.
Paul Tomblin

그것을 사용하려는 코드를 (간접적으로) 호출 한 클래스 초기화가 아닌 한. IFYSWIM. 순환 의존성과 그 모든 것.
Tom Hawtin-tackline

1
@Tom이 맞습니다. 다른 정적 초기화 프로그램이 호출되기 전에 하나의 정적 초기화 프로그램이 정적 메소드를 호출하는 무언가를 작성할 수는 있지만 내 생각은 생각에 반항하므로 결코 고려하지 않았습니다.
Paul Tomblin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.