Java Data Types

Su Suriya Ravichandran Updated 14 Nov 2026
8 min read ·Lesson 8 of 133

Every value in Java has a type, and the type determines three things: how much memory the value occupies, what range of values it can hold, and which operations are valid on it. Java's types fall into two groups: eight primitive types built into the language, and reference types (classes, interfaces, arrays, records, enums), which are unlimited in number.

Primitive Types

Primitives hold a value directly, are never null, and are not objects.

TypeSizeRangeDefaultTypical use
byte8-bit−128 to 1270Raw binary data, I/O buffers
short16-bit−32,768 to 32,7670Rarely used; memory-constrained arrays
int32-bit−2,147,483,648 to 2,147,483,6470Default integer type
long64-bit±9.2 × 10¹⁸0LLarge counts, timestamps, IDs
float32-bit~±3.4 × 10³⁸, ~7 significant digits0.0fGraphics, large numeric arrays
double64-bit~±1.8 × 10³⁰⁸, ~15 significant digits0.0Default floating-point type
char16-bit'\u0000' to '\uffff' (0 to 65,535)'\u0000'Single UTF-16 code unit
booleanJVM-dependenttrue or falsefalseConditions and flags

Sizes are fixed by the language specification and identical on every platform. An int is 32 bits on a Raspberry Pi and on a 64-bit server. This is part of what makes compiled Java code portable.

Integer types

byte  b = 100;
short s = 30_000;
int   i = 2_000_000_000;
long  l = 9_000_000_000L;

An integer literal is an int by default. A value outside int range needs the L suffix to be read as a long; without it, 9_000_000_000 is a compile error (integer number too large). Lowercase l is also accepted but looks like the digit 1, so uppercase is universal.

Integer arithmetic wraps silently on overflow:

int max = Integer.MAX_VALUE;   // 2147483647
int wrapped = max + 1;         // -2147483648

No exception is thrown. The result rolls over to the minimum value. When overflow must be detected, Math.addExact, Math.multiplyExact, and related methods throw ArithmeticException instead of wrapping. For values that can grow without bound, use java.math.BigInteger.

Use int unless there is a reason not to. long is for values that can exceed about two billion: milliseconds since 1970, file sizes, database IDs. byte and short do not make arithmetic faster; the JVM computes in int anyway, so they are useful mainly for compact arrays and binary protocols.

Floating-point types

float  f = 3.14f;
double d = 3.14;
double sci = 6.022e23;   // scientific notation

A decimal literal is a double by default; float f = 3.14; is a compile error because assigning a double to a float may lose precision. The f suffix makes the literal a float.

Floating-point numbers are binary approximations, and most decimal fractions cannot be represented exactly:

System.out.println(0.1 + 0.2);          // 0.30000000000000004
System.out.println(0.1 + 0.2 == 0.3);   // false

This is not a Java bug; it is inherent to IEEE 754 binary floating point and happens in every language that uses it. The consequences:

  • Never compare floating-point values with ==. Compare the absolute difference against a small tolerance.
  • Never use float or double for money. Use java.math.BigDecimal, which represents decimal fractions exactly, or store amounts as long cents.

Floating-point types also have special values: Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, and Double.NaN (not a number). Dividing a double by zero produces infinity rather than throwing; 0.0 / 0.0 produces NaN. NaN is not equal to anything, including itself, so Double.isNaN(x) is the only correct test.

double is the default for a reason: it has roughly twice the precision of float and on modern hardware is not slower. Use float only when memory matters, such as large arrays of coordinates, or when an API (OpenGL, some ML libraries) requires it.

char

A char holds a single 16-bit UTF-16 code unit, written in single quotes.

char letter = 'A';
char digit = '7';
char newline = '\n';
char unicode = '\u00E9';   // é
char fromCode = 65;        // also 'A': chars are numeric

Because char is a numeric type, arithmetic works on it:

char c = 'A';
c++;                        // 'B'
int code = 'A';             // 65
char next = (char) ('A' + 2); // 'C'; the cast is required because 'A' + 2 is an int

Characters outside the Basic Multilingual Plane (most emoji, some CJK characters) need two char values, a surrogate pair. A single char cannot hold them; use an int code point and the Character class methods when handling such text.

boolean

A boolean is either true or false. It cannot be converted to or from a number:

boolean ready = true;
int flag = ready ? 1 : 0;    // explicit conversion via the ternary operator
boolean b = 1;               // compile error
if (ready == true) { }       // works but redundant; write if (ready)

