Contents | Prev | Next | Index

CHAPTER 4

Types, Values, and Variables


Java is a strongly typed language, which means that every variable and every expression has a type that is known at compile time. Types limit the values that a variable (§4.5) can hold or that an expression can produce, limit the operations supported on those values, and determine the meaning of the operations. Strong typing helps detect errors at compile time.

The types of the Java language are divided into two categories: primitive types and reference types. The primitive types (§4.2) are the boolean type and the numeric types. The numeric types are the integral types byte, short, int, long, and char, and the floating-point types float and double. The reference types (§4.3) are class types, interface types, and array types. There is also a special null type. An object (§4.3.1) in Java is a dynamically created instance of a class type or a dynamically created array. The values of a reference type are references to objects. All objects, including arrays, support the methods of class Object (§4.3.2). String literals are represented by String objects (§4.3.3).

Types are the same (§4.3.4) if they have the same fully qualified names and are loaded by the same class loader. Names of types are used (§4.4) in declarations, in casts, in class instance creation expressions, in array creation expressions, and in instanceof operator expressions.

A variable (§4.5) is a storage location. A variable of a primitive type always holds a value of that exact type. A variable of a class type T can hold a null reference or a reference to an instance of class T or of any class that is a subclass of T. A variable of an interface type can hold a null reference or a reference to any instance of any class that implements the interface. If T is a primitive type, then a variable of type "array of T" can hold a null reference or a reference to any array of type "array of T"; if T is a reference type, then a variable of type "array of T" can hold a null reference or a reference to any array of type "array of S" such that type S is assignable (§5.2) to type T. A variable of type Object can hold a null reference or a reference to any object, whether class instance or array.

4.1 The Kinds of Types and Values

There are two kinds of types in Java: primitive types (§4.2) and reference types (§4.3). There are, correspondingly, two kinds of data values that can be stored in variables, passed as arguments, returned by methods, and operated on: primitive values (§4.2) and reference values (§4.3).

There is also a special null type, the type of the expression null, which has no name. Because the null type has no name, it is impossible to declare a variable of the null type or to cast to the null type. The null reference is the only possible value of an expression of null type. The null reference can always be cast to any reference type. In practice, the Java programmer can ignore the null type and just pretend that null is merely a special literal that can be of any reference type.

4.2 Primitive Types and Values

A primitive type is predefined by the Java language and named by its reserved keyword (§3.9):

Primitive values do not share state with other primitive values. A variable whose type is a primitive type always holds a primitive value of that same type. The value of a variable of primitive type can be changed only by assignment operations on that variable.

The numeric types are the integral types and the floating-point types.

The integral types are byte, short, int, and long, whose values are 8-bit, 16-bit, 32-bit and 64-bit signed two's-complement integers, respectively, and char, whose values are 16-bit unsigned integers representing Unicode characters.

The floating-point types are float, whose values are 32-bit IEEE 754 floating-point numbers, and double, whose values are 64-bit IEEE 754 floating-point numbers.

The boolean type has exactly two values: true and false.

4.2.1 Integral Types and Values

The values of the integral types are integers in the following ranges:

4.2.2 Integer Operations

Java provides a number of operators that act on integral values:

Other useful constructors, methods, and constants are predefined in the classes Integer (§20.7), Long (§20.8), and Character (§20.5).

If an integer operator other than a shift operator has at least one operand of type long, then the operation is carried out using 64-bit precision, and the result of the numerical operator is of type long. If the other operand is not long, it is first widened (§5.1.2) to type long by numeric promotion (§5.6). Otherwise, the operation is carried out using 32-bit precision, and the result of the numerical operator is of type int. If either operand is not an int, it is first widened to type int by numeric promotion.

The built-in integer operators do not indicate overflow or underflow in any way. The only numeric operators that can throw an exception (§11) are the integer divide operator / (§15.16.2) and the integer remainder operator % (§15.16.3), which throw an ArithmeticException if the right-hand operand is zero.

