Java Quiz

Noch eins: Was ist die Ausgabe dieses Programms? (Falls man das noch erwähnen muss: Ohne Ausprobieren :rolleyes: )

class SomeClass
{
    public SomeClass(DangerousSelfReference dangerousSelfReference)
    {
        System.out.println("State: ");
        System.out.println("   a: "+dangerousSelfReference.getA());
        System.out.println("   b: "+dangerousSelfReference.getB());
        System.out.println("   c: "+dangerousSelfReference.getC());
        System.out.println("   d: "+dangerousSelfReference.getD());
        System.out.println(" ref: "+dangerousSelfReference.getRef());
    }
}

public class DangerousSelfReference
{
    public static void main(String[] args)
    {
        DangerousSelfReference d = new DangerousSelfReference();
    }

    private String a;
    private String b = "b";
    private final SomeClass ref = new SomeClass(this);
    private final String c = "c";
    private String d = "d";

    DangerousSelfReference()
    {
        a = "a";
    }

    String getA()
    {
        return a;
    }
    String getB()
    {
        return b;
    }
    String getC()
    {
        return c;
    }
    String getD()
    {
        return d;
    }
    SomeClass getRef()
    {
        return ref;
    }
}

Ist das zu schwer? Was seid ihr denn für loser?! :twisted:

:o)

[SPOILER]
In der Main wird eine Instanz erzeugt
bevor der Konstruktor ausgefuehrt wird werden die Class-members initialisiert
das ganze wird von oben nach unten durchgemacht.
daher ist a null
b bekommt den Wert b
nun wird ref erzeugt und der Konstruktor augerufen
daher kann zu diesem zeitpunk eigentlich nur b einen Wert haben und alles andere muss null sein.
vl dass c auch schon den Wert c bekommen hat, da es final ist, bin mir aber nicht sicher
[/SPOILER]

Würde mal spontan auf:
a: null
b: b
c: null
d: null
ref: null
tippen. a, b, d und ref sind relativ klar. c könnte eventuell noch “c” sein wenn das inlined wird.

Wußt’ ich’s doch, Provokation funktioniert immer :stuck_out_tongue:


State: 
   a: null
   b: b
   c: c
   d: null
 ref: null

Das interessante ist dabei, dass

  • b schon initialisiert ist (weil es vor dem „new SomeClass(this)“ steht) aber d noch nicht
  • c schon initlialisiert ist (weil es final und der Wert zur Compilezeit bekannt ist), aber d noch nicht

Dass die „in Lesereihenfolge“ initialisiert werden, hätte man raten können. Dass das „final“ dafür sorgt, dass diese Reihenfolge aufgebrochen wird, ist aber ein ziemlich fieses Detail…

“dass diese Reihenfolge aufgebrochen wird”
inwiefern jetzt? wie genau läuft das also ab?

Ohje… jetzt hab’ ich durch die letzte Antwort die Anforderungen für so eine “Erklärung” in kaum handabbare Höhen geschraubt… :eek: Aber immerhin wurde mir dadurch eine Ungenauigkeit bewußt (ist oben schon entsprechend ergänzt).

Es schwierig, hier “mit dem Finger auf die entsprechende Stelle zu zeigen”. In der JLS, 13.1. The Form of a Binary, ist erwähnt: 3. References to fields that are constant variables (§4.12.4) are resolved at compile time to the constant value that is denoted.

Das, in Kombination mit 12.5. Creation of New Class Instances, Punkt 4, **Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. ** (was noch vor dem Ausführen des Konstruktors passiert), ist wohl die Erklärung für das Verhalten, das in diesem Beispiel nochmal ausführlicher demonstriert ist:

class InitializationTestUser
{
    public InitializationTestUser(InitializationTest initializationTest)
    {
        System.out.println("State: ");
        System.out.println("  am: "+initializationTest.am);
        System.out.println("   a: "+initializationTest.a);
        System.out.println("  ma: "+initializationTest.ma);

        System.out.println("  bm: "+initializationTest.bm);
        System.out.println("   b: "+initializationTest.b);
        System.out.println("  mb: "+initializationTest.mb);

        System.out.println("  cm: "+initializationTest.cm);
        System.out.println("   c: "+initializationTest.c);
        System.out.println("  mc: "+initializationTest.mc);

        System.out.println("  dm: "+initializationTest.dm);
        System.out.println("   d: "+initializationTest.d);
        System.out.println("  md: "+initializationTest.md);
    }
}
 
public class InitializationTest
{
    public static void main(String[] args)
    {
        InitializationTest t = new InitializationTest();
    }
 
    String am = ma();
    String a = "a";
    String ma = ma();
    final String bm = mb();
    final String b = "b";
    final String mb = mb();
    final InitializationTestUser ref = new InitializationTestUser(this);
    String cm = mc();
    String c = "c";
    String mc = mc();
    final String dm = md();
    final String d = "d";
    final String md = md();
 
