Primitive Types
Know the eight primitive types, their sizes and literal forms, and which one to pick for counts, IDs, sizes, flags and money.
int count = 42;
long id = 9_000_000_000L;
double ratio = 0.75; float f = 0.75f;
char c = 'A'; boolean ok = true; byte b = 0x7F;Java has eight primitive types. They hold the value directly, they cannot be null, and their sizes are the same on every platform:
| Type | Size | Range or use |
|---|---|---|
byte | 8 bits | -128 to 127; raw bytes from files and sockets |
short | 16 bits | -32,768 to 32,767; rarely used |
int | 32 bits | about plus or minus 2.1 billion; the default for whole numbers |
long | 64 bits | about plus or minus 9.2 quintillion; IDs, timestamps, byte counts |
float | 32 bits | about 7 significant digits |
double | 64 bits | about 15 to 16 significant digits; the default for decimals |
char | 16 bits | one UTF-16 code unit, unsigned |
boolean | not specified | true or false only |
Literals
A whole-number literal is an int unless it ends in L, so 5_000_000_000 does not compile but 5_000_000_000L does. A decimal literal is a double unless it ends in f. Underscores between digits are ignored and make large numbers readable. 0x starts hexadecimal and 0b binary.
Choosing a type in back-end code
Use int for counts, sizes of in-memory things and loop indexes. Use long for database IDs (a BIGINT column), epoch milliseconds and file sizes: an int runs out at about 2.1 billion, and tables and files get there. Use boolean for flags, never 0 and 1.
Do not use double or float for money. They are binary fractions, so 0.1 has no exact form and 0.1 + 0.2 is 0.30000000000000004. Store money as a long count of minor units (cents) or use BigDecimal. Compare doubles with a tolerance, not ==. Floating-point division by zero does not throw: it gives Infinity or NaN, and NaN is not equal even to itself.
A char is a number underneath, which is why 'A' + 1 is 66. It holds one UTF-16 code unit, so characters outside the Basic Multilingual Plane, such as most emoji, take two char values. Work with String for text.
One more back-end trap: JSON numbers are often read by JavaScript, which loses precision above 2 to the power 53. Large long IDs are safer sent as strings in public APIs.
Example
When to use it
- Map a BIGINT primary key column to long so IDs keep working past 2.1 billion rows.
- Store order totals as a long number of cents so sums and refunds stay exact.
- Compute a cache expiry in epoch milliseconds with long arithmetic so it cannot wrap negative.
More examples
Why doubles are wrong for money
Binary floating point cannot store 0.1 exactly, so equality checks fail. Counting whole cents in a long keeps every sum exact, and you only format at the edge.
void main() {
double sum = 0.1 + 0.2;
IO.println(sum);
IO.println(sum == 0.3);
IO.println(Math.abs(sum - 0.3) < 1e-9);
long priceCents = 1999;
long shippingCents = 450;
long totalCents = priceCents + shippingCents; // exact
IO.println(totalCents / 100 + "." + String.format("%02d", totalCents % 100));
}
// Output:
// 0.30000000000000004
// false
// true
// 24.49char is a number
Arithmetic on char promotes to int, so 'a' + 'b' is 97 + 98. The emoji is one code point but two UTF-16 code units, which is why length() returns 2 and codePointCount returns 1.
void main() {
char c = 'A';
IO.println(c + 1); // int arithmetic
IO.println((char) (c + 1)); // back to a char
IO.println('a' + 'b'); // two chars add as numbers
IO.println("" + 'a' + 'b'); // string concatenation
IO.println(Character.isDigit('7') + " " + Character.getNumericValue('7'));
String rocket = Character.toString(0x1F680); // one emoji
IO.println(rocket.length() + " " + rocket.codePointCount(0, rocket.length()));
}
// Output:
// 66
// B
// 195
// ab
// true 7
// 2 1int overflow in a time calculation
Thirty days in milliseconds is larger than Integer.MAX_VALUE, so the int product wraps to a negative number without any error. Floating-point division by zero gives Infinity or NaN instead of throwing, so check with Double.isNaN.
void main() {
int wrongMs = 30 * 24 * 60 * 60 * 1000; // 30 days, computed in int
long rightMs = 30L * 24 * 60 * 60 * 1000; // L makes the whole product long
IO.println(wrongMs);
IO.println(rightMs);
IO.println(1.0 / 0);
IO.println(0.0 / 0);
double nan = 0.0 / 0;
IO.println((nan == nan) + " " + Double.isNaN(nan));
}
// Output:
// -1702967296
// 2592000000
// Infinity
// NaN
// false true
Discussion