In Java unterscheidest du grob zwischen primitiven Datentypen und Referenztypen. Primitive Typen sind die Basis: Sie speichern den Wert direkt, ohne Objekt-Hülle. Das macht sie schnell, vorhersehbar und speichereffizient. Wichtig ist dabei: Die Größe der primitiven Typen ist in Java fest definiert und hängt nicht vom Betriebssystem ab. Genau diese Festlegung macht Code auf verschiedenen Plattformen so stabil.
Die Speicherplatzreservierung wird bei primitiven Typen in Bits angegeben. 8 Bit sind 1 Byte. Damit kannst du sehr konkret abschätzen, was eine Variable mindestens belegt. Bei Referenztypen wie String, Arrays oder eigenen Klassen liegt in der Variable dagegen nur eine Referenz. Die eigentlichen Daten liegen im Heap, und die Größe ist von der Implementierung und dem Inhalt abhängig.
Hier ist der kompakte Überblick über die primitiven Typen, inklusive Reservierung, typischer Wertebereiche und dem, was du im Alltag wirklich beachten solltest:
| Typ | Reservierung | Größe | Wertebereich (vereinfacht) | Typische Nutzung |
|---|---|---|---|---|
byte |
8 Bit | 1 Byte | -128 bis 127 | Rohdaten, kleine Zähler, IO |
short |
16 Bit | 2 Byte | -32.768 bis 32.767 | selten, Binary-Formate |
int |
32 Bit | 4 Byte | ca. -2,1 Mrd bis 2,1 Mrd | Standard für Ganzzahlen |
long |
64 Bit | 8 Byte | sehr groß | Zeitstempel, große Zähler |
float |
32 Bit | 4 Byte | Gleitkomma, ca. 7 Stellen | Speicher sparen, selten |
double |
64 Bit | 8 Byte | Gleitkomma, ca. 15-16 Stellen | Standard für Gleitkomma |
char |
16 Bit | 2 Byte | 0 bis 65.535 | einzelne UTF-16 Code-Unit |
boolean |
JVM-abhängig | logisch | true oder false |
Bedingungen, Flags |
Bei boolean ist der Wertebereich zwar klar, aber die konkrete Speicherplatzbelegung ist eine JVM-Implementierungsfrage. In Arrays wird boolean oft stark komprimiert, als einzelne Variable kannst du praktisch nicht mit einer exakten Byte-Zahl planen. Für Design-Entscheidungen ist das aber meist egal: Du nutzt boolean, wenn du genau zwei Zustände ausdrücken willst. Sobald du mehr als zwei Zustände hast, ist ein Enum meistens die sauberere Wahl.
Ein Punkt, der Anfängern häufig passiert: Standardwerte. Felder in Klassen bekommen Default-Werte wie 0, 0.0, false oder \u0000. Lokale Variablen müssen dagegen vor der ersten Nutzung initialisiert werden, sonst meckert der Compiler. Das ist keine Schikane, sondern verhindert echte Fehler.
Ganze Zahlen: byte, short, int, long, char und die Sache mit Overflows
Für Ganzzahlen ist int der Normalfall. Er ist schnell, gut unterstützt und hat für die meisten Zähler, Mengen und IDs genug Luft. long nimmst du, wenn du sicher weißt, dass der Wertebereich von int nicht reicht, zum Beispiel bei Zeitwerten in Millisekunden oder sehr großen Sequenzen. byte und short tauchen vor allem in IO, Protokollen oder Dateiformaten auf, weil dort jedes Byte zählt oder ein Format diese Breite festlegt. Im Business-Code sind sie selten sinnvoll.
Der größte Stolperstein bei Ganzzahlen ist der Overflow. Java wirft bei einem Überlauf keinen Fehler, sondern der Wert springt im Zweierkomplement um. Das kann zu sehr unangenehmen Bugs führen, wenn du Grenzwerte nicht beachtest.
int x = Integer.MAX_VALUE;
x = x + 1; // Overflow: wird negativ
Wenn du mit Summen oder Produkten arbeitest, ist eine typische Abwehrmaßnahme, früh auf long zu gehen oder explizit zu prüfen. In Java gibt es dafür auch Hilfen wie Math.addExact, die bei Overflow eine Exception werfen. Das kostet etwas mehr, ist aber bei kritischen Berechnungen oft besser als stiller Datenmatsch.
char ist formal eine Ganzzahl, aber semantisch für Zeichen gedacht. Wichtig: char ist in Java eine 16-Bit UTF-16 Code-Unit, nicht automatisch ein komplettes Unicode-Zeichen. Viele Emojis oder Zeichen außerhalb der Basic Multilingual Plane brauchen zwei char Werte als sogenanntes Surrogate Pair. Wenn du also wirklich "Zeichen" verarbeiten willst, solltest du eher mit String und Code Points arbeiten als mit char allein.
String s = "A🙂";
int codePointCount = s.codePointCount(0, s.length());
Vorteile von int und long sind klar: exakte Darstellung ganzer Zahlen, schnelle CPU-Operationen, keine Rundungsfehler. Nachteile sind Overflows und die Tatsache, dass Ganzzahlen keine Dezimalstellen können. Sobald du Dezimalbrüche brauchst, ist der Sprung zu float oder double verlockend, aber da wartet das nächste Problem.
float und double: schnell, bequem, aber nicht exakt
float und double sind IEEE-754 Gleitkommazahlen. Sie können sehr große und sehr kleine Werte darstellen, aber nicht jeden Dezimalwert exakt. Das ist kein Java-Problem, sondern Mathematik plus Binärdarstellung. Ein Klassiker ist 0.1, das in binärer Form keine endliche Darstellung hat. Ergebnis: Rundungsfehler, die sich bei vielen Operationen aufsummieren.
double a = 0.1 + 0.2;
System.out.println(a); // oft 0.30000000000000004
double ist der Standard, weil er mehr Präzision hat als float. float nutzt du nur dann bewusst, wenn Speicher und Bandbreite wirklich ein Thema sind, etwa in großen Arrays für Grafik oder Sensorwerte. Für normale Anwendungsentwicklung ist float oft eine Fehlerquelle: weniger Präzision, gleiche konzeptionelle Probleme.
Die Vorteile von float und double sind Performance und ein großer Wertebereich. Du bekommst schnelle Berechnungen, die für viele technische Aufgaben völlig ausreichen, zum Beispiel Geometrie, Messwerte oder Statistik. Der Nachteil ist die fehlende Dezimal-Exaktheit. Das wird besonders sichtbar bei Geld, Rabatten, Steuern oder allgemein bei Werten, die Menschen als Dezimalzahlen erwarten.
Für Geld und andere exakte Dezimalberechnungen ist BigDecimal das Werkzeug der Wahl. BigDecimal arbeitet mit beliebiger Präzision und dezimaler Darstellung. Der Preis ist mehr Speicher und deutlich weniger Geschwindigkeit. In Business-Logik ist das aber oft genau der richtige Trade-off, weil ein Cent Fehler nicht akzeptabel ist.
import java.math.BigDecimal;
BigDecimal price = new BigDecimal("19.99");
BigDecimal tax = new BigDecimal("0.19");
BigDecimal gross = price.multiply(BigDecimal.ONE.add(tax));
Wichtig bei BigDecimal: Erzeuge Werte für Dezimalzahlen als String, nicht über double, sonst schleppst du den Rundungsfehler schon beim Erzeugen mit. Und setze bei Divisionen eine Scale und ein RoundingMode, sonst bekommst du bei nicht terminierenden Dezimalbrüchen eine Exception.
Neben Gleitkomma gibt es in Java auch Referenztypen wie String, Integer oder Double. Die Wrapper-Klassen sind praktisch, wenn du null als "kein Wert" brauchst, oder wenn du mit Generics arbeitest, weil Generics nur Referenztypen akzeptieren. Der Nachteil ist Autoboxing: Java wandelt dann automatisch zwischen primitive und Wrapper um. Das kann in heißen Schleifen Performance kosten und bei null sogar zu NullPointerExceptions führen.
Integer n = null;
int m = n; // NullPointerException durch Unboxing
kleine Anekdote: Das 2038-Problem: wenn Zeit in Sekunden plötzlich kippt
Das 2038-Problem betrifft Systeme und Datenformate, die Zeitpunkte als Sekunden seit dem 1. Januar 1970 (Unix Epoch) in einem 32-Bit signed int speichern. Ein int kann maximal 2.147.483.647 halten. Dieser Wert entspricht 2038-01-19 03:14:07 UTC. Eine Sekunde später kommt der Overflow und der Wert springt ins Negative - je nach System wird daraus dann ein Zeitpunkt im Jahr 1901 oder ein anderer falscher Wert. In Java bist du nicht automatisch safe, nur weil du "Java" nutzt: Sobald du Zeitstempel aus Datenbanken, APIs, Protokollen oder alten Schnittstellen als int liest oder speicherst, kann dich das einholen. Praxisregel: Zeitstempel immer als long (Millisekunden) oder über moderne Typen wie Instant und LocalDateTime modellieren, und bei SQL-Spalten genau hinschauen, ob irgendwo noch ein 32-Bit Epoch-Seconds Feld herumliegt.
Fazit: nimm den Typ, der deine Absicht am klarsten ausdrückt
Datentypen sind weniger eine Frage von "was passt irgendwie" und mehr eine Frage von Absicht. int ist dein Arbeitstier für Ganzzahlen. long kommt dazu, wenn der Wertebereich wirklich größer sein muss. boolean ist perfekt für genau zwei Zustände. char ist gut für einzelne UTF-16 Code-Units, für echte Textverarbeitung bist du mit String und Code Points besser aufgestellt. double ist bequem für technische Berechnungen, aber nicht für exakte Dezimalwerte. Sobald es um Geld oder exakte Dezimalarithmetik geht, nimm BigDecimal.
Wenn du diese Grundregeln beachtest, ersparst du dir die häufigsten Klassen von Fehlern: Overflows bei Ganzzahlen, Rundungsprobleme bei Gleitkomma und Null-Fallen bei Wrappern. Der Rest ist Erfahrung und das kommt vor allem dadurch, dass du dir bei jeder Variable kurz klar machst: Welche Werte erwarte ich wirklich, und welche Fehler dürfen auf keinen Fall passieren.
