System.out
로 선언됩니다 public static final PrintStream out
.
하지만 전화 할 수 있습니다 System.setOut()
를 재 할당 .
어? 만약 그렇다면 이것이 어떻게 가능합니까?final
합니까?
(같은 점이 System.in
및에System.err
)
그리고 더 중요한 것은 공개 정적 최종 필드를 변경할 수 있다면 보장 (있는 경우)이 final
제공 하는 한 이것이 무엇을 의미 합니까? (나는 System.in/out/err이 final
변수로 작동한다는 것을 깨닫지 못했고 예상하지 못했습니다 )
답변
일반적으로 최종 정적 필드는 수정할 수 없습니다. 그러나
System.in
,System.out
및System.err
레거시 이유, 방법에 의해 변경 될 수 있어야, 최종 정적 필드이다System.setIn
,System.setOut
하고System.setErr
. 이러한 필드 를 일반 최종 필드와 구별하기 위해 쓰기 금지 된 필드라고합니다.컴파일러는 이러한 필드를 다른 최종 필드와 다르게 처리해야합니다. 예를 들어, 일반 최종 필드의 읽기는 동기화에 “면역”됩니다. 잠금 또는 휘발성 읽기와 관련된 장벽은 최종 필드에서 읽는 값에 영향을 미치지 않습니다. 쓰기 방지 된 필드의 값이 변경되는 것으로 보일 수 있으므로 동기화 이벤트가 영향을 미칩니다. 따라서 의미론은 해당 사용자 코드가
System
클래스 에 있지 않는 한 이러한 필드를 사용자 코드로 변경할 수없는 일반 필드로 처리하도록 지시합니다 .
그런데 실제로 final
필드를 호출 setAccessible(true)
하거나 Unsafe
메서드 를 사용하여 리플렉션을 통해 필드를 변경할 수 있습니다 . 이러한 기술은 Hibernate 및 기타 프레임 워크 등에서 deserialization 중에 사용되지만 한 가지 제한이 있습니다. 수정 전에 최종 필드의 값을 본 코드는 수정 후 새로운 값을 볼 수 있다는 보장이 없습니다. 문제의 필드에서 특별한 점은 컴파일러에 의해 특별한 방식으로 처리되기 때문에 이러한 제한이 없다는 것입니다.
답변
Java는 네이티브 메서드를 사용하여 setIn()
, setOut()
및 setErr()
.
내 JDK1.6.0_20에서 setOut()
다음과 같이 보입니다.
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
...
private static native void setOut0(PrintStream out);
여전히 final
변수를 “일반적으로”재 할당 할 수 없으며이 경우에도 필드를 직접 재할 당하지 않습니다 (예 : ” System.out = myOut
“을 컴파일 할 수 없음 ). 기본 메소드는 일반 Java에서 단순히 할 수없는 일을 허용합니다. 이는 기본 라이브러리를 사용하기 위해 애플릿에 서명해야하는 요구 사항과 같은 기본 메소드에 제한 사항이있는 이유를 설명합니다.
답변
Adam이 말한 것을 확장하기 위해 다음은 impl입니다.
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
setOut0은 다음과 같이 정의됩니다.
private static native void setOut0(PrintStream out);
답변
구현에 따라 다릅니다. 마지막 것은 절대 변경되지 않을 수 있지만 실제 출력 스트림에 대한 프록시 / 어댑터 / 데코레이터가 될 수 있습니다. 예를 들어 setOut은 out 멤버가 실제로 쓰는 멤버를 설정할 수 있습니다. 그러나 실제로는 기본적으로 설정됩니다.
답변
out
System 클래스에서 final로 선언 된 클래스 수준 변수입니다. 아래 방법 중 어느 것이 로컬 변수입니다. 우리는 클래스 레벨을 실제로이 메소드에 전달하는 곳이 아닙니다.
public static void setOut(PrintStream out) {
checkIO();
setOut0(out);
}
위 방법의 사용법은 다음과 같습니다.
System.setOut(new PrintStream(new FileOutputStream("somefile.txt")));
이제 데이터가 파일로 전환됩니다. 이 설명이 이해되기를 바랍니다.
따라서 최종 키워드의 목적을 변경하는 데 네이티브 메서드 또는 반사의 역할이 없습니다.
답변
어떻게하면 소스 코드를 다음과 같이 살펴볼 수 있습니다 java/lang/System.c
.
/*
* The following three functions implement setter methods for
* java.lang.System.{in, out, err}. They are natively implemented
* because they violate the semantics of the language (i.e. set final
* variable).
*/
JNIEXPORT void JNICALL
Java_java_lang_System_setOut0(JNIEnv *env, jclass cla, jobject stream)
{
jfieldID fid =
(*env)->GetStaticFieldID(env,cla,"out","Ljava/io/PrintStream;");
if (fid == 0)
return;
(*env)->SetStaticObjectField(env,cla,fid,stream);
}
...
즉, JNI는 “속임수”를 사용할 수 있습니다. ; )
답변
나는 setout0
지역 수준 변수를 수정 하고 있다고 생각 하며 out
클래스 수준 변수를 수정할 수 없습니다 out
.