Primitive Types and Variables
|
This section documents the current Java release line, with Java 25 LTS as the reference point (no specific patch version is pinned), as published at the Java developer portal, the Java Tutorials, and the Java SE API specification — which are the references these pages are written and verified against. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Java has eight built-in primitive types that hold plain values — not objects — each with a fixed size and a defined range. This page follows dev.java "Primitive Types", dev.java "Using the var Keyword", and JLS chapter 5, "Conversions and Contexts".
The Eight Primitive Types
type bits range / values default
---------------------------------------------------------------------------
byte 8 -128 .. 127 0
short 16 -32,768 .. 32,767 0
int 32 -2,147,483,648 .. 2,147,483,647 0
long 64 -9,223,372,036,854,775,808 .. 9.22e18 0L
float 32 IEEE 754, ~6-7 significant decimal digits 0.0f
double 64 IEEE 754, ~15-16 significant decimal digits 0.0
char 16 U+0000 .. U+FFFF (one unsigned UTF-16 code unit) U+0000
boolean -- true / false false
int count = 0;
long fileSize = 9_000_000_000L; // exceeds int range: needs the L suffix
double ratio = 3.0 / 7.0;
char grade = 'A';
boolean done = false;
class Config {
int retries; // a FIELD without an initializer defaults to 0
boolean verbose; // defaults to false
double timeout; // defaults to 0.0
}
Primitives have no methods and can never be null; each has a companion wrapper class
(Integer,
Long, Double, Character, Boolean, …) covered on
Numbers and Math. Fields and array elements are automatically
zero-initialized (0, 0.0, false, \u0000); local variables are not (see definite assignment
below). int is the default type for integer literals and integer arithmetic. The exact list is
JLS 4.2.
Literals
int dec = 1_000_000; // underscores group digits; not at the ends, not next to the dot
int hex = 0xFF; // 255
int bin = 0b1010_0101; // 165
int oct = 0777; // leading zero means octal -> 511 (avoid: easy to misread)
long big = 123_456_789L; // L suffix (never lowercase l -- it looks like 1)
float f = 3.14f; // f suffix required: a decimal literal is double by default
double d = 6.022e23; // exponent notation
double d2 = 42d; // optional d suffix
char tab = '\t'; // \t \n \r \f \b \\ \' \"
char quote = '\'';
char eacute = '\u00e9'; // the letter e-acute, written as a Unicode escape
int code = 'A'; // 65 -- a char promotes to int in numeric context
boolean ok = true; // only true / false
String s = null; // null is assignable to any reference type
Underscores may appear only between digits. A plain decimal point makes a double; add f for a
float. See the Java Tutorials
"Primitive Data Types" page and JLS
3.10. String literals and text blocks are on Strings and Text.
Declaring and Initializing Variables
int a = 5; // declaration with an initializer
int b, c; // two variables, still uninitialized
b = 1;
c = b + a;
final int LIMIT = 100; // a final local: assigned exactly once
// LIMIT = 200; // ERROR: cannot assign a value to final variable LIMIT
final int mode; // a "blank final": assign later, once, on every path
if (a > 0) {
mode = 1;
} else {
mode = -1;
}
int unset;
// System.out.println(unset); // ERROR: variable unset might not have been initialized
The compiler enforces definite assignment
(JLS chapter 16): a local variable must
be provably assigned on every path that reaches a read of it. final on a local or a parameter means
"assigned once" — required for locals captured by a lambda or anonymous class, and the right default
for values that should not change. Declaring one variable per line reads better than the comma form.
var: Local-Variable Type Inference
var names = new ArrayList<String>(); // inferred as ArrayList<String>
var count = 0; // int
var total = 0L; // long
var path = Path.of("in.txt"); // Path
for (var entry : System.getenv().entrySet()) { // Map.Entry<String, String>
System.out.println(entry.getKey());
}
try (var in = Files.newBufferedReader(path)) { // BufferedReader
in.readLine();
}
// NOT allowed:
// var x; // no initializer to infer from
// var y = null; // null has no type
// var z = () -> 42; // a lambda needs an explicit target type
// var w = { 1, 2, 3 }; // an array initializer needs a declared type
var (Java 10 and later) infers the static type of a local variable from its initializer. It is
not allowed for fields, method parameters, method return types, or catch parameters, and it always
needs an initializer. The variable is still statically typed — var is neither Object nor dynamic
typing. Use it when the type is obvious from the right-hand side (a constructor or a well-named factory
method) and spell the type out when doing so aids the reader. Full guidance is at
dev.java "Using the var Keyword".
Conversions, Casts, and Overflow
// Widening: automatic and lossless (int/long -> float/double may lose precision, but not range)
int i = 1_000;
long l = i; // int -> long
double dd = l; // long -> double
// Narrowing: requires an explicit cast and may discard information
double pi = 3.99;
int truncated = (int) pi; // 3 -- the fraction is dropped, not rounded
long huge = 4_000_000_000L;
int wrapped = (int) huge; // -294967296 -- high bits discarded
byte tiny = (byte) 200; // -56
char ch = (char) 97; // 'a'
int back = 'a'; // 97
// Integer overflow is SILENT: it wraps modulo 2^n
int max = Integer.MAX_VALUE;
int oops = max + 1; // -2147483648
// Ask for an exception instead
int safe = Math.addExact(max, 1); // throws ArithmeticException: integer overflow
long prod = Math.multiplyExact(1_000_000L, 1_000_000L);
int narrowed = Math.toIntExact(10_000_000_000L); // throws: value out of int range
Widening primitive conversions (byte → short → int → long → float → double, and
char → int) happen automatically
(JLS 5.1.2). Narrowing
conversions (JLS 5.1.3)
require an explicit (type) cast and can silently lose range or precision.
Integer arithmetic never traps on overflow — it wraps. When a wrong answer would be dangerous, use the
exact methods on Math
(addExact, subtractExact, multiplyExact, negateExact, incrementExact, toIntExact), which
throw ArithmeticException
on overflow. Floating-point follows IEEE 754: it has signed zeros, Infinity, and NaN, and never
throws on division by zero (1.0 / 0 is Infinity, 0.0 / 0.0 is NaN). Integer division or
remainder by zero does throw ArithmeticException.
See Also
-
Operators and Expressions — what you do with these values, including numeric promotion.
-
Numbers and Math — wrapper classes,
BigInteger,BigDecimal, andMath. -
Strings and Text —
Stringliterals and text blocks. -
Lexical Structure and Style — where literals and identifiers fit in the grammar.