The eight primitives#
Java has exactly eight primitive types. They are not objects, they hold a value directly, and they are the only types that work this way — everything else in Java, String included, is a reference type.
| Type | Size | Range | Literal |
|---|---|---|---|
byte | 8 bits | −128 to 127 | (byte) 100 |
short | 16 bits | −32768 to 32767 | (short) 1000 |
int | 32 bits | −2147483648 to 2147483647 | 42 |
long | 64 bits | −9223372036854775808 to 9223372036854775807 | 42L |
float | 32 bits | about 7 decimal digits of precision | 3.14f |
double | 64 bits | about 15 decimal digits of precision | 3.14 |
char | 16 bits | 0 to 65535 (a character code) | 'A' |
boolean | — | true or false only | true |
Two of those rows deserve a second look. int is the default for whole numbers and double is the default for decimals — which is exactly why the next two errors happen.
Literals have types before they are assigned#
This looks like it should obviously work, and it does not compile:
long big = 10000000000;
Big.java:3: error: integer number too large
long big = 10000000000;
^
1 error
The variable is a long, so what is too large? The literal is. Java reads 10000000000 as an int before it ever looks at the left-hand side, and ten billion does not fit in an int. The L suffix tells the compiler to read it as a long from the start:
long big = 10000000000L; // compiles
The same thing happens with float, in the other direction:
float f = 3.14;
Flt.java:3: error: incompatible types: possible lossy conversion from double to float
float f = 3.14;
^
1 error
3.14 is a double, and squeezing a 64-bit value into a 32-bit variable could lose precision, so Java refuses. Write 3.14f.
Java will silently widen a value into a larger type, but never silently narrow one into a smaller type. Narrowing needs your explicit permission, in the form of a suffix or a cast. Even this fails, because a variable's value is not known at compile time:
int x = 5;
byte b = x; // error: incompatible types: possible lossy conversion from int to byteTrap 1 — integer division throws away the remainder#
If both operands are whole numbers, Java does whole-number division. There is no rounding involved:
System.out.println(7 / 2);
System.out.println(7 % 2);
System.out.println(7.0 / 2);
3
1
3.5
7 / 2 is 3, not 3.5 and not 4. The % operator recovers the remainder that division discarded.
The fix is to make at least one operand floating-point — but where you put the cast decides whether it works:
System.out.println((double) 7 / 2); // cast first, then divide
System.out.println((double) (7 / 2)); // divide first, then cast
3.5
3.0
In the second line the damage is already done: 7 / 2 evaluated to the int 3, and casting 3 to a double gives 3.0. Casting cannot recover information that was already discarded.
Trap 2 — overflow is silent#
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MAX_VALUE + 1);
2147483647
-2147483648
Adding one to the largest possible int produces the smallest possible int. No exception, no warning, no crash — the value wraps around like a car odometer rolling over, and the program keeps going with a wrong number.
This is why counters, running totals, and anything measured in milliseconds usually want long rather than int.
Trap 3 — decimals are not exact#
System.out.println(0.1 + 0.2);
System.out.println(0.1 + 0.2 == 0.3);
0.30000000000000004
false
double and float store values in binary. One tenth cannot be written exactly in binary for the same reason one third cannot be written exactly in decimal — the digits never stop. What gets stored is the nearest available value, and the tiny errors accumulate.
never compare floating-point values with ==. Compare the size of the difference against a small tolerance instead: Math.abs(a - b) < 1e-9. And never use double for money — count whole cents in an int or long.
Trap 4 — char is a number wearing a costume#
System.out.println('A' + 1);
System.out.println((char) ('A' + 1));
66
B
A char holds a number identifying a character; 'A' is 65. Arithmetic on it therefore produces a number, and Java promotes the result to int — which is why the first line prints 66 and not 'B'. Casting the result back to char gives the letter.
This is genuinely useful once you expect it. ch - 'a' converts a lowercase letter to its position in the alphabet, and (char) ('a' + n) converts a position back to a letter.
Casting truncates, it does not round#
System.out.println((int) 9.99);
System.out.println((int) -9.99);
9
-9
A cast to int deletes the fractional part and moves toward zero. It is not Math.floor either — floor would give −10 for the second one. If you want rounding, say so with Math.round.
Where these go wrong#
- Computing an average with integer division.
(a + b) / 2on twoints silently truncates. This is the single most common version of Trap 1. - Casting after the fact.
(double) (sum / count)is too late. Cast an operand, not the result. - Using
==on doubles. It compiles, it runs, and it returnsfalsefor values that look identical when printed. - Forgetting
Landf. Both produce compile errors that name a type you did not write, which makes them confusing until you know that the literal has a type of its own. - Expecting overflow to announce itself. It does not. A total that suddenly goes negative is the symptom.
- Reading
'A' + 1as string concatenation. Both operands are numeric here, so it is addition. Concatenation needs aStringon one side.
Test yourself in the free Kestrel Exams app
Topic-selectable practice — offline, no ads, no account.
Practice this topic →Frequently asked questions#
Why does 7 / 2 give 3 in Java?
Because both operands are int, so Java performs integer division and discards the remainder. It is not rounding — 7 / 2 is 3, not 4. To get 3.5, at least one side must be a floating-point value: use 7.0 / 2, or cast one operand with (double) 7 / 2.
What are the eight primitive types in Java?
byte, short, int, long, float, double, char and boolean. Everything else in Java — including String — is a reference type, not a primitive.
Why can't I write long big = 10000000000;?
Because the literal itself is treated as an int before it is ever assigned, and it does not fit in an int. The compiler reports “integer number too large”. Add an L suffix: long big = 10000000000L.
Why is 0.1 + 0.2 not equal to 0.3?
double and float store values in binary, and 0.1 has no exact binary representation, just as one third has no exact decimal representation. The sum comes out as 0.30000000000000004. Never compare floating-point values with ==; compare the absolute difference against a small tolerance instead.
Is char a number or a letter?
Both. A char stores a number from 0 to 65535 that identifies a character. Because it is a number, arithmetic works on it: 'A' + 1 evaluates to the int 66. Casting back with (char) turns 66 into 'B'.
What is the difference between casting and rounding?
Casting a double to an int truncates toward zero — it deletes the fractional part rather than rounding it. (int) 9.99 is 9, and (int) -9.99 is -9, not -10. Use Math.round when you want rounding.
