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.