JavaBasicsbeginner
Updated:

Java Data Types Explained with Examples

7 min read

Master all 8 primitive types, understand reference types, and learn the casting rules that decide whether your Java code compiles, overflows or behaves.

TL;DR – Quick Answer

Java has two families of data types. The 8 primitive types (byte, short, int, long, float, double, char, boolean) store simple values directly and have fixed sizes. Reference types (String, arrays, and every class or interface) store the address of an object on the heap. For everyday code you will mostly use int, long, double, boolean and String.

On This Page

Every value in a Java program has a type, and the compiler enforces those types before a single line runs. That strictness is Java's biggest gift to beginners: entire categories of bugs get caught at compile time instead of exploding in production. But it also means you cannot write serious Java until the type system is second nature.

This guide covers both families — primitives and reference types — plus the casting rules and overflow behavior that interviewers use to separate memorizers from people who actually understand the machine.

The two families: primitive vs reference

Java splits all types into two camps:

  • Primitive types store the value itself, directly in the variable. There are exactly 8, they are built into the language, and their names are lowercase: int, double, boolean and friends.
  • Reference types store a reference (an address) to an object living on the heap. String, arrays, ArrayList, and every class you write are reference types.

The practical differences show up everywhere:

Aspect Primitive Reference
Stores The actual value Address of an object
Can be null Never Yes
Default (as a field) 0 / 0.0 / false / '' null
Compared with == Compares values Compares addresses
Memory Fixed, small (1–8 bytes) Object overhead + fields

That fourth row causes real bugs: == on two String objects compares references, not text. Why strings behave this way — and the string pool that complicates it — gets full treatment in Java strings.

The 8 primitive types at a glance

Type Size Range Default Typical use
byte 8-bit -128 to 127 0 Raw binary data, file I/O
short 16-bit -32,768 to 32,767 0 Rare; legacy and memory-tight arrays
int 32-bit approx. ±2.14 billion 0 Default choice for whole numbers
long 64-bit approx. ±9.2 quintillion 0L Timestamps, IDs, big counts
float 32-bit ~6-7 decimal digits precision 0.0f Rare; graphics, ML model weights
double 64-bit ~15 decimal digits precision 0.0d Default choice for decimals
char 16-bit a single UTF-16 code unit '' Single characters
boolean JVM-dependent true / false false Conditions and flags

Two habits worth forming immediately: use int for whole numbers and double for decimals unless you have a measured reason not to. byte, short and float exist for specific niches; reaching for them "to save memory" in ordinary code is premature optimization.

Here is each family in action:

public class PrimitivesTour {
    public static void main(String[] args) {
        int seats = 30;                  // everyday whole number
        long worldPopulation = 8_100_000_000L;  // needs L suffix: too big for int
        double price = 1499.99;          // everyday decimal
        float rating = 4.5f;             // needs f suffix
        char grade = 'A';                // single quotes for char
        boolean enrolled = true;

        byte flags = 0b0101;             // binary literal
        short year = 2026;

        System.out.println(seats + " seats, price " + price
                + ", grade " + grade + ", enrolled: " + enrolled);
        System.out.println("Population: " + worldPopulation
                + ", rating: " + rating + ", flags: " + flags + ", year: " + year);
    }
}

Notice the suffixes: L makes a literal long, f makes it float. Without L, the literal 8_100_000_000 fails to compile because integer literals are int by default and that value exceeds the int range. The underscores in numeric literals are legal and purely for readability.

Pro tip: When a whole number might ever exceed 2.14 billion — database IDs, epoch milliseconds, view counts — start with long. Migrating a codebase from int to long after an overflow incident is painful; System.currentTimeMillis() returning long is the API's way of telling you this.

Integer overflow: the silent wraparound

Java does not throw an error when an int calculation exceeds its range. It silently wraps around, which produces spectacular bugs:

public class OverflowDemo {
    public static void main(String[] args) {
        int max = Integer.MAX_VALUE;         // 2,147,483,647
        System.out.println(max + 1);          // -2147483648 (wrapped!)

        // int tooBig = 3_000_000_000;       // compile error: exceeds int range
        long safe = 3_000_000_000L;           // correct: use long
        System.out.println("Safe value: " + safe);

        // classic interview bug: multiplying ints before widening
        long wrong = 2_000_000 * 2_000;       // overflows as int math first
        long right = 2_000_000L * 2_000;      // long math from the start
        System.out.println("wrong = " + wrong + ", right = " + right);
    }
}

The last pair is the one to internalize: 2_000_000 * 2_000 is computed in int arithmetic before being assigned to long, so the damage is already done. Making one operand long forces the whole multiplication into 64-bit math.

Interview note: "What happens when int overflows in Java?" The answer is silent wraparound (modulo 2^32), never an exception. Mention Math.addExact() and Math.multiplyExact(), which throw ArithmeticException on overflow, and you have turned a basic question into a strong answer.

Floating point: why 0.1 + 0.2 != 0.3

double stores numbers in binary, and many decimal fractions have no exact binary representation. Run this once and never forget it:

public class MoneyMistake {
    public static void main(String[] args) {
        System.out.println(0.1 + 0.2);            // 0.30000000000000004
        System.out.println(0.1 + 0.2 == 0.3);     // false

        // The right way for money: BigDecimal from Strings
        java.math.BigDecimal fee = new java.math.BigDecimal("999.10");
        java.math.BigDecimal gst = new java.math.BigDecimal("179.84");
        System.out.println(fee.add(gst));          // 1178.94 exactly
    }
}

