Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Was läuft bei diesem Code schief?

2 views
Skip to first unread message

Anton Behr

unread,
Oct 2, 2003, 10:23:19 AM10/2/03
to
Hallo,

wie lässt sich die NullpointerException beim Ausführen des unten
stehenden Codes erklären?

<code>
public class Test {

private String test = "Hallo!";

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

public Test() {
new TestInner();
}

public class TestInnerSuper {

public TestInnerSuper() {
showHallo();
}

public void showHallo() {
}

}

public class TestInner extends TestInnerSuper {

public void showHallo() {
System.out.println(test);
}

}

}
</code>

Resultat:
java.lang.NullPointerException
at Test.access$0(Test.java:3)
at Test$TestInner.showHallo(Test.java:27)
at Test$TestInnerSuper.<init>(Test.java:16)
at Test$TestInner.<init>(Test.java:24)
at Test.<init>(Test.java:10)
at Test.main(Test.java:6)
Exception in thread "main"

Für schlüssige Erklärungen wäre ich dankbar.

Grüße
Anton

Wolf Martinus

unread,
Oct 2, 2003, 10:42:44 AM10/2/03
to
Anton Behr wrote:
> wie lässt sich die NullpointerException beim Ausführen des unten
> stehenden Codes erklären?

Für mich erstmal gar nicht, ich bekomm die nämlich nicht.
Welches JDK?

Wolf


--
For a private reply to reiss...@gmx.de make sure to provide an
interesting subject line.

Sascha Broich

unread,
Oct 2, 2003, 10:43:29 AM10/2/03
to
Anton Behr schrieb:

