Java Variables and Primitive Types
Master Java primitives, reference types, literals, and casting. Know exactly what each type costs and where overflow and precision will bite you.
What you'll learn
- ✓The eight primitive types and their exact ranges
- ✓When to use a wrapper class instead of a primitive
- ✓Literal syntax for ints, longs, doubles, and chars
- ✓Implicit widening and when an explicit cast is required
- ✓How var works and where it hurts readability
Prerequisites
Java is statically typed: every variable has a type known at compile time, and the compiler refuses to let you mix incompatible types without an explicit cast. That strictness is a feature. It catches bugs before the program runs.
The eight primitives
Primitives are value types. They live on the stack (or inside an object), not on the heap, and they cost exactly what their bit width says.
| Type | Bits | Range or precision |
|---|---|---|
| byte | 8 | -128 to 127 |
| short | 16 | -32,768 to 32,767 |
| int | 32 | about plus or minus 2.1 billion |
| long | 64 | about plus or minus 9.2 quintillion |
| float | 32 | ~7 decimal digits |
| double | 64 | ~15 decimal digits |
| char | 16 | one UTF-16 code unit |
| boolean | 1 | true or false |
int answer = 42;
long bigNumber = 9_000_000_000L; // L suffix required
double price = 19.99;
float ratio = 0.5f; // f suffix required
char letter = 'J';
boolean ready = true;
The underscore in 9_000_000_000L is a digit separator the compiler ignores. Use it for readability.
Reference types
Anything not in the table above is a reference type: classes, arrays, interfaces, enums. A reference variable holds a pointer to an object on the heap, or null.
String name = "Codeloom"; // reference
int[] scores = {90, 85, 72}; // reference to an array
String missing = null; // legal for references, not primitives
Trying to assign null to an int is a compile error. That alone prevents a class of bugs you would hit in JavaScript or Python.
Wrapper classes
Each primitive has a boxed class: Integer, Long, Double, Boolean, Character, and so on. Use them when a generic container demands an object (you cannot have a list of int, only a list of Integer).
import java.util.List;
List<Integer> nums = List.of(1, 2, 3); // autoboxed from int
int first = nums.get(0); // auto-unboxed back to int
Watch for two traps:
- Wrappers can be
null. UnboxingnullthrowsNullPointerException. - Comparing wrappers with
==compares references, not values. Use.equals()or unbox first.
Literals
Numeric literals default to int and double. Add a suffix to override.
int hex = 0xFF; // 255
int binary = 0b1010; // 10
long big = 1_000_000L;
double d = 1.5e3; // 1500.0
char nl = '\n';
char unicode = 'Ω'; // omega
String s = "Hello";
Text blocks (Java 15+) let you write multi line strings without escaping:
String json = """
{
"name": "Codeloom",
"year": 2026
}
""";
Widening and narrowing
Java widens automatically when there is no loss of information:
int i = 100;
long l = i; // implicit widening
double d = l; // implicit widening
Narrowing requires an explicit cast and may lose data:
double pi = 3.14159;
int trimmed = (int) pi; // 3, fractional part dropped
int big = 130;
byte b = (byte) big; // overflows to -126
The cast is the compiler asking you to confirm you know what you are doing.
Overflow is silent
Integer arithmetic wraps around on overflow without throwing.
int max = Integer.MAX_VALUE;
System.out.println(max + 1); // -2147483648
If you need overflow checks, use Math.addExact, Math.multiplyExact, etc. They throw ArithmeticException instead of wrapping.
Floating point is not exact
double and float follow IEEE 754. They cannot represent 0.1 exactly.
System.out.println(0.1 + 0.2); // 0.30000000000000004
For money, use BigDecimal:
import java.math.BigDecimal;
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
System.out.println(a.add(b)); // 0.3
Always construct BigDecimal from a String, never from a double.
var: local type inference
Since Java 10 you can let the compiler infer a local variable type with var.
var name = "Codeloom"; // inferred as String
var nums = List.of(1, 2, 3); // inferred as List<Integer>
var only works for local variables with an initializer. It is not dynamic typing; the type is still fixed at compile time. Use it when the right hand side already names the type (constructors, factory methods). Avoid it when it hides the type from a reader, like var x = process(data);.
Constants
Use final for values that should never be reassigned:
final double PI = 3.14159;
final int MAX_RETRIES = 3;
Combine with static at class level for true constants. By convention, constants are UPPER_SNAKE_CASE.
Wrap up
Pick primitives by default, reach for wrappers when generics force you, and use BigDecimal for money. Next, learn how to drive these values with branches and loops in Java Control Flow, or jump ahead to Java Classes and Objects when you are ready to model real things.