Java Rätsel

Hallo,

Der Java Compiler erkennt, dass ein primitiver Typ als ein Referenztyp benötigt wird und fügt automatisch eine entsprechende Auto-Boxing Operation durch. Dabei wird die statische valueOf(...) Methode der entsprechenden Wrapper-Klasse verwendet.
Die folgenden Wrapper-Typen verwenden in der valueOf- Methode einen zum Teil konfigurierbaren (*) Cache (Standard Werte Bereiche in [...] angegeben) :Byte[-128,+127], Short[-128,+127], Integer[-128,+127], Long[-128,+127], Character[0,+128], (Boolean[TRUE,FALSE])
Diese Caches bilden die primitiven Werte auf entsprechende Wrapper Typ Instanzen ab.
Wird die valueOf Methode mit einem Argument aufgerufen, dass innerhalb der Cache-Grenzen so wird
immer die selbe Wrapper-Instanz zurückgegeben.

Hierbei handelt es sich im Prinzip um das Flyweight-Design Pattern. Hierbei handelt es sich also um eine Optimierung um die Objekt-Erzeugung von Wrapper-Typen in Standardfällen zu reduzieren und somit weniger "GC-Pressure" zu erzeugen und auch "weniger" Speicher zu verbrauchen, da die entsprechenden Wrapper Instanzen im Idealfall ja nur 1-Mal vorhanden sind.
Die Wrapper-Klassen Double und Float haben keine solche Cache-Mechanismen eingebaut.

System.out.println(Integer.valueOf(1) == Integer.valueOf(1));
System.out.println(System.identityHashCode(Integer.valueOf(1)) + " = " + System.identityHashCode(Integer.valueOf(1)));

Da der Cache im Falle von Integer standardmäßig auf den Wertebereich [-128,+127] beschränkt ist, liefert die valueOf Methode ab einem Wert ober- oder unterhalb der Cache grenzen dann neue Integer Instanzen.

System.out.println(Integer.valueOf(128) == Integer.valueOf(128));
System.out.println(System.identityHashCode(Integer.valueOf(128)) + " != " + System.identityHashCode(Integer.valueOf(128)));

(*)
versuchs mal mit -Djava.lang.Integer.IntegerCache.high=1000000 oder-XX:+AggressiveOpts laufen zu lassen ;-)

Gruß Tom
 
Das ist eigentlich nicht so sehr schwer. Der code sieht nach dem Kompilieren eigentlich so aus:
Java:
public static void main(String args[]) {
    Integer a1 = Integer.valueOf(50); // irgend eine Instanz
    Integer b1 = Integer.valueOf(50); // die selbe Instanz wie a1
    Integer a2 = Integer.valueOf(500); // neue Instanz, da > 127 -> new Integer(500)
    Integer b2 = Integer.valueOf(500); // neue Instanz, da > 127 -> new Integer(500)
 
    System.out.println(a1 == b1);
    System.out.println(a2 == b2);
}

Die Sache ist die. Integer arbeitet mit einem Cache, der von -128 bis 127 reicht. Wird eine Zahl innerhlab dieses Bereiches "angefordert", wird eine gecachte Instanz zurück gegeben. Alles, was außerhalb dieses Bereiches ist, wird neu instanziiert und somit sind das dann beim Vergleich mittels Vergleichsoperator unterschiedliche Instanzen, daher auch ein false als Ergebnis.

EDIT:
Mist, Thomas war schneller :D
 
Zuletzt bearbeitet:
@Tom: perfekte Erklärung! Ist alles dabei was man wissen muss :)
Dass man das ganze noch per VM-Argument beeinflussen kann ist sehr interessant. Da hab auch ich noch was dazugelernt ;)

@Akeshihiro: ebenfalls die richtige Erklärung! :)
 
Hallo,

dann habe ich auch mal ein einfaches neues Rätsel:

Für welche Argumente gibt Math#abs(..) einen negativen Wert zurück?

Gruß Tom
 
Code:
System.out.println(Math.abs(Integer.MIN_VALUE));
Erklärung JavaDoc
Code:
Note that if the argument is equal to the value of Integer.MIN_VALUE, 
the most negative representable int value, the result is that same value, 
which is negative.
Vermutlich weil der MIN_VALUE ohne Vorzeichen ins eins grösser ist als MAX_VALUE...?