> private String test = "Hallo!";
>
> public class TestInner extends TestInnerSuper {
> public void showHallo() {
> System.out.println(test);
^^^^
Hier merkt Eclipse bei mir an, daß der Zugriff auf das private Field nur
mittels 'synthetic accessor' möglich ist.

Abgesehen davon läuft das Programm bei mir durch und gibt "Hallo!" aus.

>
> Resultat:
> java.lang.NullPointerException
> at Test.access$0(Test.java:3)
> at Test$TestInner.showHallo(Test.java:27)
> at Test$TestInnerSuper.<init>(Test.java:16)
> at Test$TestInner.<init>(Test.java:24)
> at Test.<init>(Test.java:10)
> at Test.main(Test.java:6)
> Exception in thread "main"

Da scheint es wohl keinen OutputStream System.out zu geben.
In was für einer Umgebung läuft dein Programm?


Sascha Broich
--
Weil, so schließt er messerscharf,
nicht sein kann, was nicht sein darf.
(Die unmögliche Tatsache, C. Morgenstern)
--

Patrick Roemer

unread,
Oct 2, 2003, 11:00:04 AM10/2/03
to
Hallo,

Anton Behr wrote:

> wie lässt sich die NullpointerException beim Ausführen des unten
> stehenden Codes erklären?
>
> <code>
> public class Test {
>
> private String test = "Hallo!";
>
> public static void main(String[] args) {
> new Test();
> }
>
> public Test() {
> new TestInner();
> }
>
> public class TestInnerSuper {
>
> public TestInnerSuper() {
> showHallo();
> }
>
> public void showHallo() {
> }
> }
>
> public class TestInner extends TestInnerSuper {
>
> public void showHallo() {
> System.out.println(test);
> }
> }
> }
> </code>

Der Zugriff von der inneren Klasse auf Members der enthaltenden Klasse
wird ueber eine synthetische Membervariable der inneren Klasse, die eben
das enthaltende Exemplar der aeusseren Klasse referenziert, geregelt.

Da der Aufruf von showHallo() auftritt, bevor der Konstruktor von
TestInner durchlaufen ist (naemlich waehrend des Konstruktorlaufs der
inneren Superklasse), wird diese Referenz noch null sein, wie Du durch
Ausgabe von Test.this in showHallo() ueberpruefen kannst. Und wenn diese
Referenz null ist, gibt es eben beim Versuch, ihr Zugriff auf eine
Membervariable abzuringen, eine NullPointerException.

Ich nehme aber an, dass es der Compiler/VM-Implementierung obliegt, in
welcher Reihenfolge sie das zusammenbaut, IOW die NPE muss IMO nicht
unbedingt auftreten. Sicher bin ich mir aber nicht, das muesste ich auch
noch mal nachlesen.

Die Moral von der Geschichte ist auf jeden Fall: Verlasse Dich in
Konstruktoren nicht auf in Subklassen zu implementierende Methoden,
insbesondere, wenn davon auszugehen ist, dass diese auf eigenen
(Subklassen-)Zustand zugreifen.

Viele Gruesse,
Patrick

Peter Boheme

unread,
Oct 2, 2003, 11:08:18 AM10/2/03
to
Hi,

[Code]

Noch eine Anmerkung.
Es ist gefaehrlich in Kontruktoren virtuelle Methoden aufzurufen.
Denn wenn die superKlasse eine virtuelle Methode im Konstruktor aufruft, dann sind zum Aufrufzeitpunkt der Methode die Fields der
abgeleiteten Klasse dort eventuell nicht initialisiert.
Beispiel:

public class Test
{

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

public Test()
{
new TestInner();
}
public class TestInnerSuper
{
public TestInnerSuper()
{
showHallo();
}

public void showHallo()
{
}
}
public class TestInner extends TestInnerSuper
{

private String _test = "Hallo";

public TestInner()
{
super();
_test = "Hallo2";
}

public void showHallo()
{
System.out.println(_test);
}
}
}


Gibt _null_ aus und nicht Hallo noch Hallo2 !

Pete

Stefan Matthias Aust

unread,
Oct 2, 2003, 11:05:16 AM10/2/03
to
Ich weiss nicht, ob es genau in diesem Fall zutrifft, aber bis zur
Version 1.4.2 macht javac etwas bei der Initialisierung von sich
gegenseitig referenzierenden Klassen falsch. Versuch mal das Programm
mit JDK 1.4.2 under der Option -target 1.4.2 zu übersetzen. Oder nimm
Eclipse, die haben (so behaupten sie) einen Compiler, der sich besser
als die Java-Sprachspezifikation hält.

bye
--
Stefan Matthias Aust // "Ist es normal, nur weil alle es tun?" -F4

Patrick Roemer

unread,
Oct 2, 2003, 11:30:38 AM10/2/03
to
Hallo,

Stefan Matthias Aust wrote:

> Ich weiss nicht, ob es genau in diesem Fall zutrifft, aber bis zur
> Version 1.4.2 macht javac etwas bei der Initialisierung von sich
> gegenseitig referenzierenden Klassen falsch. Versuch mal das Programm
> mit JDK 1.4.2 under der Option -target 1.4.2 zu übersetzen. Oder nimm
> Eclipse, die haben (so behaupten sie) einen Compiler, der sich besser
> als die Java-Sprachspezifikation hält.

Mit Eclipse 2.1 dasselbe Problem; mit JDK1.4.2 laeuft's.

Aber ich bin mir nicht sicher, ob die Exception wirklich 'falsch' ist.
Warum sollte denn der synthetische Zugriff der Subklasse auf die
umgebende Klasse schon zur Verfuegung stehen, wenn gerade erst der
Konstruktor der Superklasse ausgefuehrt wird?

Viele Gruesse,
Patrick

Stefan Matthias Aust

unread,
Oct 2, 2003, 11:38:14 AM10/2/03
to
Patrick Roemer wrote:

> Mit Eclipse 2.1 dasselbe Problem; mit JDK1.4.2 laeuft's.

Eclipse 2.1 ist alt :) Zudem, mit 1.3 oder 1.4 Kompatibilität?

> Aber ich bin mir nicht sicher, ob die Exception wirklich 'falsch' ist.

Ich habe mir ehrlich gesagt den Code nicht angeschaut sondern nur geraten.

Patrick Roemer

unread,
Oct 2, 2003, 12:11:28 PM10/2/03
to
Hallo,

Stefan Matthias Aust wrote:

>> Mit Eclipse 2.1 dasselbe Problem; mit JDK1.4.2 laeuft's.
>
> Eclipse 2.1 ist alt :) Zudem, mit 1.3 oder 1.4 Kompatibilität?

Treffer. ;) Bei Eclipse 2.1 und 3.0 laeuft es jeweils mit 1.4er
Einstellung durch.

Mir kommt's auf jeden Fall komisch vor. Der Deutlichkeit halber mal
umgestellt:

public class Test {
private String test = "Hallo!";

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

public Test() {
new TestInner();
}

public class TestInner extends TestInnerSuper {
public void showHallo() {
System.out.println(test);
}
}
}

abstract class TestInnerSuper {
public TestInnerSuper() {
showHallo();
}

protected abstract void showHallo();
}

Und da wuerde ich schon denken, dass ich damit rechnen muss, dass die
Referenz auf die aeussere Klasse erst im Konstruktor von TestInnerSuper
'erzeugt' wird. Oder uebersehe ich irgendwas, was deren Vorhandensein
schon vorher erzwingt?