The example:


class Test {
	public static void main(String[] args) {
		int i = 1000000;
		System.out.println(i * i);
		long l = i;
		System.out.println(l * l);
		System.out.println(20296 / (l - i));
	}
}
produces the output:


-727379968
1000000000000
and then encounters an ArithmeticException in the division by l - i, because l - i is zero. The first multiplication is performed in 32-bit precision, whereas the second multiplication is a long multiplication. The value -727379968 is the decimal value of the low 32 bits of the mathematical result, 1000000000000, which is a value too large for type int.

Any value of any integral type may be cast to or from any numeric type. There are no casts between integral types and the type boolean.

4.2.3 Floating-Point Types and Values

The floating-point types are float and double, representing the single-precision 32-bit and double-precision 64-bit format IEEE 754 values and operations as specified in IEEE Standard for Binary Floating-Point Arithmetic, ANSI/IEEE Standard 754-1985 (IEEE, New York).

The IEEE 754 standard includes not only positive and negative sign-magnitude numbers, but also positive and negative zeros, positive and negative infinities, and a special Not-a-Number (hereafter abbreviated NaN). The NaN value is used to represent the result of certain operations such as dividing zero by zero. NaN constants of both float and double type are predefined as Float.NaN (§20.9.5) and Double.NaN (§20.10.5).

The finite nonzero values of type float are of the form , where s is +1 or -1, m is a positive integer less than , and e is an integer between -149 and 104, inclusive. Values of that form such that m is positive but less than and e is equal to -149 are said to be denormalized.

The finite nonzero values of type double are of the form , where s is +1 or -1, m is a positive integer less than , and e is an integer between -1075 and 970, inclusive. Values of that form such that m is positive but less than and e is equal to -1075 are said to be denormalized.

Except for NaN, floating-point values are ordered; arranged from smallest to largest, they are negative infinity, negative finite nonzero values, negative zero, positive zero, positive finite nonzero values, and positive infinity.

Positive zero and negative zero compare equal; thus the result of the expression 0.0==-0.0 is true and the result of 0.0>-0.0 is false. But other operations can distinguish positive and negative zero; for example, 1.0/0.0 has the value positive infinity, while the value of 1.0/-0.0 is negative infinity. The operations Math.min and Math.max also distinguish positive zero and negative zero.

NaN is unordered, so the numerical comparison operators <, <=, >, and >= return false if either or both operands are NaN (§15.19.1). The equality operator == returns false if either operand is NaN, and the inequality operator != returns true if either operand is NaN (§15.20.1). In particular, x!=x is true if and only if x is NaN, and (x<y) == !(x>=y) will be false if x or y is NaN.

Any value of a floating-point type may be cast to or from any numeric type. There are no casts between floating-point types and the type boolean.

4.2.4 Floating-Point Operations

Java provides a number of operators that act on floating-point values:

Other useful constructors, methods, and constants are predefined in the classes Float (§20.9), Double (§20.10), and Math (§20.11).

If at least one of the operands to a binary operator is of floating-point type, then the operation is a floating-point operation, even if the other is integral.

If at least one of the operands to a numerical operator is of type double, then the operation is carried out using 64-bit floating-point arithmetic, and the result of the numerical operator is a value of type double. (If the other operand is not a double, it is first widened to type double by numeric promotion (§5.6).) Otherwise, the operation is carried out using 32-bit floating-point arithmetic, and the result of the numerical operator is a value of type float. If the other operand is not a float, it is first widened to type float by numeric promotion.

Operators on floating-point numbers behave exactly as specified by IEEE 754. In particular, Java requires support of IEEE 754 denormalized floating-point numbers and gradual underflow, which make it easier to prove desirable properties of particular numerical algorithms. Floating-point operations in Java do not "flush to zero" if the calculated result is a denormalized number.