double is fine for scientific calculations, percentages and averages where tiny imprecision is acceptable. For money, use BigDecimal constructed from a String (never from a double, which imports the imprecision you were escaping) or keep amounts in integer paise.

char and boolean: the odd ones out

char is a 16-bit unsigned type holding one UTF-16 code unit. It uses single quotes ('A'), and because it is numeric underneath, 'A' + 1 is 66. That numeric nature enables tricks like char next = (char) ('A' + 1); yielding 'B'.

boolean holds only true or false. Unlike C, Java refuses to treat numbers as booleans — if (1) is a compile error, which kills the classic if (x = 5) typo-bug at the source. Booleans drive the conditions you will write with Java operators in every if and loop.

Reference types and null

Every non-primitive is a reference type: String, arrays, collections, your own classes. Three consequences matter from day one:

  1. They can be null — meaning "refers to nothing". Calling a method on a null reference throws NullPointerException, the most common exception in all of Java.
  2. Assignment copies the reference, not the object. Two variables can point at the same object; changing it through one is visible through the other.
  3. == compares references. Use .equals() to compare object contents.

Arrays deserve special mention: even an array of primitives like int[] is itself a reference type living on the heap. Their behavior gets a full guide in Java arrays.

Type casting: widening and narrowing

Java converts between numeric types in two directions:

Widening (implicit, safe): small type to large type happens automatically because no information can be lost. byte → short → int → long → float → double.

Narrowing (explicit, risky): large to small requires a cast operator and can lose data:

public class CastingDemo {
    public static void main(String[] args) {
        int rupees = 500;
        double amount = rupees;          // widening: automatic, 500.0

        double precise = 99.99;
        int truncated = (int) precise;   // narrowing: cast required
        System.out.println(truncated);   // 99 — decimal part DROPPED, not rounded

        int big = 130;
        byte b = (byte) big;             // 130 doesn't fit in byte
        System.out.println(b);           // -126 — wrapped around
    }
}

Two behaviors to notice: casting double to int truncates toward zero (99.99 becomes 99, not 100 — use Math.round() if you want rounding), and narrowing an out-of-range value wraps around silently, just like overflow.

Common mistake: A common mistake beginners make is assuming (int) 99.99 rounds to 100. Casting always truncates the fraction. If a fee calculation or percentage looks "off by one" in your program, an accidental truncating cast is the first suspect.

Wrapper classes and autoboxing

Each primitive has an object twin: int/Integer, double/Double, boolean/Boolean and so on. Wrappers exist because generics and collections work only with objects — you write ArrayList<Integer>, never ArrayList<int>.

Java converts between the two automatically (autoboxing and unboxing), which keeps code clean but hides two traps: unboxing a null Integer throws NullPointerException, and == between two Integer objects compares references (with a confusing cache for values -128 to 127). Rule of thumb: compare wrappers with .equals(), and prefer primitives in fields and loops unless null is a meaningful state.

Choosing the right type: a decision guide

  • Whole number, fits in ±2.14 billion → int
  • Whole number, might not fit (IDs, timestamps, money in paise) → long
  • Decimal for science/statistics → double
  • Money → BigDecimal or long paise
  • Yes/no state → boolean
  • Text → String; single character → char
  • Inside collections/generics → wrapper classes

Types are half the story of storing data — the other half is where and how you declare the variables that hold them, which is exactly where this series goes next. And if you are still setting up your environment, start from what is Java to get the toolchain in place first.

Frequently Asked Questions

How many data types are there in Java?
Java has exactly 8 primitive types: byte, short, int, long, float, double, char and boolean. Beyond those, every class, interface, enum and array is a reference type, so the number of reference types is unlimited — you create new ones every time you write a class.
Is String a primitive data type in Java?
No. String is a class in java.lang, which makes it a reference type. It feels primitive because Java gives it special support like string literals and the + operator, but a String variable holds a reference to an object, not the characters themselves.
What is the default value of int and other primitives?
Fields get automatic defaults: 0 for byte, short, int and long, 0.0 for float and double, the null character for char, and false for boolean. Reference type fields default to null. Local variables inside methods get no default — using one before assigning it is a compile error.
Why should I not use float or double for money?
float and double use binary floating point, which cannot represent many decimal fractions exactly — 0.1 + 0.2 does not equal 0.3 exactly. For currency, use BigDecimal or store amounts as long paise or cents to avoid rounding errors in financial calculations.
What is the difference between int and Integer?
int is a primitive that stores the number directly and can never be null. Integer is a wrapper class, a reference type that boxes an int inside an object. Collections like ArrayList can only hold objects, so you use Integer there, and Java autoboxes between the two.
What is the range of int in Java?
int is a 32-bit signed integer with a range of -2,147,483,648 to 2,147,483,647 — roughly plus or minus 2.14 billion. If a value can exceed that, such as a phone number stored as digits or a large ID, use long instead.

Want to Build Your Career in Java Full Stack with AI?

Join CodeBegun and train with working industry engineers — Explore the Program

Apply for Demo Class →
Siva Prasad Galaba
Founder, CodeBegun · Staff Engineer

Founder of CodeBegun. 15+ years building Java systems at companies like Crunchyroll. Teaches Java, Spring Boot and system design the way the industry actually works, and mentors students through projects, mock interviews and placement preparation.

Technically reviewed by CodeBegun Technical TeamLast reviewed 14 July 2026 LinkedIn
Chat with us