Conditions in if, while, and for must be boolean. The JVM specification does not fix the storage size of a boolean; it is typically one byte in arrays and an int-sized slot on the stack.

Reference Types

Everything that is not a primitive is a reference type. A reference variable holds the address of an object (or null); the object itself lives on the heap.

String name = "Ada";             // String
int[] scores = {90, 85, 77};     // array
List<String> items = new ArrayList<>();   // interface type, implemented by ArrayList
LocalDate today = LocalDate.now();        // class from java.time

Reference types start with an uppercase letter by convention, which is the quickest visual way to tell them from primitives. Their default value in a field is null.

String

String is a class, not a primitive, but it has language-level support: literals in double quotes, + for concatenation, and automatic conversion of other values when concatenated. Strings are immutable: no method changes a String; every modifying operation returns a new one.

String s = "hello";
s.toUpperCase();              // returns "HELLO" but s is unchanged
s = s.toUpperCase();          // now s is "HELLO"

Strings are covered in depth in their own section.

Arrays

An array holds a fixed number of values of one type. The type of an array of int is int[].

int[] counts = new int[5];       // five ints, all 0
String[] names = {"Ada", "Linus"};
counts[0] = 42;
System.out.println(names.length);   // 2

An array of primitives stores the values directly in a contiguous block. An array of objects stores references, initially all null.

Wrapper Classes

Each primitive has a corresponding class in java.lang:

PrimitiveWrapper
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

Wrappers exist because generics and collections work only with reference types. List<int> is illegal; List<Integer> is the form.

List<Integer> numbers = new ArrayList<>();
numbers.add(5);              // autoboxing: int 5 becomes Integer 5
int first = numbers.get(0);  // unboxing: Integer becomes int

The compiler inserts the conversions automatically (autoboxing and unboxing). Two consequences deserve attention:

Integer a = 127, b = 127;
Integer c = 1000, d = 1000;
System.out.println(a == b);       // true  (small values are cached)
System.out.println(c == d);       // false (different objects)
System.out.println(c.equals(d));  // true

== on wrapper objects compares references, not values. Only values from −128 to 127 are guaranteed to come from a cache. Always compare wrappers with equals or unbox them first.

Integer count = null;
int n = count;    // NullPointerException: unboxing null

A wrapper can be null; a primitive cannot. Unboxing null throws. This is a frequent source of bugs when reading from maps whose keys may be absent.

Wrapper classes also host utility methods: Integer.parseInt("42"), Double.parseDouble("3.5"), Integer.MAX_VALUE, Character.isDigit('7'), Integer.toBinaryString(10).

Primitive Versus Reference: Side by Side

AspectPrimitiveReference
StoresThe value itselfA reference to an object
Can be nullNoYes
Default in a field0, 0.0, false, '\u0000'null
Compared with ==By valueBy identity (same object)
Compared with equalsNot applicableBy content, if the class defines it
Usable in genericsNo (use wrapper)Yes
MemoryFixed, small, on the stack or inline in objectsObject on the heap plus a reference
PerformanceFastestExtra indirection and allocation

The performance difference matters in tight loops and large arrays. An int[] of a million elements is four megabytes of contiguous memory; an Integer[] is a million separate objects plus a million references. For bulk numeric work, primitives and primitive arrays are the right choice.

Choosing a Type

  • Whole numbers: int. Switch to long if values can exceed ±2 billion.
  • Decimal numbers for science, graphics, statistics: double.
  • Money or anything requiring exact decimals: BigDecimal.
  • Single character: char. Text: String.
  • Yes/no: boolean.
  • A value that might be absent in a collection or generic context: the wrapper class, with explicit null handling, or Optional.
  • A fixed-size sequence of one type: array. A growable sequence: List.
public class Product {
    private final long id;            // IDs can be large; long is standard
    private final String name;        // text
    private final BigDecimal price;   // money: exact decimal arithmetic
    private int stock;                // small whole number that changes
    private boolean discontinued;     // flag
    private final char sizeCode;      // single letter: 'S', 'M', 'L'

    public Product(long id, String name, BigDecimal price, int stock, char sizeCode) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.stock = stock;
        this.sizeCode = sizeCode;
    }
}

Each field uses the narrowest type that correctly represents the data. price is a BigDecimal rather than a double so that 19.99 * 3 is exactly 59.97, not 59.970000000000006.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about Java Data Types and get answers drawn from it.

Signed-in readers only.