Java Final 변수에 기본값이 있습니까?


81

다음과 같은 프로그램이 있습니다.

class Test {

    final int x;

    {
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }

}

실행하려고하면 다음과 같이 컴파일러 오류가 발생합니다. variable x might not have been initializedJava 기본값에 따라 아래 출력을 올바르게 받아야합니까 ??

"Here x is 0".

최종 변수에 dafault 값이 있습니까?

이렇게 내 코드를 변경하면

class Test {

    final int x;

    {
        printX();
        x = 7;
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }

}

다음과 같이 출력됩니다.

Here x is 0                                                                                      
Here x is 7                                                                                     
const called

누구든지이 행동을 설명해 주시겠습니까 ..

답변:


62

http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html , "인스턴스 멤버 초기화"장 :

Java 컴파일러는 이니셜 라이저 블록을 모든 생성자로 복사합니다.

즉 말하자면:

{
    printX();
}

Test() {
    System.out.println("const called");
}

정확히 다음과 같이 작동합니다.

Test() {
    printX();
    System.out.println("const called");
}

보시다시피 인스턴스가 생성되면 최종 필드는 확실히 할당 되지 않은 반면 ( http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html에서 # jls-8.3.1.2 ) :

빈 최종 인스턴스 변수는 선언 된 클래스의 모든 생성자 끝에 확실히 할당되어야합니다. 그렇지 않으면 컴파일 타임 오류가 발생합니다.

이있다 그래서, (적어도 내가 그것을 찾을 수 없어) 워드 프로세서에서 explitely 생성자가 끝나기 전에 최종 필드해야 일시적 걸릴 기본값을 언급하지 않는 것 동안 예측 값을 사용하면 경우 할당하기 전에 읽어보십시오.

기본값 : http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

두 번째 스 니펫에서 x는 인스턴스 생성시 초기화되므로 컴파일러가 불평하지 않습니다.

Test() {
    printX();
    x = 7;
    printX();
    System.out.println("const called");
}

또한 다음 접근 방식은 작동하지 않습니다. 최종 변수의 기본값 사용은 메소드를 통해서만 허용됩니다.

Test() {
    System.out.println("Here x is " + x); // Compile time error : variable 'x' might not be initialized
    x = 7;
    System.out.println("Here x is " + x);
    System.out.println("const called");
}

1
예 중 하나에서 super ()에 대한 암시 적 (또는 명시 적) 호출이 어디에 있는지 주목할 가치가 있습니다.
— Patrick

2
이것은 최종 필드를 초기화하지 않으면 컴파일 오류가 발생하는 이유에 대한 대답이 아닙니다.
— justhalf

@ sp00m 좋은 참조-나는 그것을 은행에 넣을 것입니다.
— 보헤미안

2
@justhalf 대답에 중요한 점이 없습니다. 기본 상태 (메소드를 통해)에서 final에 액세스 할 수 있지만 생성 프로세스가 끝나기 전에 초기화하지 않으면 컴파일러가 불만을 표시합니다. 이것이 두 번째 시도가 작동하지만 (실제로 x 초기화) 첫 번째 시도는 작동하지 않는 이유입니다. 컴파일러는 또한 빈 최종 결과에 직접 액세스하려고하면 불평 할 것입니다.
— Luca

28

JLS 는 생성자 (또는 거의 동일한 초기화 블록)의 빈 최종 인스턴스 변수에 기본값을 할당 해야 한다고 말합니다 . 이것이 첫 번째 경우에 오류가 발생하는 이유입니다. 그러나 이전에는 생성자에서 액세스 할 수 없다고 말하지 않습니다. 약간 이상해 보이지만 할당하기 전에 액세스하여 int-0의 기본값을 볼 수 있습니다.

UPD. @ I4mpi에서 언급했듯이 JLS 는 액세스 전에 각 값을 확실히 할당 해야한다는 규칙을 정의 합니다 .

Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.

그러나 생성자 및 필드와 관련 하여 흥미로운 규칙 이 있습니다.

If C has at least one instance initializer or instance variable initializer then V is [un]assigned after an explicit or implicit superclass constructor invocation if V is [un]assigned after the rightmost instance initializer or instance variable initializer of C.

따라서 두 번째 경우 값 x은 생성자의 끝에 할당을 포함하기 때문에 생성자의 시작 부분에 확실히 할당 됩니다.


사실, 그것은 않습니다 당신이 할당하기 전에 액세스 할 수 없습니다 말한다 : "각 지역 변수 (§14.4) 모든 빈 최종 필드 (§4.12.4을 §8.3.1.2) 값의 액세스가 발생할 때 확실히 할당 된 값이 있어야합니다 "
— l4mpi 2014-07-28

1
그것이 그러나이 규칙은 생성자의 관점에서 이상한 행동을 가지고, "확실히 할당"해야한다, 나는 대답 업데이트 한
— udalmik

복잡한 조건에 따라 final필드를 읽거나 읽지 않을 수있는 코드 메서드 가 있고 해당 코드가 필드가 작성되기 전과 후에 실행될 수있는 경우 컴파일러는 일반적으로 기록되기 전에 실제로 필드를 읽었는지 여부를 알고 있습니다.
— supercat 2014-07-28

7

초기화하지 않으면 초기화되지 않았으므로 x컴파일 타임 오류가 발생 x합니다.

x최종 선언 은 생성자 또는 이니셜 라이저 블록 에서만 초기화 될 수 있음을 의미합니다. (이 블록은 컴파일러에 의해 모든 생성자로 복사되기 때문입니다).

0변수가 초기화되기 전에 인쇄 되는 이유 는 매뉴얼에 정의 된 동작 때문입니다 ( "기본값"섹션 참조).

기본값

필드가 선언 될 때 항상 값을 할당 할 필요는 없습니다. 선언되었지만 초기화되지 않은 필드는 컴파일러에 의해 적절한 기본값으로 설정됩니다. 일반적으로이 기본값은 데이터 유형에 따라 0 또는 널입니다. 그러나 이러한 기본값에 의존하는 것은 일반적으로 잘못된 프로그래밍 스타일로 간주됩니다.

다음 차트는 위 데이터 유형의 기본값을 요약 한 것입니다.

Data Type   Default Value (for fields)
--------------------------------------
byte        0
short       0
int         0
long        0L
float       0.0f
double      0.0d
char        '\u0000'
String (or any object)      null
boolean     false

4

첫 번째 오류는 컴파일러가 최종 필드가 있지만 초기화 할 코드가 없다고 불평하는 것입니다.

두 번째 예에서는 값을 할당하는 코드가 있지만 실행 순서는 할당 전후에 필드를 참조한다는 것을 의미합니다.

모든 필드의 미리 할당 된 값이 기본값입니다.


2

클래스의 최종 필드가 아닌 모든 필드는 기본값으로 초기화됩니다 ( 0숫자 데이터 유형, false부울 및 null참조 유형 (복잡 객체라고도 함)). 이러한 필드는 생성자 (또는 인스턴스 초기화 블록)가 생성자 이전 또는 이후에 필드가 선언되었는지 여부에 관계없이 실행되기 전에 초기화됩니다.

클래스의 최종 필드 에는 기본값 이 없으며 클래스 생성자가 작업을 완료하기 전에 한 번만 명시 적으로 초기화해야합니다.

실행 블록 (예 : 메서드) 내부의 지역 변수에는 기본값이 없습니다. 이러한 필드는 처음 사용하기 전에 명시 적으로 초기화해야하며 지역 변수가 최종 항목으로 표시되는지 여부는 중요하지 않습니다.


1

제가 할 수있는 가장 간단한 말로 표현하겠습니다.

final변수를 초기화해야합니다. 이는 언어 사양에서 지정합니다. 단, 선언시 초기화 할 필요는 없습니다.

객체가 초기화되기 전에 초기화해야합니다.

이니셜 라이저 블록을 사용하여 최종 변수를 초기화 할 수 있습니다. 이제 이니셜 라이저 블록은 두 가지 유형이 static있으며non-static

사용한 블록은 비 정적 초기화 블록입니다. 따라서 객체를 만들 때 런타임은 생성자를 호출하고 차례로 부모 클래스의 생성자를 호출합니다.

그 후 모든 이니셜 라이저 (귀하의 경우 비 정적 이니셜 라이저)를 호출합니다.

귀하의 질문에서, 사례 1 : 초기화 블록이 완료된 후에도 최종 변수는 초기화되지 않은 상태로 유지되며 이는 오류 컴파일러가 감지합니다.

년 의 경우 2 : 이니셜 라이저 따라서 컴파일러는 객체가 초기화되기 전에, 최종 이미 초기화 된 것을 알고, 최종 변수를 초기화합니다. 따라서 불평하지 않을 것입니다.

이제 질문은 왜 x0을 취하는 것입니다. 이유는 컴파일러가 이미 오류가 없음을 알고 있으므로 init 메소드를 호출하면 모든 최종 결과가 기본값으로 초기화되고 x=7. 아래의 init 호출을 참조하십시오.

여기에 이미지 설명 입력


1

내가 아는 한 컴파일러는 항상 클래스 변수를 기본값 (최종 변수 포함)으로 초기화합니다. 예를 들어, int를 자체로 초기화하는 경우 int는 기본값 인 0으로 설정됩니다. 아래를 참조하십시오.

class Test {
    final int x;

    {
        printX();
        x = this.x;
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }
}

위의 내용은 다음을 인쇄합니다.

Here x is 0
Here x is 0
const called

1
최종 변수 x는 OP 코드에서 정적이 아닙니다.
— JamesB

this.x로 초기화하기 위해 OP의 코드를 쉽게 수정할 수 있으며 동일한 일이 발생합니다. 그것이 정적이든 아니든 상관 없습니다.
— Michael D.

OP의 질문을 읽지 않은 것처럼 여기에서 정적 콘텐츠를 제거하는 것이 좋습니다.
— JamesB 2014

OP의 코드에서 기준선을 사용하면 도움이됩니까? 내가 말했듯이 변수가 정적인지 여부는 중요하지 않습니다. 내 요점은 변수를 자체적으로 초기화하고 기본값을 얻는 것은 변수가 명시 적으로 초기화되기 전에 암시 적으로 초기화된다는 것을 의미합니다.
— Michael D.

1
초기화되기 전에 최종 변수에 (직접) 접근하려고하기 때문에 컴파일되지 않습니다. 6 행
— Luca

1

실행하려고하면 컴파일러 오류가 발생합니다. 변수 x가 Java 기본값을 기반으로 초기화되지 않았을 수 있습니다. 아래 출력을 올바르게 받아야합니까 ??

"여기서 x는 0"입니다.

아니요. 처음에 컴파일 타임 오류가 발생하기 때문에 해당 출력이 표시되지 않습니다. 최종 변수는 기본값을 얻지 만 JLS (Java Language Specification)에서는 생성자의 끝 (LE : 여기에 초기화 블록 포함)까지 초기화해야합니다. 그렇지 않으면 컴파일 타임 오류가 발생합니다. 코드가 컴파일되고 실행되는 것을 방지합니다.

두 번째 예제는 요구 사항을 존중하므로 (1) 코드가 컴파일되고 (2) 예상되는 동작을 얻습니다.

앞으로 JLS에 익숙해 지도록 노력하십시오. Java 언어에 대한 더 좋은 정보 소스는 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.