Primitive Data Types
Java's eight primitive types are fixed-size, non-object values with language-guaranteed ranges — the raw numeric and logical building blocks everything else is built on.
Java defines exactly eight primitive types: byte, short, int, long, float, double, char, and boolean. Unlike reference types, primitives hold their value directly (not a reference to an object), have a fixed bit width and range guaranteed by the Java Language Specification, and are never null. This topic covers each primitive's range and default value, initialization rules, and the basics of signed integer and floating-point behavior.
The Eight Primitive Types at a Glance
These bit widths and ranges are guaranteed by the Java Language Specification for every conforming JVM — unlike C/C++, where int's size can vary by platform, Java's int is always exactly 32 bits everywhere.
Type Size Range / Notes Default
byte 8-bit -128 to 127 0
short 16-bit -32,768 to 32,767 0
int 32-bit -2,147,483,648 to 2,147,483,647 0
long 64-bit ~-9.22 * 10^18 to 9.22 * 10^18 0L
float 32-bit IEEE 754 single-precision 0.0f
double 64-bit IEEE 754 double-precision 0.0d
char 16-bit 0 to 65,535 (unsigned UTF-16 code unit) '\u0000'
boolean n/a true or false only (size unspecified) falseInteger Types: byte, short, int, long
All four integer types are signed (they can represent negative values) using two's-complement representation. int is the default type for integer literals and the most commonly used integer type; long is needed when values may exceed int's range, and requires an L suffix on its literals.
byte smallValue = 100;
short mediumValue = 30_000;
int standard = 2_000_000_000;
long large = 9_000_000_000L; // L suffix required, exceeds int rangeSigned Integer Behavior and Overflow
Because integer types are signed and fixed-width, arithmetic that exceeds a type's range silently wraps around (overflows) rather than throwing an exception or growing the type — this is a guaranteed language behavior, not an error condition the JVM detects for you.
This wraparound is defined, guaranteed behavior in the JLS for int/long arithmetic — it is a common source of subtle bugs, not undefined behavior.
Math.addExact, Math.multiplyExact, and similar methods throw ArithmeticException on overflow instead of silently wrapping, when that safety is worth the extra check.
int max = Integer.MAX_VALUE; // 2147483647
System.out.println(max + 1); // prints -2147483648 (wraps around, no exception)Floating-Point Types: float and double
float and double follow the IEEE 754 standard for binary floating-point arithmetic — float is single-precision (32-bit), double is double-precision (64-bit) and is the default for floating-point literals and the far more commonly used of the two in ordinary code.
Floating-point types cannot represent all decimal values exactly (e.g. 0.1 has no exact binary floating-point representation), which is why floating-point equality comparisons and financial/monetary calculations are risky with float/double — BigDecimal is the appropriate type for exact decimal arithmetic (covered in its own topic).
float/double division by zero does not throw an exception like integer division by zero does — it produces Infinity, -Infinity, or NaN per IEEE 754 rules.
System.out.println(0.1 + 0.2); // prints 0.30000000000000004, not exactly 0.3
double a = 1.0 / 0.0; // positive infinity, not an exception
double b = 0.0 / 0.0; // NaN (Not a Number), not an exceptionchar: Unicode Code Units
char is a 16-bit unsigned integer type representing a single UTF-16 code unit — it is the one primitive type that is unsigned. Because Unicode has more than 65,536 characters, a single char cannot represent every possible Unicode character on its own; characters outside the Basic Multilingual Plane require a surrogate pair of two char values (covered in the Character & Text Handling topic).
char letter = 'A';
char digit = '9';
char unicodeEscape = '\u0041'; // also 'A'
int codePoint = letter; // char widens implicitly to int (its underlying numeric value)boolean
boolean has exactly two possible values, true and false, and cannot be converted to or from any numeric type (unlike C, where 0/non-zero often stand in for boolean). Its exact storage size is not specified by the JLS — it is left as a JVM implementation detail.
boolean isValid = true;
// int x = isValid; // compile error: boolean is not convertible to int in JavaDefault Field Values
When a primitive is declared as an instance field or static field and not explicitly initialized, it automatically receives a default value: 0 for the numeric integer types, 0.0 for float/double, '\u0000' (the null character) for char, and false for boolean. This automatic default applies only to fields — never to local variables.
class Counter {
int value; // defaults to 0
double rate; // defaults to 0.0
boolean active; // defaults to false
}Local-Variable Initialization Requirements
Unlike fields, local variables of primitive type get no automatic default value — the compiler enforces definite assignment, requiring the variable to be assigned before it is read on every possible path (see the Variables topic for the full rule).
void example() {
int total; // no default value here
// System.out.println(total); // compile error: total might not have been initialized
total = 0;
System.out.println(total); // OK now
}Primitive Storage Semantics (Language Level)
A primitive variable holds its actual value directly — copying a primitive variable's value into another variable copies the bits, producing two fully independent values. This is fundamentally different from a reference-type variable, which holds a reference to an object; copying it copies the reference, not the object (covered in Reference Types).
Exactly how primitive values are physically laid out in memory at run time (stack slots, register allocation, etc.) is a JVM implementation detail, not something the Java Language Specification mandates — only the type's size, range, and observable arithmetic behavior are guaranteed.
int a = 5;
int b = a; // b gets a copy of a's value
b = 10; // changing b has no effect on a
System.out.println(a); // still prints 5Integer Literals and Suffixes (Recap)
Integer literals default to type int; use the L (or l) suffix for a long literal. See the Lexical Structure topic for the full rules on hex/octal/binary literal prefixes and underscore separators.
long id = 123_456_789_012L;Key Takeaways
Java has exactly eight primitive types: byte, short, int, long, float, double, char, boolean — each with a fixed, JLS-guaranteed size and range on every conforming JVM.
Integer arithmetic silently overflows/wraps around on exceeding its type's range; it does not throw an exception (Math.*Exact methods exist for checked arithmetic).
float/double follow IEEE 754 and cannot represent all decimal values exactly; use BigDecimal for exact decimal arithmetic like currency.
char is a 16-bit unsigned UTF-16 code unit — one char cannot always represent one full Unicode character.
Uninitialized primitive fields get automatic default values (0/0.0/false/'\u0000'); uninitialized local primitive variables get none and must be definitely assigned before use.
if (a / b == 0.5) {... } // comparing floating-point results for exact equality if (Math.abs(a / b - 0.5) < 1e-9) {... } // compare within a small tolerance
Floating-point arithmetic cannot represent most decimal fractions exactly, so direct == comparison is unreliable; comparing within an epsilon tolerance is the standard approach.
double price = 19.99; double total = price * 3; // for currency math
BigDecimal price = new BigDecimal("19.99"); BigDecimal total = price.multiply(new BigDecimal(3));
double/float are binary floating-point types that introduce rounding error unsuitable for exact monetary calculations; BigDecimal (constructed from a String) gives exact decimal arithmetic.
int result = Integer.MAX_VALUE + 1; // expecting an exception or a bigger value long result = (long) Integer.MAX_VALUE + 1; // or use Math.addExact for a checked int operation int arithmetic silently wraps around on overflow rather than throwing; if overflow is a real risk, either widen to long or use Math.addExact to get an ArithmeticException instead of silent wraparound.
How many primitive types does Java define, and name them all.
Eight: byte, short, int, long, float, double, char, and boolean.
What happens when int arithmetic exceeds Integer.MAX_VALUE? Does Java throw an exception?
It silently wraps around (overflows) to a very negative number using two's-complement wraparound — no exception is thrown by default. Math.addExact and similar methods exist if checked, exception-throwing arithmetic is needed.
Why is char considered the one 'unsigned' primitive type in Java, and what does it actually represent?
char is a 16-bit unsigned integer representing a single UTF-16 code unit (range 0 to 65,535) — unlike the signed integer types, it has no negative range.
Why is double unsuitable for exact monetary calculations, and what type should be used instead?
double is a binary floating-point type (IEEE 754) that cannot represent most decimal fractions exactly, introducing rounding error; BigDecimal, constructed from a String, provides exact decimal arithmetic appropriate for currency.
What default value does an uninitialized instance field of type boolean receive? What about a local boolean variable?
An instance field of type boolean automatically defaults to false. A local boolean variable receives no automatic default — it must be definitely assigned before it's read, or the code fails to compile.
Does Java allow implicit conversion between boolean and int, the way C often treats 0/non-zero as false/true?
No. Java's boolean is a distinct type with no numeric equivalence — there is no implicit or explicit conversion between boolean and any integer type.
What does copying a primitive variable's value into another variable actually copy, and how does that differ from copying a reference-type variable?
It copies the raw value itself, producing two fully independent variables. Copying a reference-type variable copies the reference (the pointer to the object), so both variables end up referring to the same underlying object.
What does this print, and why?
It prints -128. byte's maximum value is 127; incrementing past it wraps around to the minimum representable byte value (-128) via two's-complement overflow, exactly as int overflow behaves at its own boundary.
Write a short program demonstrating that 0.1 + 0.2 does not exactly equal 0.3 with double, and show the correct way to compare them.
double a = 0.1 + 0.2; double b = 0.3; System.out.println(a == b); // false System.out.println(Math.abs(a - b) < 1e-9); // true
0.1, 0.2, and 0.3 have no exact binary floating-point representation, so 0.1 + 0.2 yields a value very close to but not exactly equal to 0.3; comparing within a small epsilon tolerance is the correct approach.
What does this print, and why, given neither field is explicitly initialized?
It prints '0 false'. Instance fields of primitive type automatically receive default values when not explicitly initialized: 0 for int, false for boolean.
byte b = 127;
b++;
System.out.println(b);class Box {
int width;
boolean sealed;
}
Box b = new Box();
System.out.println(b.width + " " + b.sealed);