Java Quiz

Wie ich es vermutetet.

Die Lösung und Erklärung zum nachlesen

fehl post bitte löschen…

[QUOTE=Bleiglanz]Erwarte ich nicht, da die meisten Abiturienten das Niveau der 10. Klasse ja nie erreichen - und auch sonst würde ich deinen Optimismus nicht teilen…

Why Can’''t Programmers… Program?[/QUOTE]

dort steht in einem Kommentar

[…] I once interviewed a candidate for a VBA job […] whom I asked to swap two variable contents without using a temp variable. It’s the standard a=a+b, b=a-b, a=a-b problem. His answer? Well, I can’t do it in VBA, but if you let me use Excel I can put the values in two cells and swap the cells’ contents using a third cell.

We hired the guy who said, well, „if they’re integers, then I’d do it by a=a|b, b=a^b, a=a^b. But I don’t know how to do it if they’re strings.“

die Pointe ist erstaunlich lau, da man gerade mit String das doch immer noch erreichen kann,
keine Ahnung ob in VBA, allgemein nach gleichen Schema, wenn auch ineffizient…, und mit null sicher noch lächerlich komplizierter,

um den Quiz etwas genüge zu tun (mir fiel kein besserer Platz zum Posten ein) mag sich das ein jeder ggfs. überlegen, ob mit oder ohne null

Ich frage mich, was diese Frage überhaupt soll: Es ist die Lösung für ein Problem, dass es nur auf assembler-ebene gibt, wo man ggf nicht genügend Register in der CPU hat. Bei allen anderen Programmiersprachen ist das erzeugen einer Temp-Variablen as sicht der Performanz vernachlässigbar. Dazu kommt noch, dass das wiederverwenden von Variablen innerhalb des selben Blocks prinzipiell vermieden werden sollte. Zudem: wie oft in eurer Programmierpaxis war es notwendig, den Inhalt zweier Variablen zu tauschen?

Der Kommentator beschwert sich über das Fehlen eines Skills, den ich bei meinen Kollegen gar nicht haben möchte. Den Code, den solche “Experten” schreiben kann nämlich keiner Lesen…

bye
TT

Was ist schneller?

Angenommen ihr habt diese beiden Methoden.

  for (int i = 0; i < n; i++) {
    log.fine("ms since 1970: " + System.currentTimeMillis());
  }
}

public void test2(int n) {
  for (int i = 0; i < n; i++) {
    log.fine(() -> "ms since 1970: " + System.currentTimeMillis());
  }
}```

Wenn die Methoden mit einem großen Integer, also größer 1000, aufgerufen werden und der Log-Level auf INFO gesetzt wurde.

Was ist schneller und warum?

Frage ist inspiriert aus einem Vortrag von Heinz Kabutz.

die Vermeidung von nicht benötigten Log-Nachrichten mit if davor ist ja etwas bekannt, da ist hier die Möglichkeit naheliegend

kann man den von dir so speziell namentlich geteaserten inspirierenden Vortrag auch irgendwo lesen?

Bestimmt ist die Sache mit der anonymen Funktion schneller (wenn es nicht überraschend wäre, hätte die Frage keinen Sinn)…

Die Frage ist etwas unklar. Entscheidend ist wohl: Welches log-level ist aktiv?

EDIT: Ach, bin wohl nicht ganz wach :o

:wink:

etwas schwieriger ist schon, die richtige Logger-API zu finden, den fehlenden Logger-holen-Code dafür zu ergänzen
(ok, soll man vielleicht gar nicht ausprobieren :wink: )

@SlaterB leider nicht zum Nachlesen, aber anschauen:

ab Minute 15.

Die Lösung:
[SPOILER]
Fall 1 könnte man um es deutlicher zu machen Umschreiben

String msg = "ms since 1970: " + time; // Ebenfalls sehr aufwendig
log.fine(msg); // kaum aufwand```


Fall 2 erstellt ein Lambda, welches allerdings aufgrund des Loglevels nie ausgeführt wird. Das aufwendige erstellen der Nachricht wird somit nie ausgeführt.
Das erstellen des Lambdas geschieht sehr ressourcensparend. Selbst ohne Lambdas, aber mit einem Anonymen-Supplier rennt das Ding immer noch.

Wenn die Nachrichten also aufgrund des Loglevels nicht verarbeitet werden ist hier Fall 2 deutlich besser.

Wenn die Nachrichten verarbeitet werden, dann spielt das erstellen des Lambdas kaum eine Rolle, da das schreiben der Lognachricht in eine Datei oder einen Stream, ca. 10 - 100 mal mehr Aufwand bedeutet.

Das Prüfen, des Loglevels in einem if funktioniert ebenfalls sehr performant, ist aber mit vergleichsweise viel Code verbunden.

Hier der Code den ich selbst zum rumspielen verwendet habe.

```package testlogging;

import java.util.function.Consumer;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

public class LogTester {

	private static final Logger log = Logger.getLogger(LogTester.class.getName());

	public void test1(int n) {
		for (int i = 0; i < n; i++) {
			log.fine("ms since 1970: " + System.currentTimeMillis());
		}
	}

	public void test2(int n) {
		for (int i = 0; i < n; i++) {
			log.fine(() -> "ms since 1970: " + System.currentTimeMillis());
		}
	}

	public static <T> void timed(Consumer<T> consumer, T n) {
		long start = System.currentTimeMillis();
		consumer.accept(n);
		long finished = System.currentTimeMillis();
		System.out.printf("Time needed %d ms%n", (finished - start));
	}

	public static void main(String[] args) {
		Level logLevel = Level.INFO;

		Logger logger = Logger.getLogger(LogTester.class.getName());

		logger.setLevel(logLevel);
		ConsoleHandler handler = new ConsoleHandler();

		handler.setLevel(logLevel);
		logger.addHandler(handler);

		LogTester tester = new LogTester();

		int n = 1_000_000;

		timed(tester::test1, n);
		timed(tester::test2, n);

	}
}```[/SPOILER]

