최종 정의가 잘못되었나요?
먼저, 퍼즐:다음 코드는 무엇을 인쇄합니까?
public class RecursiveStatic {
public static void main(String[] args) {
System.out.println(scale(5));
}
private static final long X = scale(10);
private static long scale(long value) {
return X * value;
}
}
답변:
0
아래 스포.
X
) X = scale(10) + 3
는, 「」, 「」가 됩니다X = 0
X = 3
,,X
으로 is is is is is is is is is is to to 로 설정되어 있습니다.0
나중에 로 설정됩니다.3
final
!
정적 수식자는 최종 수식어와 함께 상수를 정의하는 데도 사용됩니다.마지막 수식자는 이 필드의 값을 변경할 수 없음을 나타냅니다.
출처 : https://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html ( ] added]]] added )
벌레가 ★★★★★★★★★★★★★★★★★? 이는?final
★★★★★★★★★★★★★★★★★★?
을 사용하다 X
됩니다. 즉, 2가지 값이 할당됩니다.0
★★★★★★★★★★★★★★★★★」3
이 는는 of of .의 위반이라고 final
.
public class RecursiveStatic {
public static void main(String[] args) {
System.out.println(scale(5));
}
private static final long X = scale(10) + 3;
private static long scale(long value) {
System.out.println("X = " + X);
return X * value;
}
}
이 질문은 Java 정적 최종 필드 초기화 순서와 중복될 수 있는 것으로 플래그가 지정되었습니다.다른 질문은 초기화 순서를 다루고 있고, 내 질문은 주기적인 초기화와 결합된 초기화를 다루고 있기 때문에 이 질문은 중복되지 않는다고 생각합니다.final
태그. 다른 질문만으로는 제 질문의 코드가 왜 오류를 만들지 않는지 이해할 수 없습니다.
얻는 합니다. 즉, 언제가 가 얻을 수 있을까요?a
가 붙어 있다final
을 사용하다
a=5
a=5
에 해당되지 않습니다.「 」는final
수를변 변경 ??? ???
아주 흥미로운 발견이야이를 이해하려면 Java Language Specification(JLS)을 자세히 살펴봐야 합니다.
는 ★★★★★★★★★★★★★★★★★★★.final
는 1개의 할당만 허용합니다.단, 기본값은 할당 없음입니다.실제로 이러한 모든 변수(클래스 변수, 인스턴스 변수, 배열 구성 요소)는 할당 전 처음부터 기본값을 가리킵니다.그런 다음 첫 번째 할당이 참조를 변경합니다.
클래스 변수 및 기본값
다음의 예를 참조해 주세요.
private static Object x;
public static void main(String[] args) {
System.out.println(x); // Prints 'null'
}
으로 값을 하지 않았습니다.x
「」, 「」를 가리키고 있습니다.null
기본값은 입니다.그것을 § 4.12.5와 비교하세요.
변수의 초기값
각 클래스 변수, 인스턴스 변수 또는 어레이 컴포넌트는 작성 시 기본값('15.9, '15.10.2')으로 초기화됩니다.
이는 예시와 같이 이러한 변수에만 적용됩니다.로컬 변수에는 적용되지 않습니다.다음 예를 참조해 주십시오.
public static void main(String[] args) {
Object x;
System.out.println(x);
// Compile-time error:
// variable x might not have been initialized
}
같은 JLS 패러그래프:
로컬 변수(1414.4, 1414.14)는 사용하기 전에 명확한 할당 규칙(def16(Definite Assignment)을 사용하여 검증할 수 있는 방법으로 초기화(414.4) 또는 할당(2615.26)에 의해 명시적으로 값을 부여해야 한다.
최종 변수
에는 ,, 럼, 럼을 .final
§ 4.12.4부터:
최종 변수
변수는 final로 선언할 수 있습니다.최종 변수는 한 번만 할당할 수 있습니다.할당 직전에 할당이 완전히 해제되지 않는 한 최종 변수가 할당되면 컴파일 시 오류가 발생합니다(§16 (Definite Assignment)).
설명.
다음으로 약간 변경된 예를 제시하겠습니다.
public static void main(String[] args) {
System.out.println("After: " + X);
}
private static final long X = assign();
private static long assign() {
// Access the value before first assignment
System.out.println("Before: " + X);
return X + 1;
}
출력합니다.
Before: 0
After: 1
우리가 배운 것을 상기하라. assign
수X
에 아직 값이 할당되지 않았습니다.따라서 클래스 변수이기 때문에 기본값을 가리키고 JLS에 따르면 이러한 변수는 항상 즉시 기본값을 가리킵니다(로컬 변수와는 대조적으로).그 후assign
를 X
값이 되어 있습니다.1
그그 of final
그렇기 때문에 다음 은 안 통합니다.final
:
private static long assign() {
// Assign X
X = 1;
// Second assign after method will crash
return X + 1;
}
JLS의 예
@Andrew 덕분에 이 시나리오를 다루는 JLS 패러그래프를 찾았습니다.
하지만 먼저, 이 문제를 살펴봅시다.
private static final long X = X + 1;
// Compile-time error:
// self-reference in initializer
이 메서드로부터의 액세스는 허가되지 않는 이유는 무엇입니까?필드가 아직 초기화되지 않은 경우 필드에 대한 접근이 언제 제한되는지에 대해 설명하는 § 8.3.3을 참조하십시오.
클래스 변수와 관련된 몇 가지 규칙이 나열됩니다.
변수에
f
interface로C
다음 경우 컴파일 시 오류입니다.
X = X + 1
이러한 규칙에 의해 검출되지만 메서드접근은 검출되지 않습니다. 이 들어 과 같은 예를 제시합니다.
메서드에 의한 액세스는 이 방법으로 체크되지 않으므로 다음과 같습니다.
class Z { static int peek() { return j; } static int i = peek(); static int j = 1; } class Test { public static void main(String[] args) { System.out.println(Z.i); } }
는 다음과 같은 출력을 생성합니다.
0
" "의
i
"peek" 값에 .j
전에j
는 변수 이니셜라이저에 의해 초기화되어 있으며, 이 시점에서도 디폴트값(default 4.12.5)이 유지됩니다.
기말고사와는 아무 상관 없어
인스턴스 또는 클래스 수준이기 때문에 아직 할당되지 않은 경우 기본값을 유지합니다.이 이 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★.0
을 사용하다
접속하는 X
하지 않고 합니다.0
.
벌레가 아니라.
의 첫 했을 때scale
라고 하는 것은, 라고 합니다.
private static final long X = scale(10);
.return X * value
X
에는 아직 않기 이 할당되어 있지 않습니다.long
, 하다,0
를 참조해 주세요.
에, 그 행은 「」로 됩니다.X * 10
ㅇㅇㅇ.0 * 10
, 「」입니다.0
.
이것은 버그가 아닙니다.단순히 말하면 불법적인 형식의 전방 참조가 아닙니다.그 이상은 아닙니다.
String x = y;
String y = "a"; // this will not compile
String x = getIt(); // this will compile, but will be null
String y = "a";
public String getIt(){
return y;
}
사양서에 의해 허가되어 있습니다.
예를 들어 다음과 같이 일치합니다.
private static final long X = scale(10) + 3;
에 대해 앞으로 참조하고 있습니다.scale
는 앞에서와 같이 인 "를 얻을 수 있습니다.X
말씀드리지만,은 명세서에 되고 있기 되어 있지 않기 때문에), 잘 합니다.
클래스 레벨 멤버는 클래스 정의 내의 코드로 초기화할 수 있습니다.컴파일된 바이트 코드는 클래스 멤버를 인라인으로 초기화할 수 없습니다.(인스턴스 멤버도 마찬가지로 처리되지만 이는 제공된 질문과는 관련이 없습니다.)
다음과 같은 것을 쓸 때:
public class Demo1 {
private static final long DemoLong1 = 1000;
}
생성되는 바이트 코드는 다음과 같습니다.
public class Demo2 {
private static final long DemoLong2;
static {
DemoLong2 = 1000;
}
}
초기화 코드는 클래스 로더가 클래스를 처음 로드할 때 실행되는 정적 이니셜라이저 내에 배치됩니다.이 지식을 바탕으로 원본 샘플은 다음과 같습니다.
public class RecursiveStatic {
private static final long X;
private static long scale(long value) {
return X * value;
}
static {
X = scale(10);
}
public static void main(String[] args) {
System.out.println(scale(5));
}
}
- JVM은 RecursiveStatic을 jar의 진입점으로 로드합니다.
- 클래스 정의가 로드되면 클래스 로더는 정적 이니셜라이저를 실행합니다.
- 합니다.
scale(10)
static final
들 fieldX
. scale(long)
되어 있는 는 초기화되지 않은 인 "되지 않은 값"을 .X
길쭉하다- 「」의 값
0 * 10
is is is is is 。X
클래스 로더가 완료됩니다. - 은 JVM을 호출하는 의 메인 합니다.
scale(5)
에 지금 된 를 곱한 것입니다.X
은 0을 하고 0은 0을 반환한다
최종 " " " "X
1회만 되며, 1회분의 합니다.final
키워드를 지정합니다. 3으로 됩니다.0 * 10 + 3
은 값입니다.3
은 '할 수 없다'는합니다.3 * 5
은 값입니다.15
.
개체의 초기화되지 않은 필드를 읽으면 컴파일 오류가 발생합니다.Java에게는 유감스럽게도 그렇지 않습니다.
그 근본적 이유는 객체가 어떻게 인스턴스화되고 구성되는지에 대한 정의 속에 '숨겨져 있다'고 생각합니다만, 저는 그 표준의 자세한 내용은 모릅니다.
어떤 의미에서, 최종은 이 문제로 인해 명시된 목적을 달성하지 못하기 때문에 잘못 정의되어 있다.하지만, 만약 여러분의 모든 수업이 적절하게 쓰여진다면, 여러분은 이 문제를 겪지 않을 것입니다.즉, 모든 필드는 항상 모든 생성자에 설정되며 생성자 중 하나를 호출하지 않으면 개체가 생성되지 않습니다.시리얼라이제이션라이브러리를사용해야하기전까지는그것은자연스럽다고생각됩니다.
언급URL : https://stackoverflow.com/questions/49365930/is-final-ill-defined
'programing' 카테고리의 다른 글
왜 Java의 Iterator는 반복가능하지 않은가? (0) | 2022.08.14 |
---|---|
문자열에서 단일 문자를 제거하는 방법 (0) | 2022.08.14 |
[Vue warn] :중복된 키가 검출되었습니다.x 。이로 인해 업데이트 오류가 발생할 수 있습니다. (0) | 2022.08.14 |
VueJ 팝업 "대화 상자"에 매개 변수 전달 (0) | 2022.08.14 |
Android에서 인터넷 접속을 확인하는 방법은?InetAddress가 타임아웃되지 않음 (0) | 2022.08.14 |