Variables & Data Types
BeginnerMaster Java's 8 primitive types, reference types, default values, type ranges, and modern local type inference with var.
Overview
Java is a statically-typed language — every variable has a fixed type determined at compile time. There are two categories: primitives (8 built-in types that store raw values directly on the stack) and reference types (objects stored on the heap, variable holds a memory address). Knowing the size, range, and default values of each primitive prevents bugs like integer overflow and is a favourite interview topic.
The 8 Primitive Types
Java has exactly 8 primitive types. They are not objects and have no methods.
byte (1 byte) — range −128 to 127 short (2 bytes) — range −32 768 to 32 767 int (4 bytes) — range −2³¹ to 2³¹−1 (≈ ±2.1 billion) long (8 bytes) — range −2⁶³ to 2⁶³−1; suffix L required for literals float (4 bytes) — IEEE-754 single-precision; suffix f required double (8 bytes) — IEEE-754 double-precision; default for decimals char (2 bytes) — single UTF-16 code unit; range 0 to 65 535 boolean — true or false; JVM size is implementation-dependent
Default values for instance/static fields: 0 / 0.0 / false / null. Local variables have no default — the compiler forces you to initialise them before use.
public class PrimitiveDemo {
// Instance fields → get default values
int defaultInt; // 0
double defaultDouble; // 0.0
boolean defaultBool; // false
public static void main(String[] args) {
byte b = 127;
short s = 32_767; // underscore separator (Java 7+)
int i = 2_147_483_647;
long l = 9_223_372_036_854_775_807L; // L suffix required
float f = 3.14f; // f suffix required
double d = 3.141592653589793;
char c = 'A'; // or 'A'
boolean flag = true;
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Long.MIN_VALUE); // -9223372036854775808
System.out.println((int) c); // 65 — char is numeric!
}
}Reference Types & null
Every class, array, interface, and enum is a reference type. A reference variable stores the memory address of an object on the heap, not the object itself. This is why passing an object to a method lets you mutate its fields — both the caller and callee hold references to the same heap object.
null is the default value for reference variables and means "pointing to nothing". Calling a method on a null reference throws NullPointerException at runtime. Java 14+ shows helpful NPE messages identifying the exact variable.
public class ReferenceDemo {
public static void main(String[] args) {
String s1 = "hello"; // s1 holds a reference (memory address)
String s2 = s1; // s2 holds the SAME reference
// String is immutable — reassigning s1 doesn't affect s2
s1 = "world";
System.out.println(s2); // "hello"
// Arrays are reference types too
int[] arr1 = {1, 2, 3};
int[] arr2 = arr1; // both point to the same array
arr2[0] = 99;
System.out.println(arr1[0]); // 99 — shared mutation!
String name = null;
// name.length(); // throws NullPointerException
System.out.println(name == null ? "no name" : name);
}
}Autoboxing, Unboxing & var
Java automatically converts between primitives and their wrapper classes (Integer, Long, Double, etc.) — called autoboxing (primitive → wrapper) and unboxing (wrapper → primitive). This lets you store primitives in collections like ArrayList<Integer>.
Beware: unboxing a null Integer throws NullPointerException. Also, Integer caches values −128 to 127, so == comparisons work in that range but fail outside it — always use .equals() for wrapper objects.
Java 10 introduced var for local type inference. The compiler infers the type from the right-hand side; the variable is still statically typed.
import java.util.ArrayList;
import java.util.List;
public class BoxingDemo {
public static void main(String[] args) {
// Autoboxing: int → Integer
Integer boxed = 42;
// Unboxing: Integer → int
int primitive = boxed;
// Integer cache: -128 to 127
Integer a = 127, b = 127;
System.out.println(a == b); // true (cached instance)
Integer x = 200, y = 200;
System.out.println(x == y); // false (different objects!)
System.out.println(x.equals(y)); // true — always use equals()
// Collections require wrapper types
List<Integer> list = new ArrayList<>();
list.add(1); // autoboxed
int val = list.get(0); // unboxed
// var — local type inference (Java 10+)
var message = "Hello Java"; // inferred as String
var count = 100; // inferred as int
var numbers = new ArrayList<String>(); // inferred as ArrayList<String>
System.out.println(message.toUpperCase());
}
}Key Points to Remember
- Java has exactly 8 primitive types: byte, short, int, long, float, double, char, boolean
- Instance/static fields default to 0/0.0/false/null; local variables must be explicitly initialised
- Always use L suffix for long literals > Integer.MAX_VALUE, and f for float literals
- Reference variables store memory addresses — two references to the same object share mutations
- Integer caches −128 to 127: use .equals() for wrapper comparisons, never ==
- var (Java 10+) enables local type inference but the variable remains statically typed
Practice Variables & Data Types in the Playground
Run and modify code directly in your browser - no setup needed.
Interview Questions
Sign in to ask AriaWhat is the difference between primitive types and reference types in Java?
What is autoboxing and unboxing? What are the performance implications?
Why does Integer == Integer return false for values outside −128 to 127?
Can a local variable be null in Java? What about a primitive?
What happens when you unbox a null wrapper object?
Ask Aria about Variables & Data Types
Your personal AI tutor — ask anything about this concept