Viele Gruesse,
Patrick

Sven Köhler

unread,
Oct 2, 2003, 1:59:31 PM10/2/03
to
> wie lässt sich die NullpointerException beim Ausführen des unten
> stehenden Codes erklären?

Du greifst auf den Element zu, bevor es durch den Konstruktur
initialisiert wurd. Dieses Problem kennt Sun bereits, und es wurde
breites behoben. Bitte arbeite beim compilen mit "-target 1.3" oder
sogar "-target 1.4".

Das Problem ruht daher, dass Sun der Meinung war, dass vor dem Aurfuf
von super() nichts initialisiert werden dürfe - auch nicht die Referenz
auf die die umgebende Objekt Instanz.

In deinem Beispiel greift du auf Test.this.test zu:
System.out.println(test);
Test.this ist ohne target 1.3/1.4 eben null.

Paul Ebermann

unread,
Oct 2, 2003, 4:44:11 PM10/2/03
to
"Patrick Roemer" skribis:

> Mir kommt's auf jeden Fall komisch vor. Der Deutlichkeit halber mal
> umgestellt:
>
> public class Test {
> private String test = "Hallo!";
>
> public static void main(String[] args) {
> new Test();
> }
>
> public Test() {
> new TestInner();
> }
>
> public class TestInner extends TestInnerSuper {
> public void showHallo() {
> System.out.println(test);

Noch etwas deutlicher:

System.out.println((Test.this).test);

> }
> }
> }
>
> abstract class TestInnerSuper {
> public TestInnerSuper() {
> showHallo();
> }
>
> protected abstract void showHallo();
> }
>
> Und da wuerde ich schon denken, dass ich damit rechnen muss, dass die
> Referenz auf die aeussere Klasse erst im Konstruktor von TestInnerSuper
> 'erzeugt' wird.

... nach dem Konstruktor ...

> Oder uebersehe ich irgendwas, was deren Vorhandensein
> schon vorher erzwingt?

http://developer.java.sun.com/developer/bugParade/bugs/4030374.html


Paul

Patrick Roemer

unread,
Oct 2, 2003, 10:05:42 PM10/2/03
to
Hallo,

Paul Ebermann wrote:

> http://developer.java.sun.com/developer/bugParade/bugs/4030374.html

Das hoert sich fuer mich aber eher so an, als haette es einen Bug
gegeben, der das 'erwartete' Verhalten per se vereitelt haette, nicht,
als sei das 'unerwartete' Verhalten ein Bug:

| Fixing this bug required initializing the synthetic member this$0
| before calling the superclass constructor. Although specifically
| allowed by the VM specification (4.8.2), the verifier failed to allow
| it prior to 1.4.

und

| This might not be a compiler bug but might be a flaw in how inner
| classes are defined.

sowie natuerlich der (einzig sinnvolle) 'Workaround'

| Don't call subclass methods from the superclass constructor.

Ist das Werfen einer NPE jetzt 'falsch' (wenn ja, warum?), oder ist es
nur endlich akzeptabel, *keine* NPE zu werfen?

Viele Gruesse,
Patrick

Paul Ebermann

unread,
Oct 2, 2003, 10:34:47 PM10/2/03
to
"Patrick Roemer" skribis:

> Paul Ebermann wrote:
>
> > http://developer.java.sun.com/developer/bugParade/bugs/4030374.html
>
> Das hoert sich fuer mich aber eher so an, als haette es einen Bug
> gegeben, der das 'erwartete' Verhalten per se vereitelt haette, nicht,
> als sei das 'unerwartete' Verhalten ein Bug:
[...]

> Ist das Werfen einer NPE jetzt 'falsch' (wenn ja, warum?), oder ist es
> nur endlich akzeptabel, *keine* NPE zu werfen?

Durch die Änderung in der VM (Verifier) ist es
jetzt möglich, Code zu generieren, der keine NPE wirft -
und die neuen Compiler-Varianten tun das entsprechend
auch.

So wie ich die Meinungen in der Seite verstanden habe,
meinen sie, dass laut LangSpec hier eine NPE nichts
zu suchen hat - ich habe das jetzt nicht gründlich
untersucht (Zeitmangel), aber finde es nachvollziehbar.

In der LangSpec steht ja schließlich nicht, dass das
Outer.this erst nach der erfolgten Exemplar-Initialisierung
gültig ist, oder? Es ist auch nicht als Exemplar-Variable
eingestuft ...


Paul

0 new messages