1.1 Primitive Types, Literals, Wrapper Classes, and Type Promotion/Casting

Key Takeaways

  • Java defines eight primitive types: byte, short, int, long, float, double, char, and boolean; member fields receive default zero-equivalent values, whereas local variables must be explicitly initialized prior to access.

  • Numeric literal representations include binary (0b), octal (0), and hexadecimal (0x); underscores are permitted strictly between digits and never adjacent to prefixes, decimal points, or type suffixes.

  • Primitive wrapper classes (Byte, Short, Integer, Long, Character, Boolean) cache immutable instances across standardized ranges (-128 to 127 for integers); comparison with reference equality (==) outside these bounds fails.

  • Binary arithmetic operations automatically promote any operand smaller than int (byte, short, char) to int before evaluation, requiring explicit casts to assign results back to narrower types unless evaluating compile-time constant expressions.

  • Integer overflow and underflow wrap silently across two's complement boundary limits without throwing runtime exceptions; widening conversions occur implicitly, whereas narrowing conversions require explicit casting.

Last updated: October 2026

Primitive Types, Literals, Wrapper Classes, and Type Promotion/Casting

Understanding Java's fundamental data representations requires mastering primitive types, numeric literal syntaxes, wrapper class caching, and arithmetic promotion rules. Professional Java certification exams frequently test edge cases in type conversion, literal formatting, and wrapper identity.


The Eight Primitive Data Types

Java provides eight built-in primitive types divided into four categories: integers, floating-point numbers, characters, and booleans. Primitive values reside directly in stack memory when declared as local variables or embedded within object layouts on the heap when declared as instance fields.

TypeSizeRangeWrapper ClassDefault Value (Fields Only)
byte8 bits (1 byte)-128 to 127 (−27-2^7 to 27−12^7 - 1)java.lang.Byte0
short16 bits (2 bytes)-32,768 to 32,767 (−215-2^{15} to 215−12^{15} - 1)java.lang.Short0
int32 bits (4 bytes)-2,147,483,648 to 2,147,483,647 (−231-2^{31} to 231−12^{31} - 1)java.lang.Integer0
long64 bits (8 bytes)-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807java.lang.Long0L
float32 bits (4 bytes)IEEE 754 floating point (approx. ±1.4×10−45\pm 1.4 \times 10^{-45} to ±3.4×1038\pm 3.4 \times 10^{38})java.lang.Float0.0f
double64 bits (8 bytes)IEEE 754 floating point (approx. ±4.9×10−324\pm 4.9 \times 10^{-324} to ±1.8×10308\pm 1.8 \times 10^{308})java.lang.Double0.0d
char16 bits (2 bytes)0 to 65,535 unsigned ('\u0000' to '\uffff')java.lang.Character'\u0000' (null character)
booleanJVM-dependenttrue or falsejava.lang.Booleanfalse

Warning

Field Defaults vs. Local Variable Initialization Only instance and static fields receive automatic default values during class loading or object instantiation. Local variables declared inside method bodies, constructor bodies, or initializer blocks receive no default value. Attempting to read a local variable before it is definitively assigned results in a compile-time error: variable x might not have been initialized.

public class InitializationDemo {
    int instanceCount; // Defaults to 0
    boolean active;    // Defaults to false

    public void compute() {
        int localSum; // Uninitialized
        // System.out.println(localSum); // COMPILE ERROR: localSum might not have been initialized
        localSum = 10;
        System.out.println(localSum); // Compiles successfully: prints 10
    }
}

Literal Formats and Numeric Notations

Integral literals default to int unless suffixed with uppercase L or lowercase l (always use L for readability). Floating-point literals default to double unless suffixed with F or f (D or d is optional for double).

Radix Prefixes

Java supports four number systems for integer literals:

  • Decimal (base 10): Standard digits 0–9 without leading zeros (e.g., 42).
  • Hexadecimal (base 16): Prefixed with 0x or 0X, digits 0–9 and A–F (case-insensitive, e.g., 0x2A is decimal 42).
  • Binary (base 2): Prefixed with 0b or 0B, digits 0 and 1 (e.g., 0b101010 is decimal 42).
  • Octal (base 8): Prefixed with a single leading 0, digits 0–7 (e.g., 052 is decimal 42).

Warning

The Octal Trap Any integer literal starting with 0 is interpreted as octal. Using digits 8 or 9 in an octal literal causes a compiler error: integer number too large: 089 (or digit '8' out of range). Always watch for leading zeros in test questions!

Underscore Placement Rules

Underscores (_) can be inserted between digits to enhance readability in large numeric literals. However, strict placement constraints apply:

  • Allowed: Strictly between adjacent digits (e.g., 1_000_000, 0b1011_0100, 0xFF_EC_01).
  • Prohibited:
    • At the very beginning or end of a literal: _100 (treated as identifier), 100_ (compile error).
    • Adjacent to a decimal point: 3._1415 or 3_.1415 (compile error).
    • Adjacent to radix prefixes: 0x_FF, 0_xFF, 0b_1010 (compile error).
    • Adjacent to type suffixes: 1000_L, 4.5_F, 99.9_D (compile error).
int validDec = 1_000_000;         // Valid
int validHex = 0xCAFE_BABE;       // Valid
long validBin = 0b1101_0010_1001L; // Valid