Java requires that floating-point arithmetic behave as if every floating-point operator rounded its floating-point result to the result precision. Inexact results must be rounded to the representable value nearest to the infinitely precise result; if the two nearest representable values are equally near, the one with its least significant bit zero is chosen. This is the IEEE 754 standard's default rounding mode known as round to nearest.

Java uses round toward zero when converting a floating value to an integer (§5.1.3), which acts, in this case, as though the number were truncated, discarding the mantissa bits. Rounding toward zero chooses at its result the format's value closest to and no greater in magnitude than the infinitely precise result.

Java floating-point operators produce no exceptions (§11). An operation that overflows produces a signed infinity, an operation that underflows produces a signed zero, and an operation that has no mathematically definite result produces NaN. All numeric operations with NaN as an operand produce NaN as a result. As has already been described, NaN is unordered, so a numeric comparison operation involving one or two NaNs returns false and any != comparison involving NaN returns true, including x!=x when x is NaN.

The example program:


class Test {

	public static void main(String[] args) {

		// An example of overflow:
		double d = 1e308;
		System.out.print("overflow produces infinity: ");
		System.out.println(d + "*10==" + d*10);

		// An example of gradual underflow:
		d = 1e-305 * Math.PI;
		System.out.print("gradual underflow: " + d + "\n      ");
		for (int i = 0; i < 4; i++)
			System.out.print(" " + (d /= 100000));
		System.out.println();

		// An example of NaN:
		System.out.print("0.0/0.0 is Not-a-Number: ");
		d = 0.0/0.0;
		System.out.println(d);

		// An example of inexact results and rounding:
		System.out.print("inexact results with float:");
		for (int i = 0; i < 100; i++) {
			float z = 1.0f / i;
			if (z * i != 1.0f)
				System.out.print(" " + i);
		}
		System.out.println();

		// Another example of inexact results and rounding:
		System.out.print("inexact results with double:");
		for (int i = 0; i < 100; i++) {
			double z = 1.0 / i;
			if (z * i != 1.0)
				System.out.print(" " + i);
		}
		System.out.println();

		// An example of cast to integer rounding:
		System.out.print("cast to int rounds toward 0: ");
		d = 12345.6;
		System.out.println((int)d + " " + (int)(-d));
	}
}
produces the output:


overflow produces infinity: 1.0e+308*10==Infinity
gradual underflow: 3.141592653589793E-305
	3.1415926535898E-310 3.141592653E-315 3.142E-320 0.0
0.0/0.0 is Not-a-Number: NaN
inexact results with float: 0 41 47 55 61 82 83 94 97
inexact results with double: 0 49 98
cast to int rounds toward 0: 12345 -12345
This example demonstrates, among other things, that gradual underflow can result in a gradual loss of precision.

The inexact results when i is 0 involve division by zero, so that z becomes positive infinity, and z * 0 is NaN, which is not equal to 1.0.

4.2.5 The boolean Type and boolean Values

The boolean type represents a logical quantity with two possible values, indicated by the literals true and false (§3.10.3). The boolean operators are:

Boolean expressions determine the control flow in several kinds of statements:

A boolean expression also determines which subexpression is evaluated in the conditional ? : operator (§15.24).

Only boolean expressions can be used in control flow statements and as the first operand of the conditional operator ? :. An integer x can be converted to a boolean, following the C language convention that any nonzero value is true, by the expression x!=0. An object reference obj can be converted to a boolean, following the C language convention that any reference other than null is true, by the expression obj!=null.

A cast of a boolean value to type boolean is allowed (§5.1.1); no other casts on type boolean are allowed. A boolean can be converted to a string by string conversion (§5.4).

4.3 Reference Types and Values

There are three kinds of reference types: class types (§8), interface types (§9), and array types (§10).

Names are described in §6; type names in §6.5 and, specifically, §6.5.4.

The sample code:


class Point { int[] metrics; }
interface Move { void move(int deltax, int deltay); }
declares a class type Point, an interface type Move, and uses an array type int[] (an array of int) to declare the field metrics of the class Point.

4.3.1 Objects

