Java Data Types
Explore Java's primitive data types (byte, short, int, long, float, double, char, boolean) and reference types.
Java is statically typed — every variable has a type known at compile time. Types fall into two families: primitives (raw values built into the language) and reference types (objects, arrays, anything created with new). This chapter walks through both.
The eight primitive types
Primitives are the only values in Java that are not objects. They store their value directly in the variable's storage slot. Java defines exactly eight:
| Type | Size | Range | Default | Example literal |
|---|---|---|---|---|
byte | 8 bits | -128 to 127 | 0 | byte b = 100; |
short | 16 bits | -32,768 to 32,767 | 0 | short s = 30000; |
int | 32 bits | -2³¹ to 2³¹-1 (≈ ±2.1 billion) | 0 | int i = 1_000_000; |
long | 64 bits | -2⁶³ to 2⁶³-1 | 0L | long l = 9_000L; |
float | 32 bits | IEEE 754 single precision | 0.0f | float f = 3.14f; |
double | 64 bits | IEEE 754 double precision | 0.0 | double d = 3.14; |
char | 16 bits | Unicode code unit (U+0000 to U+FFFF) | \0 | char c = 'A'; |
boolean | JVM-defined | true or false | false | boolean b = true; |
A few things worth committing to memory:
intis the default integer type. Most counters and indexes areint. Uselongonly when you actually need numbers above ~2 billion.doubleis the default floating-point type. Plain3.14is adouble;3.14fis afloat. Usedoubleunless memory pressure forcesfloat.- Underscores in numeric literals improve readability:
1_000_000is the same as1000000. - Default values apply only to fields, not to local variables. A local variable has no default — you must initialise it before reading.
Reference types
Everything else — String, arrays, your own classes, library classes — is a reference type. A reference-type variable doesn't hold the object itself; it holds a reference (essentially a pointer) to the object on the heap:
String name = "Ada";
int[] scores = new int[10];
LocalDate today = LocalDate.now();The default value of any reference variable is null — a special value meaning "no object."
String s = null;
System.out.println(s.length()); // throws NullPointerException at runtimenull is the source of one of Java's most common runtime errors. The Java Exceptions chapter covers how to handle the NullPointerException this raises.
Wrapper classes
Each primitive has a matching wrapper class — a real object that wraps the primitive value:
| Primitive | Wrapper |
|---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
You need wrappers when you want to put primitives into collections (which only hold objects):
List<Integer> scores = new ArrayList<>();
scores.add(42); // autoboxed from int → Integer
int first = scores.get(0); // unboxed from Integer → intJava automatically converts between primitives and wrappers — called autoboxing and unboxing — so most of the time you don't notice.
A note on String
String is a reference type, not a primitive. It's special enough that the language gives it a literal syntax ("text") and the + operator. Strings are immutable — once created, their contents never change. Every "modification" returns a new string. The Java Strings chapter goes deep.
Choosing a type
A short decision tree for everyday Java:
- Counting things or array indexes →
int - Large counts (file sizes, timestamps in millis) →
long - Money →
BigDecimal(notdouble— floating-point rounding will bite you) - Single character →
char - Yes/no →
boolean - Text →
String - A date or time → types in
java.time(LocalDate,Instant,Duration)
Resist the urge to "save memory" by using byte or short for ordinary integers. JVMs are tuned for int operations; short doesn't actually use less memory in most cases.
Common gotchas
A handful of type behaviours surprise newcomers regularly. Knowing them up front saves hours of debugging.
Integer overflow wraps silently
Integer arithmetic in Java never throws on overflow — it wraps around. Adding 1 to the largest int produces the smallest int:
int max = Integer.MAX_VALUE; // 2147483647
System.out.println(max + 1); // -2147483648If a value might exceed ~2.1 billion, use long. For counts that could overflow even long, reach for BigInteger.
Floating-point is not exact
float and double follow the IEEE 754 standard, which cannot represent most decimal fractions exactly. The classic example:
System.out.println(0.1 + 0.2); // 0.30000000000000004This is why you should never use double for money. Use BigDecimal (constructed from a String, e.g. new BigDecimal("0.1")) when exact decimal values matter.
char is a number
A char is an unsigned 16-bit integer holding a Unicode code unit, so it participates in arithmetic:
char a = 'A';
System.out.println((int) a); // 65
System.out.println((char) (a + 1)); // BCompare wrappers with .equals(), not ==
== on wrapper objects compares references, not values. Because of an autoboxing cache for small Integer values (-128 to 127), == appears to work for small numbers and then fails for large ones:
Integer a = 127, b = 127;
System.out.println(a == b); // true (cached)
Integer c = 128, d = 128;
System.out.println(c == d); // false (different objects)
System.out.println(c.equals(d)); // true (compares values)Always use .equals() to compare wrapper values. See Java Type Casting for the rules on converting between these types.
A working demonstration
What's next
Java Type Casting shows how to convert between numeric types and between related reference types.