Gruss
slowy
 
Hallo,

Stimmt zum Teil ;-) aber deine Erklärung passt IMHO nicht bzw. entspricht nicht die aus dem JavaDoc Fragment. Dies enthält jedoch die Antwort: -Integer.MIN_VALUE == Integer.MIN_VALUE und --Long.MIN_VALUE == Long.MIN_VALUE. Das sind zwei explizite Sonderfälle :)
Math.signum(Math.abs(Integer.MIN_VALUE)) und Math.signum(Math.abs(Long.MIN_VALUE)) geben beide -1 zurück.

Vermutlich weil der MIN_VALUE ohne Vorzeichen ins eins grösser ist als MAX_VALUE...?
... fast ;-)
Java:
Integer.MIN_VALUE-1  == Integer.MAX_VALUE

Math#abs ist für Fließkommawerte anders definiert als die Ganzzahl-Varianten, deshalb tritt das Verhalten von #abs dort nicht so auf -> Math.signum(Math.abs(Float.MIN_VALUE)) und Math.signum(Math.abs(Double.MIN_VALUE)) geben beide "+1" zurück.

Java:
        System.out.printf("-(%s) => %s%n",Byte.MIN_VALUE, -Byte.MIN_VALUE);
        System.out.printf("-(%s) => %s%n",Short.MIN_VALUE, -Short.MIN_VALUE);
        System.out.printf("-(%s) => %s%n",Integer.MIN_VALUE, -Integer.MIN_VALUE);
        System.out.printf("-(%s) => %s%n",Long.MIN_VALUE, -Long.MIN_VALUE);
        System.out.printf("-(%s) => %s%n",Float.MIN_VALUE, -Float.MIN_VALUE);
        System.out.printf("-(%s) => %s%n",Double.MIN_VALUE, -Double.MIN_VALUE);

Ausgabe:
Code:
-(-128) => 128
-(-32768) => 32768
-(-2147483648) => -2147483648  ******
-(-9223372036854775808) => -9223372036854775808 ******
-(1.4E-45) => -1.4E-45 ******
-(4.9E-324) => -4.9E-324  ******

... habe dieses Beispiel gewählt, weil dies wieder mal eine der Stellen zeigt, wo die API nicht ganz das zu machen scheint, was man auf den ersten Blick erwartet ;-)

... und die Moral von der Geschicht - vergiss ins JavaDoc zu schauen nicht :)

Gruß Tom
 
Für folgende Argumente wird ein negativer Wert zurückgeliefert:

Java:
Integer.MIN_VALUE // bzw. Integer.MAX_VALUE + 1
Long.MIN_VALUE // bzw. Long.MAX_VALUE + 1

MAX_VALUE + 1 hat im Prinzip genau den gleichen Wert wie MIN_VALUE wegen dem internen "RollOver", siehe auch Zweierkomplement

Daher auch:

Java:
(Integer.MAX_VALUE + 1) == Integer.MIN_VALUE == -Integer.MIN_VALUE
(Long.MAX_VALUE + 1) == Long.MIN_VALUE == -Long.MIN_VALUE


----- Oh, zu langsam :)
 
So dann schau ich mal ob ich ein Rätsel zur Verfügung stellen kann.

Folgende Klasse ist gegeben und bekannt:
Java:
public class KnackMich{
       private String geheim = "Ich bin privat";
       private String privat = "Ich bin geheim";
}
Diese Klasse ist in in sich geschlossen und kann nicht verändert werden.
Aufgabe: Erstelle ein Object der Klasse und gebe den Inhalt der Variabeln geheim und private aus.
:)
 
Huhu =)

Code:
import java.lang.reflect.Field;

public class Knacker {
	public static void main(String[] args) {
		try {
			KnackMich knackMich = new KnackMich();

			Field[] fields = KnackMich.class.getDeclaredFields();

			for (Field field : fields) {
				field.setAccessible(true);
				System.out.println(field.get(knackMich));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}
}

Ausgabe:
Code:
Ich bin privat
Ich bin geheim

Ohne die Zeile
field.setAccessible(true);
wird eine IllegalAccessException geworfen.

Grüsse, slowy
 
Zurück