// int err1 = 0x_7F;   // COMPILE ERROR: cannot put underscore after 0x
// double err2 = 12_.5; // COMPILE ERROR: cannot put underscore before decimal point
// float err3 = 100_f;  // COMPILE ERROR: cannot put underscore before 'f' suffix

Wrapper Classes, Autoboxing, and Value Caching

Each primitive type has a corresponding wrapper class in java.lang. Modern Java provides automatic conversion between primitives and wrappers:

  • Autoboxing: Automatic conversion of a primitive to its corresponding wrapper object (e.g., Integer num = 42;).
  • Unboxing: Automatic extraction of the primitive value from a wrapper object (e.g., int val = num;).
Integer boxed = 100;     // Autoboxing via Integer.valueOf(100)
int unboxed = boxed;     // Unboxing via boxed.intValue()

Integer nullRef = null;
// int failedUnbox = nullRef; // Compiles, but throws NullPointerException at runtime!

The Flyweight Value Cache

To conserve memory and boost performance, the JVM maintains an internal cache of pre-allocated wrapper instances. When autoboxing occurs or when calling valueOf(), the JVM returns a reference to a cached instance for values within the following boundaries:

  • Byte, Short, Integer, Long: -128 through 127 (inclusive).
  • Character: 0 through 127 ('\u0000' through '\u007F').
  • Boolean: Boolean.TRUE and Boolean.FALSE.
  • Float and Double: No caching (infinite floating-point representations).

Because wrapper objects are reference types, the == operator compares object identity (heap memory addresses), whereas .equals() compares wrapped primitive values:

Integer a = 127;
Integer b = 127;
System.out.println(a == b);      // true (both point to the cached flyweight instance)

Integer c = 128;
Integer d = 128;
System.out.println(c == d);      // false (values outside cache allocate new heap objects)
System.out.println(c.equals(d)); // true (compares internal int value: 128 == 128)

Note

Constructors for all primitive wrappers (e.g., new Integer(10)) were deprecated in Java 9 and marked for removal in Java 16 (JEP 390) because they bypass caching and allocate redundant heap objects. In Java 17 they still compile, but with a removal warning. Always prefer autoboxing or static factory methods such as Integer.valueOf(10).


Type Promotion and Casting Rules

Java enforces strict binary numeric promotion rules for arithmetic expressions (+, -, *, /, %). The operands are widened according to the following precedence order:

  1. If either operand is double, the other is converted to double.
  2. Otherwise, if either operand is float, the other is converted to float.
  3. Otherwise, if either operand is long, the other is converted to long.
  4. Otherwise, BOTH operands are converted to int, even if neither was originally an int!
byte b1 = 10;
byte b2 = 20;
// byte b3 = b1 + b2; // COMPILE ERROR: b1 + b2 evaluates to int; cannot assign int to byte
byte b3 = (byte)(b1 + b2); // Explicit cast required

Compile-Time Constant Inlining

An exception to mandatory narrowing casts occurs with compile-time constant expressions. When the compiler can evaluate an expression at compile time and the resulting value falls within the range of the target smaller integer type (byte, short, char), the assignment is permitted without an explicit cast:

final byte f1 = 20;
final byte f2 = 30;
byte f3 = f1 + f2; // Compiles! Evaluated at compile time as 50, fitting into byte

final int bigConst = 1000;
// byte f4 = bigConst; // COMPILE ERROR: 1000 exceeds byte range (-128 to 127)

Widening vs. Narrowing and Two's Complement Overflow

Widening conversions (e.g., byte -> short -> int -> long -> float -> double) happen automatically without data loss of magnitude (except potential least-significant-digit precision loss when converting int to float, or long to float or double). Narrowing conversions truncate higher-order bits and wrap around two's complement boundaries without throwing exceptions:

int high = 130;
byte narrowed = (byte) high;
System.out.println(narrowed); // Prints -126 (binary 00000000_00000000_00000000_10000010 truncated to 10000010 = -126)

int max = Integer.MAX_VALUE; // 2,147,483,647
System.out.println(max + 1); // Prints -2,147,483,648 (silent overflow to Integer.MIN_VALUE)

Finally, boolean is entirely incompatible with numeric types: it cannot be cast to or from any integer or floating-point type, neither implicitly nor explicitly ((int) true is a compile error).

Test Your Knowledge

Which of the following numeric literal declarations compiles successfully without any compiler errors?

A

int oct = 089;

B

long b = 0b1011_0100L;

C

float f = 3.14_f;

D

double total = 1_000_.0;

Test Your Knowledge

Consider the following code snippet:

final byte a = 40;
byte b = 50;
byte c = a + 10;
byte d = b + 10;
System.out.println(c + " " + d);

What is the result of attempting to compile and run this code?

A

Throws a ClassCastException at runtime

B

Outputs 50 60

C

Compilation fails on the assignment to d (byte d = b + 10;)

D

Compilation fails on the assignment to c (byte c = a + 10;)

Test Your Knowledge

What is the output of the following code snippet?

Integer x = 127;
Integer y = 127;
Integer m = 128;
Integer n = 128;
System.out.print((x == y) + " ");
System.out.print((m == n) + " ");
System.out.print(m.equals(n));
A

true false true

B

true true true

C

false false false

D

false false true

Sections you finish are checked off in the contents.