java: System "końcowy".Na Zewnątrz, System.in i System.err?

System.out jest zadeklarowana jako public static final PrintStream out.

Ale możesz zadzwonić System.setOut() żeby go przydzielić.

Huh? Jak to możliwe, jeśli to final?

(ten sam punkt dotyczy System.in i System.err)

I co ważniejsze, jeśli możesz zmutować publiczne statyczne pola końcowe, co to oznacza jeśli chodzi o Gwarancje (jeśli istnieją), które final daje Ci? (Nigdy nie zdawałem sobie sprawy, ani nie spodziewałem się System.in/out/err final zmienne)

 75
Author: Stephen C, 2011-05-10

6 answers

JLS 17.5.4 Write Protected Fields :

Zazwyczaj końcowe pola statyczne nie mogą być modyfikowane. Jednakże System.in, System.out, i System.err są końcowymi polami statycznymi, które ze względów historycznych muszą być dozwolone do zmiany za pomocą metod System.setIn, System.setOut i System.setErr. Te pola nazywamy chronionymi przed zapisem , aby odróżnić je od zwykłych pól końcowych.

Kompilator musi traktować te pola inaczej niż inne pola końcowe. Na przykład, odczyt zwykłego pola końcowego jest "odporny" na synchronizację: bariera zaangażowana w blokadę lub Lotny odczyt nie musi wpływać na to, jaka wartość jest odczytywana z pola końcowego. Ponieważ wartość pól zabezpieczonych przed zapisem może się zmieniać, zdarzenia synchronizacji powinny mieć na nie wpływ. Dlatego semantyka nakazuje, aby te pola były traktowane jako zwykłe pola, których nie można zmienić za pomocą kodu użytkownika, chyba że kod użytkownika znajduje się w klasie System.

Przy okazji, właściwie możesz mutować final pola poprzez odbicie, wywołując na nich setAccessible(true) (lub używając metod Unsafe). Takie techniki są używane podczas deserializacji, przez Hibernate i inne frameworki, itd., ale mają jedno ograniczenie: kod, który widział wartość ostatniego pola przed modyfikacją, nie ma gwarancji, że zobaczy nową wartość po modyfikacji. Wyjątkowość tych pól polega na tym, że są one wolne od tego ograniczenia, ponieważ są traktowane w specjalny sposób przez kompilator.

 55
Author: axtavt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-16 07:13:36

Java używa natywnej metody do implementacji setIn(), setOut() i setErr().

Na moim JDK1.6.0_20, setOut() wygląda tak:
public static void setOut(PrintStream out) {
    checkIO();
    setOut0(out);
}

...

private static native void setOut0(PrintStream out);

Nadal nie można "normalnie" przypisać zmiennych final, a nawet w tym przypadku nie można bezpośrednio przypisać pola (tzn. nadal nie można skompilować " System.out = myOut"). Metody natywne pozwalają na pewne rzeczy, których po prostu nie można zrobić w zwykłej Javie, co wyjaśnia, dlaczego istnieją ograniczenia dotyczące metod natywnych, takie jak wymóg podpisywania apletu w celu użyj natywnych bibliotek.

 28
Author: Adam Batkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-10 14:28:33

Aby rozszerzyć to, co powiedział Adam, oto impl:

public static void setOut(PrintStream out) {
    checkIO();
    setOut0(out);
}

I setOut0 jest zdefiniowane jako:

private static native void setOut0(PrintStream out);
 7
Author: Amir Raminfar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-10 14:21:15

Zależy od implementacji. Ostatni może się nigdy nie zmienić, ale może to być proxy/adapter/dekorator dla rzeczywistego strumienia wyjściowego, setOut może na przykład ustawić element, do którego dany członek rzeczywiście pisze. W praktyce jednak jest ustawiony natywnie.

 6
Author: vickirk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-10 14:22:09

The out która jest zadeklarowana jako końcowa w klasie systemowej jest zmienną poziomu klasy. gdzie jako out, który jest w poniższej metodzie jest zmienną lokalną. nie jesteśmy tam, gdzie przechodzenie poziomu klasy, który jest w rzeczywistości ostatnim w tej metodzie

public static void setOut(PrintStream out) {
  checkIO();
  setOut0(out);
    }

Użycie powyższej metody jest jak poniżej:

System.setOut(new PrintStream(new FileOutputStream("somefile.txt")));

Teraz dane zostaną przekierowane do pliku. mam nadzieję, że to wyjaśnienie ma sens.

Więc żadna rola metod rodzimych czy refleksji tutaj w zmienianiu cel ostatniego słowa kluczowego.

 1
Author: Krish Krishna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-13 16:48:45

Jeśli chodzi o sposób, możemy spojrzeć na kod źródłowy 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);
}

...
Innymi słowy, JNI może "oszukiwać". ; )
 0
Author: Radiodef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-05 19:20:45