Data Types (Primitives & Ranges)
Reviewed & published by Brayan K
In the Variables lesson you met int, double, String and boolean. Now let's go deeper: Java's eight primitive types, exactly how much each one can hold, and how picking the right type prevents subtle bugs like overflow and rounding errors.
Part of the free Java course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
- All 8 primitive types and their exact bit sizes
- The precise range (MIN/MAX) of each numeric type
- Why long needs L and float needs f
- Integer overflow — and why Java doesn't warn you
- That char is really a number under the hood
- How to choose the right type for real data
1️⃣ Two Families of Types
Java has exactly 8 primitive types. A primitive stores a raw value directly in a fixed block of memory — there is no object, no method, and it can never be null. They split into two families:
Whole numbers, no fractional part.
byte → short → int → long
Each step is twice the bits, so twice the range.
float (32-bit) → double (64-bit)
double has roughly twice the precision of float.
The remaining two don't fit either family: char holds a single character, and boolean holds only true or false.
💡 Analogy: Think of types as buckets of different sizes. A byte is an egg cup, an int is a kitchen jug, and a long is a water barrel. You could carry a teaspoon of water in a barrel, but it wastes space — and you can't pour a barrel into an egg cup without spilling.
public class Main {
public static void main(String[] args) {
// --- The integer family (whole numbers) ---
byte level = 120; // 8 bits: -128 .. 127
short year = 2026; // 16 bits: -32,768 .. 32,767
int views = 1_240_000; // 32 bits: ~ -2.1B .. 2.1B (the default)
long galaxies = 200_000_000_000L; // 64 bits: needs the L suffix
// --- The floating-point family (decimals) ---
float ratio = 0.5f; // 32 bits, ~7 digits, needs the f suffix
double price = 19.99; // 64 bits, ~15 digits (the default)
// --- The odd ones out ---
char grade = 'A'; // 16 bits: ONE character, single quotes
boolean active = true; // 1 bit: true / false only
System.out.println("byte -> " + level);
System.out.println("short -> " + year);
System.out.println("int -> " + views);
System.out.println("long -> " + galaxies);
System.out.println("float -> " + ratio);
System.out.println("double -> " + price);
System.out.println("char -> " + grade);
System.out.println("boolean -> " + active);
}
}2️⃣ The Complete Range Table
Because every primitive has a fixed size, its minimum and maximum values are constants baked into the language. Memorise the int range; look the rest up via the wrapper classes (Integer.MAX_VALUE, Long.MIN_VALUE, etc.).
| Type | Size | Minimum | Maximum | Default |
|---|---|---|---|---|
| byte | 8-bit | -128 | 127 | 0 |
| short | 16-bit | -32,768 | 32,767 | 0 |
| int | 32-bit | -2,147,483,648 | 2,147,483,647 | 0 |
| long | 64-bit | -9.2 × 10¹⁸ | 9.2 × 10¹⁸ | 0L |
| float | 32-bit | ~±3.4 × 10³⁸ | ~7 digits | 0.0f |
| double | 64-bit | ~±1.8 × 10³⁰⁸ | ~15 digits | 0.0 |
| char | 16-bit | '\u0000' (0) | 65,535 | '\u0000' |
| boolean | 1-bit* | false | true | false |
* The JVM doesn't truly use a single bit; size is implementation-defined. The "Default" column is the value a field gets when you don't initialise it (local variables get no default — you must assign them).
3️⃣ Overflow — When Numbers Wrap Around
Because each type has a hard ceiling, going one past it doesn't throw an error — it silently wraps back to the minimum, like an odometer rolling over to zero.
int max = Integer.MAX_VALUE; // 2,147,483,647
int oops = max + 1; // -2,147,483,648 (wrapped!)4️⃣ char Is Secretly a Number
A char looks like a letter, but internally it's an unsigned 16-bit number — the Unicode code point. That means you can do arithmetic on characters:
char a = 'A'; // code point 65
int code = a; // widening: 'A' -> 65
char next = (char)(a + 1); // 66 -> 'B'
boolean isUpper = a >= 'A' && a <= 'Z'; // trueThis trick — treating letters as numbers — is the basis of cipher puzzles, sorting, and case-conversion. Single quotes mean char; double quotes mean String. They are not interchangeable.
public class Main {
public static void main(String[] args) {
// Every primitive has a fixed size, so its MIN and MAX are constants.
// The wrapper classes expose them so you never have to memorise them.
System.out.println("int min = " + Integer.MIN_VALUE);
System.out.println("int max = " + Integer.MAX_VALUE);
System.out.println("long max = " + Long.MAX_VALUE);
System.out.println("byte range = " + Byte.MIN_VALUE + " .. " + Byte.MAX_VALUE);
// OVERFLOW: go one past the max and it silently wraps around.
int max = Integer.MAX_VALUE; // 2,147,483,647
System.out.println("max = " + max);
System.out.println("max + 1 = " + (max + 1)); // wraps to the minimum!
// A real bug: a counter that overflows instead of erroring.
int seconds = Integer.MAX_VALUE;
seconds++; // no exception — just garbage
System.out.println("overflowed counter = " + seconds);
// 'char' is secretly a number (its Unicode code point).
char letter = 'A';
int code = letter; // 'A' is 65
System.out.println("'A' as a number = " + code);
System.out.println("65 as a char = " + (char) 66); // 'B'
}
}5️⃣ Choosing the Right Type
You rarely need the exotic types. Use this decision guide:
- Whole number that fits in a couple of billion → int (the default)
- Whole number bigger than ~2 billion (IDs, timestamps, byte counts) → long
- Decimal number (measurements, science, prices while learning) → double
- Yes/no flag → boolean
- A single character → char
- Real money in production → BigDecimal (a class, not a primitive)
- Raw file/network bytes → byte (often in a byte[])
public class Main {
public static void main(String[] args) {
// SCENARIO: an online order. Pick the RIGHT type for each value.
int quantity = 3; // whole count -> int
double unitPrice = 12.50; // money with decimals -> double*
boolean giftWrap = true; // yes/no flag -> boolean
char sizeCode = 'M'; // single S/M/L/X code -> char
long orderId = 9007199254740993L; // huge id -> long
double subtotal = quantity * unitPrice;
double total = giftWrap ? subtotal + 2.99 : subtotal;
System.out.println("Order #" + orderId);
System.out.println("Size: " + sizeCode);
System.out.println("Quantity: " + quantity + " @ $" + unitPrice);
System.out.printf("Total: $%.2f%n", total);
// * Note: for REAL currency, prefer BigDecimal over double.
// double cannot store 0.1 exactly:
System.out.println("0.1 + 0.2 = " + (0.1 + 0.2));
}
}public class Main {
public static void main(String[] args) {
// 🎯 YOUR TURN — replace each ___ using the hint beside it.
// 1) Never more than 127: the smallest whole-number type will do.
___ smallCount = 120; // 👉 replace ___ with byte
// 2) Millions fit in an int comfortably. _ is just a readability aid.
int people = 2_500_000;
// 3) Three billion is past int's limit, so the literal needs a suffix.
long stars = 3000000000___; // 👉 replace ___ with L
// 4) A decimal literal is a double unless you mark it.
float ratio = 1.5___; // 👉 replace ___ with f
double precise = 0.1 + 0.2; // binary floating point, not a bug
// 5) One character, single quotes. Double quotes would be a String.
char grade = ___; // 👉 replace ___ with 'B'
boolean open = true;
System.out.println("byte: " + smallCount);
System.out.println("int: " + people);
System.out.println("long: " + stars);
System.out.println("float: " + ratio);
System.out.println("double: " + precise);
System.out.println("char: " + grade);
System.out.println("boolean: " + open);
System.out.println("int max: " + Integer.MAX_VALUE);
System.out.println("Overflow: " + (Integer.MAX_VALUE + 1));
}
}🧩 Reorder Challenge
These lines are scrambled. Put them in the correct order so the program declares a long file size, converts it to a double in megabytes, and prints it.
System.out.printf("%.2f MB%n", megabytes);
double megabytes = bytes / (1024.0 * 1024.0);
long bytes = 5_368_709_120L;
public class Main {
public static void main(String[] args) {public class Main {
public static void main(String[] args) {
long bytes = 5_368_709_120L;
double megabytes = bytes / (1024.0 * 1024.0);
System.out.printf("%.2f MB%n", megabytes);
}
}Why: The class and method headers must come first. bytes must be declared before it can be used, and it needs the L suffix because 5.3 billion overflows int. We divide by 1024.0 (a double) so the division is floating-point, not integer — giving 5120.00 MB.
🧠 Quick Recall
1. What does this print?
System.out.println(Integer.MAX_VALUE + 1);-2147483648. Adding 1 to the largest int overflows and wraps to the smallest int.
2. What does this print?
char c = 'A';
System.out.println((char)(c + 2));C. 'A' is 65, plus 2 is 67, and casting 67 back to char gives 'C'.
3. What does this print?
System.out.println(7 / 2);
System.out.println(7 / 2.0);3 then 3.5. The first is integer division (both ints, decimal dropped); the second has a double operand, so it's floating-point division.
Common Beginner Mistakes
- ❌ Forgetting the L on big longs: long n = 3000000000; won't compile — the literal is read as an int first and overflows. Write 3000000000L.
- ❌ Forgetting the f on floats: float f = 3.14; fails because 3.14 is a double. Write 3.14f.
- ❌ Trusting double for money: 0.1 + 0.2 is 0.30000000000000004. Use BigDecimal for currency.
- ❌ Assuming overflow errors: it doesn't throw — it wraps. Use long or Math.addExact when overflow matters.
- ❌ Single vs double quotes: 'A' is a char; "A" is a String. Mixing them is a compile error.
📋 Quick Reference
| Type | Size | Range | Notes |
|---|---|---|---|
| byte | 8-bit | -128 .. 127 | Raw bytes; integer family |
| short | 16-bit | -32,768 .. 32,767 | Rarely used |
| int | 32-bit | ~ -2.1B .. 2.1B | Default whole number |
| long | 64-bit | ~ -9.2 .. 9.2 × 10¹⁸ | Needs L suffix: 10L |
| float | 32-bit | ~7 decimal digits | Needs f suffix: 3.14f |
| double | 64-bit | ~15 decimal digits | Default decimal number |
| char | 16-bit | 0 .. 65,535 | Single char in 'quotes'; a number |
| boolean | 1-bit* | false / true | Only true or false |
| Integer.MAX_VALUE | — | 2,147,483,647 | int max constant |
| Integer.MIN_VALUE | — | -2,147,483,648 | int min constant |
| Long.MAX_VALUE | — | 9,223,372,036,854,775,807 | long max constant |
| 1_000_000 | — | 1000000 | Underscores aid readability |
| MAX + 1 | — | wraps to MIN | Overflow is silent, not an error |
| BigDecimal | class | exact | Use for money, not double |
🎉 Lesson Complete!
You now know all 8 primitive types, their exact ranges, why long needs L and float needs f, how overflow silently wraps, and why double is wrong for money. Most of your code will use int, double, and boolean — but now you know what the others are for.
Practice quiz
How many primitive types does Java have?
- 6
- 7
- 8
- 9
Answer: 8. Java has exactly 8 primitives: byte, short, int, long, float, double, char, boolean.
What does System.out.println(Integer.MAX_VALUE + 1); print?
- -2147483648
- 2147483648
- 0
- An error
Answer: -2147483648. Adding 1 to the largest int overflows and silently wraps around to Integer.MIN_VALUE, -2147483648.
Why does 'long big = 3000000000;' fail to compile?
- long is not a real type
- long needs quotes
- 3 billion is too small
- The literal is read as an int first and overflows
Answer: The literal is read as an int first and overflows. Whole-number literals are int by default; 3 billion overflows int. Add the L suffix: 3000000000L.
What is the value of (char)(c + 1) when char c = 'A';?
- 'A'
- 'B'
- 66
- 'a'
Answer: 'B'. 'A' is code point 65; 65 + 1 = 66, and casting back to char gives 'B'.
Which primitive is 64 bits and the default for decimal numbers?
- double
- float
- long
- int
Answer: double. double is 64-bit and the default floating-point type; float (32-bit) needs an f suffix.
What does 7 / 2.0 evaluate to?
- 3
- 4
- 3.5
- 3.0
Answer: 3.5. Because one operand is a double, it is floating-point division, giving 3.5.
What happens on integer overflow in Java?
- It throws an exception
- It silently wraps around to the minimum
- The program halts
- It returns 0
Answer: It silently wraps around to the minimum. Java does not throw on overflow — it silently wraps around, which is a real source of bugs.
What does System.out.println(0.1 + 0.2); print?
- 0.3
- 0.30
- An error
- 0.30000000000000004
Answer: 0.30000000000000004. double is binary floating point and cannot store 0.1 exactly, so the result is 0.30000000000000004.
What is the range of an int?
- -128 to 127
- about -2.1 billion to 2.1 billion
- -32,768 to 32,767
- 0 to 65,535
Answer: about -2.1 billion to 2.1 billion. int is 32-bit, ranging from -2,147,483,648 to 2,147,483,647 (about ±2.1 billion).
Why does 'float f = 3.14;' fail to compile?
- 3.14 is too large
- float cannot hold decimals
- 3.14 is a double, too wide for float
- It needs a long suffix
Answer: 3.14 is a double, too wide for float. Decimal literals are double by default; assigning a double to a float needs the f suffix: 3.14f.
Continue this course
- Previous: Variables and Data Types
- Next: Type Casting — Widening, narrowing, and converting between types safely
- Quick reference: Java cheat sheet
Frequently asked questions
Why does Java have eight primitive types instead of just 'number'?
Each primitive maps to a fixed amount of memory, which makes Java fast and predictable. A byte is 8 bits, an int is 32 bits, a long is 64 bits. Smaller types use less memory and are useful when you store millions of values (such as image pixels or file bytes), while bigger types hold larger ranges. Most of the time you only need int, double, and boolean — the others exist for performance and special cases.
What is the difference between a primitive and a reference type?
A primitive (int, double, boolean, char, byte, short, long, float) stores its actual value directly and can never be null. A reference type (String, arrays, ArrayList, your own classes) stores a pointer to an object on the heap and can be null. Primitives are compared with ==; reference types should be compared with .equals().
Why do long and float need the L and f suffixes?
By default Java reads a whole number literal as an int and a decimal literal as a double. long big = 3000000000; fails because 3000000000 overflows int before it is ever assigned — the L tells Java to treat it as a long from the start. Likewise float f = 3.14; fails because 3.14 is a double that is too wide for float, so you write 3.14f.
What is integer overflow and why doesn't Java throw an error?
Each integer type has a fixed range. When a calculation goes past the maximum, it silently wraps around to the minimum (Integer.MAX_VALUE + 1 becomes Integer.MIN_VALUE). Java does this for speed — checking every operation would be slow. It is a real source of bugs in counters and timestamps, which is why huge values use long, and money/IDs sometimes use BigInteger.
Should I use double for money?
No, not for real financial code. double is binary floating point, so values like 0.1 cannot be stored exactly and rounding errors accumulate. For currency, use BigDecimal or store amounts as an integer number of cents. double is fine for learning, measurements, and science where tiny rounding is acceptable.