Java has eight primitive types, and picking the wrong one is a quiet source of bugs: a counter that overflows at the wrong moment, a price that loses its cents, a character that is stored as a number. The set is small enough to learn completely, and worth learning completely.
What are the eight primitive types?
Four are for whole numbers of different widths, two are for fractional numbers, one is for text characters, and one is for true or false. That is the entire list.
public class Sizes {
public static void main(String[] args) {
System.out.println("byte : " + Byte.MIN_VALUE + " .. " + Byte.MAX_VALUE);
System.out.println("short : " + Short.MIN_VALUE + " .. " + Short.MAX_VALUE);
System.out.println("int : " + Integer.MIN_VALUE + " .. " + Integer.MAX_VALUE);
System.out.println("long : " + Long.MIN_VALUE + " .. " + Long.MAX_VALUE);
System.out.println("float : " + Float.MIN_VALUE + " .. " + Float.MAX_VALUE);
System.out.println("double : " + Double.MIN_VALUE + " .. " + Double.MAX_VALUE);
System.out.println("char : " + (int) Character.MIN_VALUE + " .. " + (int) Character.MAX_VALUE);
System.out.println("boolean: " + Boolean.FALSE + " .. " + Boolean.TRUE);
}
}
Two of those lines are deliberately noisy: char prints as a number because it is one. The rest print as you would expect.
Which type should I use for whole numbers?
int is the default and the right answer far more often than people expect. It covers roughly plus or minus two billion, which is enough for counters, array indexes, timestamps in seconds, and most quantities you will ever hold. byte and short exist for memory-sensitive tables and for values that come out of a binary format. long is for identifiers, nanosecond timestamps, and anything that legitimately exceeds the int range.
long requestId = 8_000_000_000L; // the L suffix makes it a long literal
int count = 2_000_000_000; // still fits in an int
short small = 32000; // fits in a short
int overflow = 2147483647 + 1; // wraps around to a negative number
System.out.println(overflow);
Java wraps around on overflow rather than throwing, which is why that last line prints a negative number instead of failing. For money, for anything that must not silently change, keep the value in a long of minor units (cents, not pounds) or use a dedicated money type.
What about floating point?
float and double store binary approximations of decimal numbers, which is why money and floating point do not mix. double is the default and the only sensible choice for general arithmetic; float halves the memory and is occasionally required by an interface.
double total = 0.1 + 0.2;
System.out.println(total); // 0.30000000000000004
System.out.println(total == 0.3); // false
System.out.println(Math.abs(total - 0.3) < 1e-9); // true
System.out.printf("%.2f%n", total); // 0.30
That first line is not a bug in Java, it is a fact about binary floating point: 0.1 has no exact representation, so the sum lands a hair away from 0.3. Comparing floating point values with == is therefore almost always wrong — compare with a tolerance, or format the value for display, as the last two lines do.
Why is char not a number?
char holds a single UTF-16 code unit. It is written in single quotes, and the arithmetic on it surprises people: adding to a char gives an int, because the compiler promotes it.
char initial = 'A';
int code = initial + 1; // 66
char next = (char) (initial + 1); // 'B' — the cast is required
System.out.println(initial + next); // 131, not AB
System.out.println("" + initial + next); // AB
Note the empty string in the last line: without it, + would do arithmetic. And note that a char holds one code unit, so characters outside the basic multilingual plane — many emoji, for example — need two char values to represent. For ordinary text, prefer String.
What about booleans?
boolean holds true or false and cannot be converted to a number. In older languages you might see 0 and 1 standing in for false and true; Java does not allow that, and the strictness prevents a class of comparison bugs.
boolean ready = true;
if (ready) {
System.out.println("starting");
}
// int flag = 1; // does not compile: incompatible types
How do I choose?
- Counters, indexes, most whole numbers:
int. - Identifiers, timestamps in nanoseconds, very large quantities:
long. - Measurements and general arithmetic:
double. - Money: integer minor units in a
long, or a dedicated money type. - Text:
String, notchararrays. - Yes or no:
boolean.
One more thing worth knowing: every primitive has a matching object form — int becomes Integer, and so on — and Java converts between them automatically where it needs to, which is why an int can be added to a collection of objects without complaint. That conversion is called autoboxing, and it is the bridge between primitives and the generics-heavy parts of the standard library.
Why do the defaults matter?
Two defaults shape most Java code you will read. An integer literal is an int, so long x = 5; needs no cast but int y = 5L; does not compile. And a floating point literal is a double, which is why float f = 0.5f; carries an f suffix. Both defaults are deliberate: the wider type is the one that is rarely wrong, and widening is free.
int a = 5; // int
long b = 5; // long, widened from an int literal
long c = 5L; // long, stated explicitly
double d = 0.5; // double
float e = 0.5f; // float, stated explicitly
short s = 5; // short, narrowed only because the target says so
The pattern to remember is that a literal takes the narrowest type that holds it, and a variable takes the type you write down. When the two disagree, the compiler either widens for free or asks for an explicit cast.
Quick reference
- Eight primitives: byte, short, int, long, float, double, char, boolean.
intis the default;longliterals need anLsuffix.- Integer overflow wraps silently — size your types deliberately.
- Never compare floating point with
==; use a tolerance. charis a number in disguise and holds one code unit.- Primitives box into their object forms automatically.
Try it yourself: write a program that reads nothing and prints the size of each primitive using the constants above, then compute a total price as double and print it with two decimals. Finally, add 0.1 + 0.2 to the total and print the raw value to see the representation error appear in your own output.