War gerade auf Stackoverflow: Wie kann man eine Instanz dieser Klasse erstellen, OHNE die Klasse selbst zu ändern?

public final class Paradox {
    private final Paradox paradox;

    public Paradox(Paradox paradox) {
        this.paradox = paradox;
        if (this.paradox == null) throw new InceptionException();
    }

    private static class InceptionException extends RuntimeException {
        public InceptionException() {
            super("Paradox requires an instance of paradox to be instantiated");
        }
    }
}
      Paradox paradox = new Paradox(null);
} catch(RuntimeException e) {}```

Würde das funktionieren? Ich hab keine Ahnung, bin seit einem Monat im Urlaub :D
Die Frage ist doch ob eine Instanz erzeugt wird wenn der Konstruktor vorzeitig abgebrochen würde. Aber ich schätze mal schon, oder? Sonst wären mir schon öfter Probleme passiert.
In dem Fall müsste man genauer überlegt nichtmal das obige machen sondern einfach nur
```new Paradox(null);```
und die Instanz wäre erzeugt (und prompt vom GC wieder eingesammelt).
Eine RuntimeException beendet nicht notwendigerweise die JVM.

Oder ist Reflection erlaubt um das Objekt zu ändern? Die Klasse würde unberührt bleiben.

Läuft aber möglicherweise nur auf einer Oracle-vm:

[spoiler]```public static void main(String[] args) throws Exception {
ReflectionFactory rf = ReflectionFactory.getReflectionFactory();
Constructor<?> declaredConstructor = Object.class.getDeclaredConstructor();
Constructor<?> constr = rf.newConstructorForSerialization(Paradox.class, declaredConstructor);

Paradox cast = (Paradox) constr.newInstance();
System.out.println(cast);

Paradox theNormalWayOfLife = new Paradox(cast);
System.out.println("Its working: " + theNormalWayOfLife);

}```[/spoiler]

Paradox paradox= (Paradox) unsafe.allocateInstance(Paradox.class);

@TMII Nee, der Punkt ist ja, dass es den Konstruktor raushaut, und das Objekt demnach gar nicht wirklich existiert

@Unregistered Ja, das wäre meine Lösung gewesen
@Tomate_Salat Das kannte ich noch nicht - hab’ es mal hier verewigt :smiley:

@Marco13
Naja bei solchen Quizes denke ich immer viel zu sehr nach was eine gültige Lösung darstellen würde indem ich die Fragestellung genau auseinandernehme. :smiley:

new Paradox(null);
erzeugt in der Tat ein Objekt bzw. eine Instanz und dieses ist auch im Speicher verfügbar und abrufbar jedoch ohne Referenzen und entsprechend wird es dann auch vom GC eingesammelt.
Man kann das Objekt jedoch trotzdem manuell zurück ins Program holen und das stellt damit unter anderem auch ein Sicherheitsrisiko dar weil man damit die Exception umgehen kann.
Theoretisch existiert also eine Instanz so oder so für einen kurzen Moment, des Objektes und theoretisch kann man das Objekt auch aus dem Speicher abgreifen wenn der GC es noch nicht eingesammelt hat was unter anderem auch eine dokumentierte Sicherheitslücke in der JavaAPI darstellt :stuck_out_tongue:

Naja man kann etwas immer hinbiegen so das es passt :smiley: Wie gesagt, ich denke über diese Fragen immer viel zu sehr nach, habe irgendwie nicht diese Fähigkeit das „Wesentliche“ in solchen Fragen zu sehen.

An allocateInstance() von unsafe hatte ich auch gedacht. Aber das funktioniert auch nicht immer, habt ihr das ausprobiert?
Mich würde interessieren was die final Variable enthält, oder setzt die Unsafe API alle Fields auf null? :slight_smile:

Mein Beispiel wird afaik so im ObjectInputStream verwendet. Hatte mich erinnert, dass mal vor Ewigkeiten mir angesehen zu haben - weil mich interessiert hatte, wie der OIS Instanzen von Klassen ohne default constructor erzeugen kann.

seit Jahren schwebt mir vor, ich hatte zwischenzeitlich glaube ich auch einmal gemacht,
die Object-Klasse in der API vorsichtig zu überschreiben, neues rt.jar oder so zu erstellen,
um über jedes erzeugte Objekt informiert zu werden inklusive StackTrace usw.,

dort das erstellte Objekt im Konstruktor statisch ablegen dürfte auch gehen, mit Super-Klasse klappt es jedenfalls

so ernst ist es also nicht, das Objekt ist nicht vollständig initialisiert, aber existiert schon ziemlich gut, nur eben nicht als Referenz zurückgegeben

hat nicht auch sonst irgendeine Java-Komponente wie Speichermanagement Verwaltung erstellter Objekte, die man anzapfen kann? :wink:

Hmm… vielleicht über JMX?
Es gibt ja sowas wie „Phantomreference“ womit man feststellen kann, ob ein Objekt gerade durch den GC verramscht wird.