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

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.).

TypeSizeMinimumMaximumDefault
byte8-bit-1281270
short16-bit-32,76832,7670
int32-bit-2,147,483,6482,147,483,6470
long64-bit-9.2 × 10¹⁸9.2 × 10¹⁸0L
float32-bit~±3.4 × 10³⁸~7 digits0.0f
double64-bit~±1.8 × 10³⁰⁸~15 digits0.0
char16-bit'\u0000' (0)65,535'\u0000'
boolean1-bit*falsetruefalse

* 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'; // true

This 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:

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

📋 Quick Reference

TypeSizeRangeNotes
byte8-bit-128 .. 127Raw bytes; integer family
short16-bit-32,768 .. 32,767Rarely used
int32-bit~ -2.1B .. 2.1BDefault whole number
long64-bit~ -9.2 .. 9.2 × 10¹⁸Needs L suffix: 10L
float32-bit~7 decimal digitsNeeds f suffix: 3.14f
double64-bit~15 decimal digitsDefault decimal number
char16-bit0 .. 65,535Single char in 'quotes'; a number
boolean1-bit*false / trueOnly true or false
Integer.MAX_VALUE—2,147,483,647int max constant
Integer.MIN_VALUE—-2,147,483,648int min constant
Long.MAX_VALUE—9,223,372,036,854,775,807long max constant
1_000_000—1000000Underscores aid readability
MAX + 1—wraps to MINOverflow is silent, not an error
BigDecimalclassexactUse 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

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.