Java is statically typed: every variable and every expression has a type known at
compile time. Types are either primitive or reference types, and
values are either primitive values or references to objects (JLS chapter 4). There is
also the type of null, which has no name.
Primitive types
The eight primitive types have the same size and behaviour on every platform. Each has
a wrapper class in java.lang for use where an object is needed (see
boxing).
| Type | Bits | Values | Default | Wrapper |
|---|---|---|---|---|
boolean | not defined | true, false | false | Boolean |
byte | 8 | −128 to 127 | 0 | Byte |
short | 16 | −32,768 to 32,767 | 0 | Short |
char | 16 | \u0000 to (0 to 65,535), unsigned | \u0000 | Character |
int | 32 | −231 to 231−1 (−2,147,483,648 to 2,147,483,647) | 0 | Integer |
long | 64 | −263 to 263−1 | 0L | Long |
float | 32 | IEEE 754 binary32, about 7 decimal digits | 0.0f | Float |
double | 64 | IEEE 754 binary64, about 15 to 17 decimal digits | 0.0d | Double |
byte, short, int, long and
char are the integral types; with float and
double they are the numeric types. byte,
short, int and long are signed two’s-complement
integers.
Integer arithmetic
- Arithmetic on
byte,shortandcharoperands is done inint(see numeric promotion):byte b = 1; b = b + 1;is an error, whileb += 1;andb++are allowed. - Overflow wraps around silently:
Integer.MAX_VALUE + 1isInteger.MIN_VALUE. TheMath.addExactfamily throws instead. - Integer division rounds towards zero:
-7 / 2is −3. The remainder takes the sign of the dividend:-7 % 2is −1. - Integer division or remainder by zero throws
ArithmeticException. The only other integer operator that can throw is unboxing anullwrapper, which throwsNullPointerException. Integer.MIN_VALUE / -1overflows toInteger.MIN_VALUEand does not throw.
Floating-point arithmetic
floatanddoublefollow IEEE 754, with round to nearest. Since Java 17 every floating-point expression is evaluated exactly in its type on every platform (JEP 306). Before, intermediate results outside thestrictfpmodifier could use a wider exponent range.- The values include positive and negative zero, positive and negative infinity and
NaN (not a number).
1.0 / 0isInfinity,0.0 / 0isNaN, and no floating-point operation throws. - Every comparison with NaN is false, including
NaN == NaN; only!=is true. Test withDouble.isNaN. 0.0 == -0.0is true, but1 / -0.0is-Infinity.Double.equalsandDouble.comparetreat the two zeros as different and NaN as equal to itself.- The remainder
%works on floating-point values, truncating like integer division:5.5 % 2is 1.5.
boolean and char
boolean is not a numeric type: there is no conversion between it and
any other primitive type, and if (1) is an error. char is a
16-bit unsigned integral type holding one UTF-16 code unit. It takes part in
arithmetic, so 'a' + 1 is the int 98 and
(char) ('a' + 1) is 'b'.
Reference types
There are four kinds of reference type (JLS 4.3):
| Kind | Examples |
|---|---|
| Class types, including enum and record classes | String, Thread.State, ArrayList<String> |
| Interface types, including annotation interfaces | Runnable, List<? extends Number> |
| Type variables | T in class Box<T> |
| Array types | int[], String[][] |
An object is an instance of a class or an array. A reference value is a pointer to an
object or the special value null, which refers to nothing. The null type
can be converted to any reference type, so null can be assigned to any
reference variable. Using null to access a field or call a method throws
NullPointerException.
Every class has Object as an ancestor, and every interface type and array
type is a subtype of Object. == on references compares
identity; to compare contents, call equals.
Generic types (parameterised types such as List<String>, raw types,
wildcards and intersection types such as Number & Comparable<T>)
are covered on the Generics page.
Subtyping among primitive types
For primitive types the subtype relation is the direction of widening
(JLS 4.10.1): double > float > long
> int > char, and int >
short > byte. boolean is unrelated to all
of them.
Variables
A variable is a storage location with a type. A variable of a primitive type holds a
value of that type; a variable of a reference type holds null or a
reference to an object of a compatible type (JLS 4.12).
| Kind | Declared by | Initial value |
|---|---|---|
| Class variable | A static field, or any field of an interface | Default value, then its initialiser when the class is initialised |
| Instance variable | A field without static | Default value, then its initialiser when the object is created |
| Array component | Array creation | Default value |
| Method, constructor and lambda parameter | A parameter list | The argument |
| Exception parameter | A catch clause | The exception caught |
| Local variable | A local declaration, a for header, a resource, or a pattern | None: must be definitely assigned before use |
The default value is zero for numeric types (0, 0L,
0.0f, 0.0, '\u0000'), false for
boolean and null for reference types.
final variables
A final variable can be assigned only once. A final
field must be assigned by the end of every constructor (instance fields) or of class
initialisation (static fields); a final variable declared without an initialiser is
a blank final. Assigning it twice, or reading it before it is assigned, is a
compile-time error (see Definite
assignment). A final reference cannot be pointed elsewhere, but the
object it refers to can still change.
A constant variable is a final variable of primitive type or
String initialised with a
constant
expression. Its value is copied into every class that uses it at compile time,
and it can be used in case labels.
static final int MAX = 100; // constant variable
static final String NAME = "app"; // constant variable
static final long START = System.nanoTime(); // final, not constant
final List<String> names = new ArrayList<>(); // the list can still change
names.add("Ada"); // allowed
// names = List.of(); // error: names is final
Effectively final
A local variable or parameter that is not declared final is
effectively final if it is never assigned after its initialisation (or, for
a variable declared without an initialiser, is definitely unassigned before each
assignment) and is never the operand of ++ or --
(JLS 4.12.4). Lambda bodies, inner classes, guards and try-with-resources (Java 9)
can only use local variables from the enclosing code that are final or
effectively final.
int count = 0;
String prefix = "n=";
Runnable r = () -> System.out.println(prefix); // fine: prefix is effectively final
// Runnable s = () -> System.out.println(count); // error if count is changed below
count++;
Local variable type inference: var
var (Java 10, JEP 286) declares a local variable whose type is
inferred from its initialiser. It is a reserved type name, not a keyword: it can
still name a variable or method, but not a class or interface.
| Allowed | Not allowed |
|---|---|
var list = new ArrayList<String>();for (var i = 0; i < n; i++)for (var s : strings)try (var in = Files.newInputStream(p))(var x, var y) -> x + y (Java 11)var _ = queue.poll(); (Java 22)
|
Fields, method parameters, return types, catch parametersvar x; (no initialiser)var x = null;var a = {1, 2}; (array initialiser)var f = () -> 1; (lambda or method reference)var a = 1, b = 2; (several declarators)var a[] = new int[3];
|
The inferred type is the type of the initialiser, after capture conversion and with wildcards replaced by their bounds. It can be a type that cannot be written in source, such as an anonymous class or an intersection type, which lets the variable use members of an anonymous class:
var point = new Object() { int x = 1, y = 2; };
System.out.println(point.x + point.y); // 3: the type is the anonymous class
var items = new ArrayList<>(); // ArrayList<Object>: diamond with no other hint
var n = 10; // int
var big = 10L; // long
In lambda parameters (Java 11, JEP 323), var allows annotations on
implicitly typed parameters: (@NonNull var x) -> x.length(). Either all
parameters use var or none do.
Where types are used
Types appear in declarations (of fields, methods, parameters, local variables, type
parameter bounds, extends, implements, throws)
and in expressions (casts, instanceof and patterns, class instance and
array creation, class literals such as String.class, explicit type
arguments, and method references such as String::length). Since Java 8 any
of these uses of a type can carry
type annotations.