    private String ma()
    {
        return a;
    }
    private String mb()
    {
        return b;
    }
    private String mc()
    {
        return c;
    }
    private String md()
    {
        return d;
    }
}```


State:
am: null
a: a
ma: a
bm: b
b: b
mb: b
cm: null
c: null
mc: null
dm: null
d: d
md: null



[ul]
[li] `am` ist null, weil die Initialisierung vor der von `a` erfolgt[/li][li] `a` ist nicht null, weil die Ausgabe nach der Initialisierung erfolgt[/li][li] `ma` ist nicht null, weil die Initialisierung nach der von `a` erfolgt[/li][li] `bm` ist nicht null, weil die Initialisierung von `b` schon zur Compilezeit stattfindet[/li][li] `b` ist nicht null, weil die Initialisierung schon zur Compilezeit stattfindet[/li][li] `mb` ist nicht null - klar ;)[/li][li] `cm` ist null, weil die Ausgabe vor der Initialisierung erfolgt[/li][li] `c` ist null, weil die Ausgabe vor der Initialisierung erfolgt[/li][li] `mc` ist null, weil die Ausgabe vor der Initialisierung erfolgt[/li][/ul]

Eigentlich müßte man wohl noch genauer differenzieren zwischen den Fields selbst und ihren Referenzierungen, aber zumindest scheint es so plausibel...

Damit’s nicht einschläft, mal etwas einfacheres:

public class FluentCallTest
{
    public static void main(String[] args) 
    {
        das().funktioniert().nicht();
    }    
    static FluentCallTest das()
    {
        System.err.print("das ");
        return null;
    }
    static FluentCallTest funktioniert()
    {
        System.err.print("funktioniert ");
        return null;
    }
    FluentCallTest nicht() 
    {
        System.err.println("nicht");
        return null;
    }
}

Was ist die Ausgabe?

Ausgabe mMn:
[spoiler]das funktioniert nicht
Begründung: statische Methoden können auch auf einer null-Referenz aufgerufen werden.[/spoiler]

[spoiler]das funktioniert und dann eine NPE. Weil funktioniert() static ist, ist der Rückgabewert von das() egal.[/spoiler]

@cmrudolph hat Recht. Das weggelassene „static“ beim „nicht“ (oder das vorhandene bei den anderen beiden) ist vielleicht ein bißchen gemein, aber … das funktioniert :smiley:

So ich mach mal weiter … ka ob wir das hier oder im alten Forum schon mal hatten… geht das?.. wenn ja, was wird ausgegeben ?

	public static void main(String[] args) {
		try{
			giveMeAException();
		}catch(Exception e){
			System.out.println(e.getMessage());
		}
	}

	private static void giveMeAException() {
		try{
			throw new IllegalArgumentException("try");
		}catch(Exception e){
			throw new IllegalArgumentException("catch");
		}finally{
			throw new IllegalArgumentException("finally");
		}
		
	}
}```

finally wird ausgegeben. So lassen auch return Werte überschreiben:

int getNumber()
{
  try {
    return 1;  
  } finally {
    return 42;
  }
}

Und wenn man beides kombiniert, kommt eine fiese Frage raus, bei der ich sicherheitshalber eben selbst nochmal getestet habe, was denn die Ausgabe ist :smiley:

public class FinallyTest
{
    private static int i=0;
    
    public static void main(String[] args)
    {
        int result = call();
        System.out.println(result);
    }

    private static int call()
    {
        try
        {
            return i++;
        }
        finally
        {
            return ++i;
        }

    }
}

Nach meinem Verständis müsste 2 herauskommen,

zuerst wird im try 0 zurückgegeben und anschließend um 1 erhöht. und ich glaub eher , dass jetzt erst der finally block dran kommt und daher den Wert 1 noch einmal erhöht und den vorherigen Rückgabe wert mit 2 “überschreibt”

Jo stimmt, … es ging darum dass er das “i++” wirklich ausführt und schon alles vorbereitet, um das zurückzugeben, aber das finally wirklich im letzten Moment reingrätscht und alles ändert…

Noch ein kleines quiz, inspiriert von einer anderen Frage:

public class CompareTest
{
    public static void main(String args[])
    {
        float a = 0;
        float b = 0;
        a += 0.7;
        b += 0.5;
        if (a < 0.7)
        {
            if (b < 0.5)
                System.out.println("Both smaller");
            else
                System.out.println("One smaller");
        }
        else
            System.out.println("None smaller");
    }
}

Was wird ausgegeben?

Interessant: Scheibt man die Initialisierung gleich in die Variablen (float a = 0.7f; float b = 0.5f), muss man den Code noch nicht mal laufen lassen. Da meckter Ecplise so schon über “Dead Code”.

Eigentlich kann ich mir kaum vorstellen, dass was anderes außer “None smaller” ausgegeben werden sollte. Denn trotz des Problems, dass Gleitkommazahlen sich nicht exakt abbilden lassen, wird doch in beiden Fällen derselbe “Rundungsfehler” gemacht, sodass < x auf jeden Fall falsch sein müsste.
Aber: so wie das Beispiel gestellt ist, kann es natürlich sein, dass durch die Addition was anderes in a steht als das, was bei dem Vergleich mit einer Konstanten verwendet wird.

Long story short: ich habe keine Ahnung.

Vom logischen her: None smaller
Vom überprüfen her: One smaller

Meine begründung schlicht und einfach die Darstellung von floatzahlen. Da dort ja immer ungenauigkeiten auftreten können nund 0,5 einfacher bzw genauer darstellbar ist als 0,7. Dadurch wird 0,7 eben nicht genau 0,7 sein. (Erklärung vllt falsch aber denke der Gedanke stimmt^^)

Btw: Ich hatte die hoffnung das strictfp was bringt aber ändert auch nichts.