An object is a class instance or an array.

The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object.

A class instance is explicitly created by a class instance creation expression (§15.8), or by invoking the newInstance method of class Class (§20.3.8). An array is explicitly created by an array creation expression (§15.8).

A new class instance is implicitly created when the string concatenation operator + (§15.17.1) is used in an expression, resulting in a new object of type String (§4.3.3, §20.12). A new array object is implicitly created when an array initializer expression (§10.6) is evaluated; this can occur when a class or interface is initialized (§12.4), when a new instance of a class is created (§15.8), or when a local variable declaration statement is executed (§14.3).

Many of these cases are illustrated in the following example:


class Point {
	int x, y;
	Point() { System.out.println("default"); }
	Point(int x, int y) { this.x = x; this.y = y; }

	// A Point instance is explicitly created at class initialization time:
	static Point origin = new Point(0,0);

	// A String can be implicitly created by a + operator:
	public String toString() {
return "(" + x + "," + y + ")";
} }
class Test { public static void main(String[] args) { // A Point is explicitly created using newInstance: Point p = null; try { p = (Point)Class.forName("Point").newInstance(); } catch (Exception e) { System.out.println(e); }
// An array is implicitly created by an array constructor: Point a[] = { new Point(0,0), new Point(1,1) };
// Strings are implicitly created by + operators: System.out.println("p: " + p); System.out.println("a: { " + a[0] + ", "
+ a[1] + " }");
// An array is explicitly created by an array creation expression: String sa[] = new String[2]; sa[0] = "he"; sa[1] = "llo"; System.out.println(sa[0] + sa[1]); } }
which produces the output:


default
p: (0,0)
a: { (0,0), (1,1) }
hello
The operators on references to objects are:

There may be many references to the same object. Most objects have state, stored in the fields of objects that are instances of classes or in the variables that are the components of an array object. If two variables contain references to the same object, the state of the object can be modified using one variable's reference to the object, and then the altered state can be observed through the reference in the other variable.

The example program:

class Value { int val; }

class Test {
	public static void main(String[] args) {
		int i1 = 3;
		int i2 = i1;
		i2 = 4;
		System.out.print("i1==" + i1);
		System.out.println(" but i2==" + i2);
		Value v1 = new Value();
		v1.val = 5;
		Value v2 = v1;
		v2.val = 6;
		System.out.print("v1.val==" + v1.val);
		System.out.println(" and v2.val==" + v2.val);
	}
}
produces the output:


i1==3 but i2==4
v1.val==6 and v2.val==6
because v1.val and v2.val reference the same instance variable (§4.5.3) in the one Value object created by the only new expression, while i1 and i2 are different variables.

See §10 and §15.9 for examples of the creation and use of arrays.

Each object has an associated lock (§17.13), which is used by synchronized methods (§8.4.3) and the synchronized statement (§14.17) to provide control over concurrent access to state by multiple threads (§17.12, §20.20).

4.3.2 The Class Object

The standard class Object is a superclass (§8.1) of all other classes. A variable of type Object can hold a reference to any object, whether it is an instance of a class or an array (§10). All class and array types inherit the methods of class Object, which are summarized here and completely specified in §20.1:

package java.lang;

public class Object {
	public final Class getClass() { . . . }
	public String toString() { . . . }
	public boolean equals(Object obj) { . . . }
	public int hashCode() { . . . }
	protected Object clone()
		throws CloneNotSupportedException { . . . }
	public final void wait()
throws IllegalMonitorStateException,
InterruptedException { . . . } public final void wait(long millis) throws IllegalMonitorStateException, InterruptedException { . . . } public final void wait(long millis, int nanos) { . . . } throws IllegalMonitorStateException, InterruptedException { . . . } public final void notify() { . . . } throws IllegalMonitorStateException public final void notifyAll() { . . . } throws IllegalMonitorStateException protected void finalize() throws Throwable { . . . } }
The members of Object are as follows:

4.3.3 The Class String

Instances of class String (§20.12) represent sequences of Unicode characters. A String object has a constant (unchanging) value. String literals (§3.10.5) are references to instances of class String.

The string concatenation operator + (§15.17.1) implicitly creates a new String object.

4.3.4 When Reference Types Are the Same

Two reference types the same type if:

4.4 Where Types Are Used

Types are used when they appear in declarations or in certain expressions.

The following code fragment contains one or more instances of each kind of usage of a type:


import java.util.Random;

class MiscMath {

int divisor;
MiscMath(int divisor) { this.divisor = divisor; }
float ratio(long l) { try { l /= divisor; } catch (Exception e) { if (e instanceof ArithmeticException) l = Long.MAX_VALUE; else l = 0; } return (float)l; }
double gausser() { Random r = new Random(); double[] val = new double[2]; val[0] = r.nextGaussian(); val[1] = r.nextGaussian(); return (val[0] + val[1]) / 2; }
}
In this example, types are used in declarations of the following:

and in expressions of the following kinds:

4.5 Variables

A variable is a storage location and has an associated type, sometimes called its compile-time type, that is either a primitive type (§4.2) or a reference type (§4.3). A variable always contains a value that is assignment compatible (§5.2) with its type. A variable's value is changed by an assignment (§15.25) or by a prefix or postfix ++ (increment) or -- (decrement) operator (§15.13.2, §15.13.3, §15.14.1, §15.14.2).

Compatibility of the value of a variable with its type is guaranteed by the design of the Java language. Default values are compatible (§4.5.4) and all assignments to a variable are checked for assignment compatibility (§5.2), usually at compile time, but, in a single case involving arrays, a run-time check is made (§10.10).

4.5.1 Variables of Primitive Type

A variable of a primitive type always holds a value of that exact primitive type.

4.5.2 Variables of Reference Type

A variable of reference type can hold either of the following:

4.5.3 Kinds of Variables

There are seven kinds of variables:

  1. A class variable is a field declared using the keyword static within a class declaration (§8.3.1.1), or with or without the keyword static within an interface declaration (§9.3). A class variable is created when its class or interface is loaded (§12.2) and is initialized to a default value (§4.5.4). The class variable effectively ceases to exist when its class or interface is unloaded (§12.8), after any necessary finalization of the class or interface (§12.6) has been completed.
  2. An instance variable is a field declared within a class declaration without using the keyword static (§8.3.1.1). If a class T has a field a that is an instance variable, then a new instance variable a is created and initialized to a default value (§4.5.4) as part of each newly created object of class T or of any class that is a subclass of T (§8.1.3). The instance variable effectively ceases to exist when the object of which it is a field is no longer referenced, after any necessary finalization of the object (§12.6) has been completed.
  3. Array components are unnamed variables that are created and initialized to default values (§4.5.4) whenever a new object that is an array is created (§15.9). The array components effectively cease to exist when the array is no longer referenced. See §10 for a description of arrays.
  4. Method parameters (§8.4.1) name argument values passed to a method. For every parameter declared in a method declaration, a new parameter variable is created each time that method is invoked (§15.11). The new variable is initialized with the corresponding argument value from the method invocation. The method parameter effectively ceases to exist when the execution of the body of the method is complete.
  5. Constructor parameters (§8.6.1) name argument values passed to a constructor. For every parameter declared in a constructor declaration, a new parameter variable is created each time a class instance creation expression (§15.8) or explicit constructor invocation (§8.6.5) invokes that constructor. The new variable is initialized with the corresponding argument value from the creation expression or constructor invocation. The constructor parameter effectively ceases to exist when the execution of the body of the constructor is complete.
  6. An exception-handler parameter is created each time an exception is caught by a catch clause of a try statement (§14.18). The new variable is initialized with the actual object associated with the exception (§11.3, §14.16). The exception-handler parameter effectively ceases to exist when execution of the block associated with the catch clause is complete.
  7. Local variables are declared by local variable declaration statements (§14.3). Whenever the flow of control enters a block (§14.2) or for statement (§14.12), a new variable is created for each local variable declared in a local variable declaration statement immediately contained within that block or for statement. A local variable declaration statement may contain an expression which initializes the variable. The local variable with an initializing expression is not initialized, however, until the local variable declaration statement that declares it is executed. (The rules of definite assignment (§16) prevent the value of a local variable from being used before it has been initialized or otherwise assigned a value.) The local variable effectively ceases to exist when the execution of the block or for statement is complete.
The following example contains several different kinds of variables:


class Point {
	static int numPoints;								// numPoints is a class variable
	int x, y;								// x and y are instance variables
	int[] w = new int[10];								// w[0] is an array component
	int setX(int x) {								// x is a method parameter
		int oldx = this.x;							// oldx is a local variable
		this.x = x;
		return oldx;
	}
}

4.5.4 Initial Values of Variables

Every variable in a Java program must have a value before its value is used:

The example program:


class Point {
	static int npoints;
	int x, y;
	Point root;
}

class Test { public static void main(String[] args) { System.out.println("npoints=" + Point.npoints); Point p = new Point(); System.out.println("p.x=" + p.x + ", p.y=" + p.y); System.out.println("p.root=" + p.root); } }
prints:


npoints=0
p.x=0, p.y=0
p.root=null
illustrating the default initialization of npoints, which occurs when the class Point is prepared (§12.3.2), and the default initialization of x, y, and root, which occurs when a new Point is instantiated. See §12 for a full description of all aspects of loading, linking, and initialization of classes and interfaces, plus a description of the instantiation of classes to make new class instances.

4.5.5 Variables Have Types, Objects Have Classes

Every object belongs to some particular class: the class that was mentioned in the creation expression that produced the object, the class whose class object was used to invoke the newInstance method (§20.3.6) to produce the object, or the String class for objects implicitly created by the string concatenation operator + (§15.17.1). This class is called the class of the object. (Arrays also have a class, as described at the end of this section.) An object is said to be an instance of its class and of all superclasses of its class.

(Sometimes a variable or expression is said to have a "run-time type" but that is an abuse of terminology; it refers to the class of the object referred to by the value of the variable or expression at run time, assuming that the value is not null. Properly speaking, type is a compile-time notion. A variable or expression has a type; an object or array has no type, but belongs to a class.)

The type of a variable is always declared, and the type of an expression can be deduced at compile time. The type limits the possible values that the variable can hold or the expression can produce at run time. If a run-time value is a reference that is not null, it refers to an object or array that has a class (not a type), and that class will necessarily be compatible with the compile-time type.

Even though a variable or expression may have a compile-time type that is an interface type, there are no instances of interfaces. A variable or expression whose type is an interface type can reference any object whose class implements (§8.1.4) that interface.

Here is an example of creating new objects and of the distinction between the type of a variable and the class of an object:


public interface Colorable {
	void setColor(byte r, byte g, byte b);
}

class Point { int x, y; } class ColoredPoint extends Point implements Colorable {
byte r, g, b;
public void setColor(byte rv, byte gv, byte bv) { r = rv; g = gv; b = bv; }
}
class Test { public static void main(String[] args) { Point p = new Point(); ColoredPoint cp = new ColoredPoint(); p = cp; Colorable c = cp; } }
In this example:

Every array also has a class; the method getClass (§20.1.1), when invoked for an array object, will return a class object (of class Class) that represents the class of the array. The classes for arrays have strange names that are not valid Java identifiers; for example, the class for an array of int components has the name "[I" and so the value of the expression:

new int[10].getClass().getName()
is the string "[I"; see §20.1.1 for details.


Contents | Prev | Next | Index

Java Language Specification (HTML generated by Suzette Pelouch on February 24, 1998)
Copyright © 1996 Sun Microsystems, Inc. All rights reserved
Please send any comments or corrections to doug.kramer@sun.com