Java Data Types: Primitive and Reference Types Explained

Primitive Data Types

Primitive types are the most basic building blocks in Java. They hold raw values and are not objects. There are eight primitive types, categorized into numeric, character, and boolean types.

Numeric Types

  • byte: 8-bit signed integer, range: -128 to 127
  • short: 16-bit signed integer, range: -32,768 to 32,767
  • int: 32-bit signed integer, range: -2,147,483,648 to 2,147,483,647 (most common used)
  • long: 64-bit signed integer, range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 (use suffix 'L' for literals)

For floating-point numbers:

  • float: 32-bit IEEE 754 single-precision, approx. ±3.4×10³⁸, 6–7 significant digits
  • double: 64-bit IEEE 754 double-precision, approx. ±1.8×10³⁰⁸, 15–16 significant digits (default for decimal literals)

Example demonstrating floating-point precision issues:

double a = 0.1;
double b = 0.2;
double sum = a + b;
System.out.println(sum); // Output: 0.30000000000000004

double fraction = 1.0 / 10.0;
System.out.println(fraction == 0.1); // Output: false

Due to binary representation limitations, decimal fractions like 0.1 cannot be stored exactly. To handle financial or high-precision calculations, use java.math.BigDecimal:

import java.math.BigDecimal;

BigDecimal x = new BigDecimal("0.1");
BigDecimal y = new BigDecimal("0.2");
BigDecimal total = x.add(y);
System.out.println(total); // Output: 0.3

Character Type

char: 16-bit Unicode character (UTF-16), range: U+0000 to U+FFFF. Supports Unicode escape sequences:

char letterA = '\u0041'; // 'A'
char chinese = '\u4F60'; // '你'
char emoji = '\uD83D\uDE0A'; // 😀 (surrogate pair)

Boolean Type

boolean: Represents truth values true or false. Its memory size is implementation-dependent (typically 1 byte), though the JVM specification does not enforce a fixed size. It is used exclusively in conditional logic:

boolean isActive = true;
if (isActive) {
    System.out.println("System is active.");
}

Reference Data Types

Reference types point to objects in memory. They include classes, interfaces, arrays, and strings.

Classes and Interfaces

A class defines a blueprint for objects. An interface defines a contract of methods that implementing classes must fulfill.

interface Drawable {
    double area();
    void render();
}

class Circle implements Drawable {
    private final double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public double area() {
        return Math.PI * radius * radius;
    }

    @Override
    public void render() {
        System.out.println("Drawing a circle with radius " + radius);
    }
}

Drawable shape = new Circle(5.0);
System.out.println(shape.area()); // Output: 78.53981633974483

Interfaces may include default methods and constants:

interface Vehicle {
    int MAX_SPEED = 120; // implicitly public static final

    void start();

    default void stop() {
        System.out.println("Vehicle has stopped.");
    }
}

Arrays

Arrays store fixed-size sequences of elements of the same type. They are objects and are created with the new keyword.

// Declaration and initialization
int[] scores = new int[5];
scores[0] = 85;

// Static initialization
String[] days = {"Mon", "Tue", "Wed", "Thu", "Fri"};

// Enhanced for-loop traversal
for (String day : days) {
    System.out.println(day);
}

// Multidimensional array
int[][] grid = new int[3][3];
grid[1][2] = 42;

Array indices start at 0. Accessing an index outside bounds throws ArrayIndexOutOfBoundsException.

String Type

String is a final class in java.lang and represents an immutable sequence of characters. Once created, its content cannot change.

String greeting = "Hello";
String message = greeting + " World!"; // Creates a new String object

System.out.println(greeting.length());       // 5
System.out.println(greeting.charAt(0));      // 'H'
System.out.println(greeting.substring(1, 4)); // "ell"
System.out.println(greeting.equals("hello")); // false
System.out.println(greeting.equalsIgnoreCase("HELLO")); // true

// String formatting
String formatted = String.format("Name: %s, Age: %d", "Alice", 23);

String Pool: String literals are stored in a special memory area called the string constant pool. Repeated literals share the same object:

String s1 = "Java";
String s2 = "Java";
String s3 = new String("Java");

System.out.println(s1 == s2); // true (same reference)
System.out.println(s1 == s3); // false (different objects)
System.out.println(s1.equals(s3)); // true (same content)

Wrapper Classes and Autoboxing

Each primitive type has a corresponding wrapper class in java.lang:

Autoboxing automatically converts primitives to wrappers, and unboxing does the reverse:

Integer count = 100;           // autoboxing: int → Integer
int value = count;             // unboxing: Integer → int

Double price = Double.valueOf("99.99"); // parsing from string
String text = Double.toString(price);   // converting to string

Wrapper clases provide utility methods like parseInt(), valueOf(), and constants such as Integer.MAX_VALUE.

Key Characteristics of Reference Types

  • Encapsulation: Data and behavior are bundled within classes, with access controlled via modifiers.
  • Inheritance: Classes can extend other classes to reuse and extend functionality.
  • Polymorphism: Objects of different types can be treated uniformly via shared interfaces or superclasses.
  • Abstraction: Abstract classes and interfaces hide implementation details behind a defined API.

Reference types are stored on the heap, while their references (pointers) reside on the stack. Garbage collection automatically reclaims memory when no references to an object remain.

Understanding the distinction between primitive and reference types is essential for memory management, performance optimization, and avoiding common bugs such as unintended object references or null pointer exceptions.

Tags: java primitive-types reference-types autoboxing string-pool

Posted on Wed, 13 May 2026 05:35:10 +0000 by aspekt9