Abridged Java Language Specification Edited by Peter Komisar
Reference source:
The JavaTM Virtual Machine Specification Second Edition, Tim Lindholm Frank
Yellin
Two mighty resources come bundled with the Java
Development Kit's documentation, the online book
form of each, The
JavaTM Language Specification, by
James Gosling, Bill Joy, & Guy
Steele.
and The JavaTM
Virtual Machine Specification Second Edition by
Tim Lindholm &
Frank Yellin.
Most people who try to read the Java Language
Specification will find it terrifically detailed in a way that
would appeal only to consumate language specialists.
Fortunately, the authors of the Java Virtual Machine
did a paried down version of the specification
in Chapter 2 of their specification.
The following is an slightly edited version of
this chapter. (Anything more might be regarded as heretical)
Any omissions made are noted. The text is changed
only when on occasion the text is overly difficult
or in a minor way repetitive. The section on
Execution of the Virtual Machine is essentially untouched.
I hasten to add Chapter 2 of the Java Virtual
Machine Specification is very readable and provides a very
concise and precise definition of the language
parts. It provides an insightful view of the Java's authors'
perspective, of language elements.
I would recommend the serious Java student to
read the second chapter of "The Java Virtual Machine
Specification". On the other hand, if you are
simply seeking to gain insight into how the language has
been defined, reading the following shorter version
of the text may well serve your needs.
1) java keywords = black
2) java package class, interface, variable or method
=maroon
3) key terminology =green
4) selected statements = maroon;
(not
bold)
5) section topics = blue
Unicode
JDK release 1.1.7 and the Java 2 platform, v1.2
use the Unicode character encoding, version 2.1,
as specified in The Unicode Standard, Version
2.0, ISBN 0-201-48345-9, and the update information
for Version 2.1 of the Unicode Standard available
at http:// www.unicode.org
Except for comments, identifiers (§2.2),
and the contents of character and string literals (§2.3),
all input
elements in a program written in the Java programming
language are formed from only ASCII characters
Literals
A literal is the source code representation of
a value of a primitive type (§2.4.1), the String
type (§2.4.8),
or the null type
Types and Values
There are two type categories, primitives and
references, and one special type called the null type that in
practice can be treated as a special literal.
Corresponding is primitive and reference values which can be
1) stored as variables 2) passed as arguments
3) returned from methods & 4) be operated on.
Primitive Types [2.4.1]
A primitive is a predefined Java language type
named by a reserved keyword. A variable of a primitive type
always holds a value of the same type. The primitives
are boolean and the numeric types, the latter being
integral and floating-point. 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.
(§2.1).
The floating-point types are float and double,
which are conceptually associated with the 32-bit
single-precision and 64-bit double-precision values and
operations. (as specified in IEEE Standard for
Binary Floating-Point Arithmetic, ANSI/IEEE Standard
754-1985 (IEEE, New York). The boolean type has
the truth values true and false.
Operators on Integral Values [2.4.2]
The language provides operators that act on integral
values, including numerical comparison, arithmetic
operators, increment and decrement, bitwise logical
and shift operators, and numeric cast (§2.6.9).
Operands of certain unary and binary operators
are subject to numeric promotion (§2.6.10). The
integer
operators do not indicate overflow in any way;
they wrap around on overflow. The only integer operators
that can throw an exception are the integer divide
and integer remainder operators, which can throw an
ArithmeticException if the right-hand operand
is zero. Values of integral type may be cast to or
from any numeric type. There are no casts between
integral types and the type boolean.
Floating-Point Types, Value Sets, and Values [2.4.3]
The IEEE 754 standard includes positive and negative
sign-magnitude numbers, zeros, infinities and a
special Not-a-Number value, abbreviated "NaN",
used to represent the result of invalid operations such
as dividing zero by zero.
Java supports two sets of floating-point values, the float and the
double value set. and may support either
or both of two extended-exponent floating-point value sets, called
the float-extended-exponent value set
and the double-extended-exponent value set. (See source details of
the form of the floating point value set)
Operators on Floating-Point Values [2.4.4]
Operators that act on floating-point values, including numerical comparison,
arithmetic operators, increment
and decrement, and numeric cast (§2.6.9).
If one operands to a binary operator is of floating-point type,
then the operation is a floating-point operation, even if the other
operand is integral. Operands of certain
unary and binary operators are subject to numeric promotion (§2.6.10).
Java operators return IEEE 754
values.
In floating-point arithmetic every floating-point operator rounds 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 having zero as its least
significant bit is chosen. When
converting a floating-point value to an integer, round towards zero
mode is used (§2.6.3). Round towards
zero mode acts as though the number were truncated, discarding the
significand bits. Round towards zero
mode chooses as its result the format's value closest to and no greater
in magnitude than the infinitely
precise result. The floating-point operators of the Java programming
language produce no exceptions
(§2.16). An operation that overflows produces
a signed infinity; an operation that underflows produces a
denormalized value or a signed zero; and an operation that has no mathematically
definite result produces
NaN. All numeric operations (except for numeric comparison) with NaN
as an operand produce NaN
as a result.
Any value of any floating-point type may be cast (§2.6.9)
to or from any numeric type. There are no
casts between floating-point types and the type boolean.
Operators on boolean Values [2.4.5]
The boolean operators include relational operators and logical operators.
Only boolean expressions can
be used in control flow statements and as the first operand of the
conditional operator ?:. There are no
casts between the type boolean and any other type.
Reference Types, Objects, and Reference Values [2.4.6]
There are three kinds of reference types: theclass
types (§2.8), the interfacetypes
(§2.13),
and the
array types (§2.15).
An object is a dynamically created
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, or by invoking the
newInstance( ) method of class Class. An array is explicitly
created by an array creation expression.
An object is created in the heap and is garbage-collected after there
are no more references to it.
Objects cannot be reclaimed or freed by explicit language directives.
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, one can be use to modify and the other to observe the state
of the object. Each object has an
associated lock (§2.19, §8.13) that
is used by synchronized methods and by the synchronized statement
to provide control over concurrent access to state by multiple threads
(§2.19, §8.12).
Reference types form a hierarchy.
Each class type is a subclass of another class type, except for the class
Object (§2.4.7), which is the superclass
(§2.8.3)
of all other class and array types. All objects, including
arrays, support the methods of class Object. String
literals (§2.3) are references to
instances of class
String (§2.4.8).
The Class Object [2.4.7]
The standard class Object is the superclass (§2.8.3)
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. All class and array types
inherit the methods of class Object.
The Class String [2.4.8]
Instances of class String represent sequences of Unicode characters
(§2.1). A String object has a constant,
unchanging value. String literals (§2.3)
are references to instances of class String.
Operators on Objects[2.4.8]
The operators on objects include field access, method invocation, cast,
string concatenation, comparison
for equality, instanceof, and the conditional operator ?:.
Variables [2.5]
A variable is a storage location. It has an associated type, sometimes
called its compile-time type,
that is either a primitive type (§2.4.1)
or a reference type (§2.4.6). A variable of a
primitive type always
holds a value of that exact primitive type. A variable of reference
type can hold either a null reference
or a reference to any object whose class is assignment compatible (§2.6.7)
with the type of the variable.
Variable value to type compatibility is guaranteed by Java's language
design, where default values
(§2.5.1) are compatible and all assignments
are checked, at compile time, for compatibility. There are
seven kinds of variables:
1.A class variable is
a field of a class type declared using the keyword static (§2.9.1)
within a class
declaration, or with or without the keyword
static in an interface declaration. Class variables are
created when the class or interface is loaded
(§2.17.2)
and are initialized on creation to default values
(§2.5.1).
The class variable effectively ceases to exist when its class or interface
is unloaded (§2.17.8).
2.An instance variable
is a field declared within a class declaration without using the keyword
static
(§2.9.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 (§2.5.1)
as part of each newly created object of class T or of any class
that is a subclass of T. 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 (§2.17.7) has been
completed.
3.Array components are
unnamed variables that are created and initialized to default values (§2.5.1)
whenever a new object that is an array
is created (§2.17.6). The array components effectively cease
to exist when the array is no longer
referenced.
4.Method parameters
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. 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
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 or explicit constructor
invocation is evaluated. 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
variable is created each time an exception is caught by a catch
clause of a try statement (§2.16.2).
The new variable is initialized with the actual object associated with
the exception (§2.16.3).
The exception-handler parameter effectively ceases to exist when execution
of the block associated with the catch
clause (§2.16.2) is complete.
7.Local variables are
declared by local variable declaration statements. Whenever the flow of
control
enters a block or a for statement, a
new variable is created for each local variable declared in a local
variable declaration statement immediately
contained within that block or for statement. The local
variable is not initialized, however,
until the local variable declaration statement that declares it is
executed. The local variable effectively
ceases to exist when the execution of the block or for
statement is complete.
Initial Values of Variables [2.5.1]
Every variable in a program must have a value before it is used. Each
class variable, instance variable,
and array component is initialized with a default value when it is
created. (tables not in original
source)
Type | Default Value |
byte | zero // (byte)0 |
short | zero // (short)0 |
int | zero // 0 |
long | zero // 0L |
float | positve zero // 0.0f |
double | positive zero // 0.0 |
char | the null character // '\u0000' |
boolean | false |
reference types | null (§2.4.6, §2.3) |
method parameter | the argument value provided at invocation (§2.3) |
constructor parameter | the argument value provided by an object creation expression or explicit constructor invocation. (§2.5) |
exception-handler parameter | initialized to the thrown object representing the exception (§2.16.3). |
local variable | must be explicitly given a value, by either initialization or assignment, before it is used |
Variables Have Types, Objects Have Classes [2.5.2]
Every object belongs to some particular class. (typed by the class used
in the class instance creation
expression or the invocation of the newInstance method) This class
is called the class of the object. An
object is said to be an instance of its class and of all superclasses
of its class. (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 runtime 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 (§2.13). A variable
or expression whose type is an interface type can reference
any object whose class implements that interface.
Every array also has a class. The classes for arrays have strange names
that are not valid identifiers; for
example, the class for an array of int components has the name "[I".
Conversions and Promotions [2.6]
A conversion from type S to type T allows an expression of type S to
be treated at compile time as if it
were of type T instead. In some cases this will require a corresponding
action at run time to check the
validity of the conversion or to translate the runtime value of the
expression into a form appropriate for
the new type T. Numeric promotions are conversions that change an operand
of a numeric operation to
a wider type, or both operands of a numeric operation to a common type,
so that an operation can be
performed. In the Java programming language, there are six broad kinds
of conversions:
1) Identity conversions
2) Widening primitive conversions
3) Narrowing primitive conversions
4) Widening reference conversions
5) Narrowing reference conversions
6) String conversions
There are five conversion contexts, (listed below), in which conversion
expressions can occur. Each
context allows conversions in some of the above-named categories but
not others.
1) Assignment conversion (§2.6.7),
which converts the type of an expression to the type of a specified
variable. The conversions permitted are limited
to those that never causes an exception.
2) Method invocation conversion (§2.6.8),
applied to each argument in a method or constructor
invocation, and, except in one case, performs the
same conversions that assignment conversion does.
Method invocation conversion never causes an exception.
3)Casting conversion (§2.6.9),
converts the type of an expression to a type specified by a cast operator.
It is more inclusive than assignment or method invocation
conversion, allowing any specific conversion
other than a string conversion, but certain casts
to a reference type may cause an exception at run time.
4) String conversion, which allows
any type to be converted to type String (§2.4.8).
5) Numeric promotion brings the
operands of a numeric operator to a common type so that an operation
can be performed.
String conversion only applies to operands of the binary + and += operators
when one of the arguments is
a String; it will not be covered further.
Widening Primitive Conversions [2.6.2]
byte to short, int, long, float, or double |
short to int, long, float, or double |
char to int, long, float, or double |
int to long, float, or double |
long to float or double |
float to double |
Widening primitive conversions do not lose information about the sign
or order of magnitude of a numeric
value. Conversions widening from an integral type to another integral
type or from float to double in strictfp
expressions do not lose any information at all; the numeric value is
preserved exactly. However, conversions
that are not strictfp may lose information about the overall magnitude
of the converted value.
Conversion of an int or a long value to float, or of a long value to
double, may lose precision, that is, the
result may lose some of the least significant bits of the value. The
resulting floating-point value is a correctly
rounded, IEEE 754 version of the integer value.
A widening conversion of a value of type char to an integral type zero-extends
the representation of the
character value to fill the wider format.
Despite the fact that loss of precision may occur, widening primitive
conversions never result in a runtime
exception (§2.16).
Narrowing Primitive Conversions [2.6.3]
The following conversions on primitive types are called narrowing primitive
conversions :
byte tochar |
short to byte or char |
char to byte or short |
int to byte, short, or char |
long to byte, short, char, or int |
float to byte, short, char, int, or long |
double to byte, short, char, int, long, or float |
Narrowing conversions may lose information about the sign or order of
magnitude, or both, of a
numeric value (i.e. narrowing an int value 32763 to type byte produces
the value -5).
Narrowing conversions may also lose precision. A narrowing conversion
of a signed integer to an
integral type simply discards all but the n lowest-order bits, where
n is the number of bits used to
represent the type. This may cause the resulting value to have a different
sign from the input value.
A narrowing conversion of a character to an integral type likewise
simply discards all but the n lowest
bits, where n is the number of bits used to represent the type. This
may cause the resulting value to be a
negative number, even though characters represent 16-bit unsigned integer
values.
In a narrowing conversion of a floating-point number to an integral
type, if the floating-point number
is NaN, the result of the conversion is 0 of the appropriate type.
If the floating-point number is too large
to be represented by the integral type or is positive infinity, the
result is the largest representable value
of the integral type. If the floating-point number is too small to
be represented or is negative infinity,
the result is the smallest representable value of the integral type.
Otherwise, the result is the floating-point
number rounded towards zero to an integer value using IEEE 754 round
towards zero mode (§2.4.4)
A narrowing conversion from double to float behaves in accordance with
IEEE 754. The result is
correctly rounded using IEEE 754 round to nearest mode (§2.4.4).
A value too small to be represented
as a float is converted to a positive or negative zero; a value too
large to be represented as a float is
converted to a positive or negative infinity. A double NaN is always
converted to a float NaN.
Despite the fact that overflow, underflow, or loss of precision may
occur, narrowing conversions among
primitive types never result in a runtime exception.
Widening Reference Conversions[2.6.4]
Widening reference conversions never require a special action at run
time and therefore never throw an
exception at run time.
Narrowing Reference Conversions[2.6.5]
The following permitted conversions are called the narrowing reference conversions:
-From any class type S to any class type T, provided that S is a superclass
of T.
-From any class type S to any interface type K, provided that S is
not final and does not implement K.
- (Important special cases are narrowing conversion from the class
type Object to any other class type,
any array type or any inteface type.)
-From any interface type J to any class type T that is not final.
-From any interface type J to any class type T that is final, provided
that T implements J.
-From any interface type J to any interface type K, provided that J
is not a subinterface of K and there is
no method name m such that J and K both declare a method named
m with the same signature but different
return types.
-From any array type SC[] to any array type TC[], provided that SC
and TC are reference types and there
is a permitted narrowing conversion from SC to TC.
Such conversions require a test at run time to find out whether the
actual reference value is a legitimate
value of the new type. If it is not, the Java virtual machine throws
a ClassCastException.
Value Set Conversion[2.6.6]
Value set conversion is the process of mapping a floating-point value
from one value set (§2.4.3) to
another without changing its type. For each operation in an expression
that is not FP-strict (§2.18),
value set conversion allows an implementation of the Java programming
language to choose between
two options:
If the value is an element of the float-extended-exponent value set,
then the implementation may map
the value to the nearest element of the float value set. This conversion
may result in overflow (in which
case the value is replaced by an infinity of the same sign) or underflow
(in which case the value may lose
precision because it is replaced by a denormalized number or zero of
the same sign).
If the value is an element of the double-extended-exponent value set,
then the implementation may map
the value to the nearest element of the double value set. This conversion
may result in overflow (in which
case the value is replaced by an infinity of the same sign) or underflow
(in which case the value may lose
precision because it is replaced by a denormalized number or zero of
the same sign).
(For more details regarding the rules of Value Set Conversion, see
source.)
Assignment Conversion[2.6.7]
Assignment conversion occurs when the value of an expression is assigned
to a variable: the type of the
expression must be converted to the type of the variable. Assignment
contexts allow the use of an identity
conversion (§2.6.1), a widening primitive
conversion (§2.6.2), or a widening reference
conversion (§2.6.4).
In addition, a narrowing primitive conversion (§2.6.3)
may be used if the following conditions are satisfied:
1) The expression is a constant expression of type int.
2) The type of the variable is byte, short, or char.
3) The value of the expression is representable in the type of
the variable.
-If the type of the expression can be converted to the type of a variable
by assignment conversion, we
say the expression (or its value) is assignable to the variable
or, equivalently, that the type of the
expression is assignment compatible with the type of the variable.
-If the type of the variable is float or double, then value set conversion
(§2.6.6)
is applied after the type
conversion:
-If the value is of type float and is an element of the float-extended-exponent
value set, then the
implementation must map the value to the nearest element of
the float value set. This conversion may
result in overflow or underflow.
-If the value is of type double and is an element of the double-extended-exponent
value set, then the
implementation must map the value to the nearest element of
the double value set. This conversion may
result in overflow or underflow.
An assignment conversion never causes an exception. A value of primitive
type must not be assigned to
a variable of reference type. A value of reference type must not be
assigned to a variable of primitive type.
A value of type boolean can be assigned only to a variable of type
boolean. A value of the null type may
be assigned to a variable of any reference type.
Assignment of a value of compile-time reference type S (source) to
a variable of compile-time reference
type T (target) is permitted:
1) If S is a class type:
2) If T is a class type, then S must be the same class
as T, or S must be a subclass of T.
3) If T is an interface type, then S must implement interface
T.
4) If S is an interface type:
5) If T is a class type, then T must be Object.
6) If T is an interface type, then T must be the same interface
as S, or T must be a superinterface of S.
7) If S is an array type SC[], that is, an array of components
of type SC:
8) If T is a class type, then T must be Object.
9) If T is an interface type, then T must be either Cloneable
or java.io.Serializable.
10) If T is an array type TC[], that is, an array of components of
type TC, then either TC and SC must
be the same primitive type, or TC and
SC are both reference types and type SC is assignable to TC.
Method Invocation Conversion[2.6.8]
Method invocation conversion is applied to each argument value in a
method or constructor invocation:
the type of the argument expression must be converted to the type of
the corresponding parameter.
Method invocation contexts allow the use of an identity conversion
(§2.6.1), a widening primitive
conversion (§2.6.2), or a widening reference
conversion (§2.6.4). Method
invocation conversions specifically
do not include the implicit narrowing of integer
constants that is part of assignment conversion (§2.6.7).
If the type of an argument expression is either float or double, then
value set conversion (§2.6.6) is applied
after the type conversion:
If an argument value of type float is an element of the float-extended-exponent
value set, then the
implementation must map the value to the nearest element of the float
value set. This conversion may result
in overflow or underflow.
If an argument value of type double is an element of the double-extended-exponent
value set, then the
implementation must map the value to the nearest element of the double
value set. This conversion may
result in overflow or underflow.
Casting Conversion[2.6.9]
Casting conversions are more powerful than assignment or method invocation
conversions applied to
the operand of a cast operator: the type of the operand expression
must be converted to the type
explicitly named by the cast operator. Casting contexts allow the use
of an identity conversion (§2.6.1),
a widening primitive conversion (§2.6.2), a narrowing primitive
conversion (§2.6.3), a widening reference
conversion (§2.6.4), or a narrowing reference conversion (§2.6.5).
Thus, casting conversions are more
inclusive than assignment or method invocation conversions: a
cast can do any permitted conversion
other than a string conversion.
Value set conversion (§2.6.6) is applied
after the type conversion.
Casting can convert a value of any numeric type
to any other numeric type. A value of type boolean
cannot be cast to another type. A value of reference
type cannot be cast to a value of primitive type.
Some casts can be proven incorrect at compile time and result in a
compile-time error. Otherwise, either
the cast can be proven correct at compile time, or a runtime validity
check is required. (See The
JavaTM Language Specification for details.) If the value at run time
is a null reference, then the cast is
allowed. If the check at run time fails, a ClassCastException is thrown.
Numeric Promotion[2.6.10]
Numeric promotion is applied to the operands of an arithmetic operator.
Numeric promotion contexts
allow the use of 1) an identity conversion (§2.6.1)
or 2) a widening primitive conversion (§2.6.2).
Numeric promotions are used to convert the operands of a numeric operator
to a common type where
an operation can be performed. The two kinds of numeric promotion are
unary numeric promotion and
binary numeric promotion. The analogous conversions in C are called
"the usual unary conversions" and "
the usual binary conversions." Numeric promotion is not a general feature
of the Java programming language,
but rather a property of specific built-in operators.
An operator that applies unary numeric promotion to a single operand
of numeric type converts
an
operand of type byte, short, or char to int by
a widening primitive conversion, and otherwise leaves the
operand alone. Value set conversion (§2.6.6) is then applied.
The operands of the shift operators are
promoted independently using unary numeric promotions.
When an operator applies binary numeric promotion to a pair of numeric
operands, the following rules
apply, in order, using widening primitive conversion to convert operands
as necessary:
1) If either operand is of type double, the other
is converted to double.
2) Otherwise, if either operand is of type float, the
other is converted to float.
3) Otherwise, if either operand is of type long, the other
is converted to long.
4) Otherwise, both operands are converted to type int.
After type conversion, if any, value set conversion is applied to each
operand.
Names and Packages [2.7]
Names are used to refer to entities declared in a program. A declared
entity is a package, type, member
(field or method) of a type, parameter, or local variable. Programs
are organized sets of packages.
Simple Names and Qualified Names [2.7.1]
A simple name is a single identifier (§2.2).
Qualified names (§2.7.4) provide access to members
of packages
and reference types. A qualified name consists
of a name, a "." token, and an identifier.
Not all identifiers are part of a name. Identifiers are also used in
declarations, where the identifier
determines the name by which an entity will be known, in field access
expressions and method invocation
expressions, and in statement labels and break and continue statements
that refer to statement labels.
Packages [2.7.2]
A package consists of a number of compilation units and has a hierarchical
name. Packages are
independently developed, and each package has its own set of names,
which helps to prevent name
conflicts. Each Java virtual machine implementation determines how
packages, compilation units, and
subpackages are created and stored; which top-level package names are
in scope in a particular
compilation; and which packages are accessible. Packages may be stored
in a local file system, in a
distributed file system, or in some form of database.
A package name component or class name might contain a character that
cannot legally appear in a host
file system's ordinary directory or file name: for instance, a Unicode
character on a system that allows
only ASCII characters in file names.
A Java virtual machine implementation must support at least one unnamed
package; it may support more
than one but is not required to do so. Which compilation units are
in each unnamed package is determined
by the host system. Unnamed packages are provided principally for convenience
when developing small
or temporary applications or when just beginning development.
An import declaration allows a type declared in another package
to be known by a simple name rather
than by the fully qualified name (§2.7.5) of
the type. An import declaration affects only the type
declarations of a single compilation unit. A compilation unit automatically
imports each of the public type
names declared in the predefined package java.lang. //compilation
unit --> source file ed.
Members [2.7.3]
Packages and reference types have members. The members of a package
(§2.7.2)
are subpackages and
all the class (§2.8) and interface (§2.13)
types declared in all the compilation units of the package. The
members of a reference type are fields (§2.9),
methods (§2.10), and nested classes and interfaces.
The Members of a Package[2.7.3.1]
In general, the subpackages of a package are determined by the host
system. However, the standard
package java always has the subpackages lang, util, io, and net. No
two distinct members of the same
package may have the same simple name (§2.7.1), but members of
different packages may have the
same simple name.
The Members of a Class Type[2.7.3.2]
The members of a class type (§2.8) are fields
(§2.9), methods(§2.10),
and nested classes and interfaces.
These include members inherited from its direct superclass (§2.8.3),
if it has one, members inherited from
any direct superinterfaces (§2.13.2),and
any members declared in the body of the class. There is no
restriction against a field and a method of a class type having the
same simple name.
A class type may have two or more methods with the same simple name
if they have different numbers
of parameters or different parameter types in at least one parameter
position. Such a method member
name is said to be overloaded.
A class type may contain a declaration for a method with the same name
and the same signature as a method that would otherwise be inherited
from a superclass or superinterface.
In this case, the method of the superclass or superinterface is not
inherited. If the method not inherited
is abstract, the new declaration is said to implement the method; if
it is not abstract, the new declaration
is said to override it.
The Members of an Interface Type[2.7.3.3]
The members of an interface type (§2.13) are
fields, methods, and nested classes and interfaces. The
members of an interface are the members inherited from any direct superinterfaces
(§2.13.2)
and members
declared in the body of the interface.
The Members of an Array Type[2.7.3.4]
The members of an array type (§2.15) are the
members inherited from its superclass, the class Object
(§2.4.7), and the field length, which is
a constant (final) field of every array.
Qualified Names and Access Control[2.7.4]
Qualified names (§2.7.1) are a means of access
to members of packages and reference types; related means
of access include field access expressions and method invocation expressions.
All
three are syntactically
similar in that a "." token appears,preceded
by some indication of a package, type, or expression having
a type and followed by an identifier that names a member of the package
or type. These are collectively
known as constructs for qualified access.
The Java programming language provides mechanisms for limiting qualified
access, to prevent users of a
package or class from depending on unnecessary
details of the implementation of that package or class.
Access control also applies to constructors.
Whether a package is accessible is determined by the host
system.
A class or interface may be declared public, in which case it
may be accessed, using a qualified name, by
any class or interface that can access the package
in which it is declared. A class or interface that is not
declared public may be accessed from, and only
from, anywhere in the package in which it is declared.
Every field or method of an interface must be
public. Every member of a public interface is implicitly public,
whether or not the keyword public appears in
its declaration. It follows that a member of an interface is
accessible if and only if the interface itself is accessible.
A field, method, or constructor of a class may be declared using at
most one of the public, private, or
protected keywords. A public member may be accessed
by any class or interface. A private member
may be accessed only from within the class that
contains its declaration. A
member that is not declared
ublic, protected, or private is said to havedefault
access and may be accessed from, and only from,
anywhere in the package in which it is declared.
A protected member of an object may be accessed only by code
responsible for the implementation of
that object. To be precise, a protected member may be accessed from
anywhere in the package in which
it is declared and, in addition, it may be accessed from within any
declaration of a subclass of the class
type that contains its declaration, provided that certain restrictions
are obeyed.
Fully Qualified Names [2.7.5]
Every package, class, interface, array type, and primitive type has
a fully qualified name. It follows that
every type except the null type has a fully qualified name. The fully
qualified name of a primitive type is the
keyword for that primitive type, namely, boolean, char, byte, short,
int, long, float, or double.
The fully qualified name of a named package that is not a subpackage
of a named package is its simple name.
The fully qualified name of a named package that is a subpackage of
another named package consists of
the fully qualified name of the containing package followed by "."
followed by the simple (member) name
of the subpackage.
The fully qualified name of a class or interface declared in an unnamed
package is it's simple name.
The fully qualified name of a class or interface that is declared in
a named package consists of the fully
qualified name of the package followed by "." followed by the simple
name of the class or interface.
The fully qualified name of an array type consists of the fully qualified
name of the component type of
the array type followed by "[]".
Classes [2.8]
A class declaration specifies a new reference type and provides its
implementation. Each class is
implemented as a subclass of a single existing class. A class may also
implement one or more interfaces.
The body of a class declares members (fields and methods), static initializers,
and constructors.
Class Names [2.8.1]
If a class is declared in a named package with the fully qualified name
P, then the class has the fully
qualified name P.Identifier. If the class is in an unnamed package,
then the class has the fully qualified
name Identifier. Two classes are the same class (and therefore the
same type) if they are loaded by
the same class loader (§2.17.2) and they
have the same fully qualified name (§2.7.5).
Class Modifiers [2.8.2]
A class declaration may include class modifiers. A class may be declared
public, as discussed in §2.7.4.
An abstract class is a class that
is incomplete, or considered incomplete. Only abstract classes may
have abstract methods (§2.10.3), that is,
methods that are declared but not yet implemented.
A class can be declared final if its definition
is complete and no subclasses are desired or required.
Because a final class never has subclasses, it's methods cannot be
overridden A class cannot be both final
and abstract, because the implementation of such a class could never
be completed.
A class can be declared strictfp to indicate that all expressions in
the methods of the class are FP-strict
(§2.18), whether or not the methods themselves
are declared FP-strict.
A class is declared public to make accessible to packages other than
the one in which it is declared,
using either its fully qualified name or a shorter name created by
an import declaration (§2.7.2). If a class
lacks the public modifier, access to the class declaration is limited
to the package in which it is declared.
Superclasses and Subclasses[2.8.3]
The extends claus in a class declaration specifies the direct
superclass of the current class, from
whose implementation the current class is derived. Only the class Object
(§2.4.7)
has no direct superclass.
If the extends clause is omitted from the class declaration, then the
superclass of the new class is Object.
The Class Members[2.8.4]
The members of a class type include those declared in the body of the
class as well as those inherited
from the direct superclass (§2.8.3) and any
direct superinterfaces (§2.13.2) Members
of a superclass
declared private are not inherited by subclasses of that class. Members
of a class not declared private,
protected, or public are not inherited by subclasses declared in a
package other than the one in which
the class is declared. Constructors (§2.12)
and static initializers (§2.11) are not members and therefore
are not inherited.
Fields [2.9]
The variables of a class type are its fields. Class (static) variables
exist once per class. Instance variables
exist once per instance of the class. Fields may include initializers
and may be modified using various
modifier keywords. If the class declares a field with a certain name,
then the declaration of that field is
said to hide any and all accessible declarations of fields with the
same name in the superclasses and
superinterfaces of the class. A class inherits from its direct superclass
and direct superinterfaces all the
fields of the superclass and superinterfaces that are accessible to
code in the class and are not hidden by
a declaration in the class. A hidden field can
be accessed by using a qualified name (if it is static) or by
using a field access expression that contains
a cast to a superclass type or the keyword super.
A value stored in a field of type float is always an element of the
float value set (§2.4.3); similarly, a
value stored in a field of type double is always an element of the
double value set. It is not permitted
for a field of type float to contain an element of the float-extended-exponent
value set that is not also an
element of the float value set, nor for a field of type double to contain
an element of the
double-extended-exponent value set that is not also an element of the
double value set.
Field Modifiers [2.9.1]
Fields may be declared public, protected, or private. If a field is
declared static, there exists exactly one
incarnation of the field, no matter how many instances (possibly zero)
of the class are created. A static
field, also called a
class variable, is incarnated when the class is initialized
(§2.17.4).
A field that is not
declared static is called an instance variable.
For every new class instance, a new variable associated
with that instance is created for every instance variable declared
in that class or in any of its superclasses.
A field can be declared final, in which case its declarator
must include a variable initializer (§2.9.2).
Both
class and instance variables may be declared final. Once
a final field has been initialized, it always contains
the same value. If a final field holds
an object reference, then the state of the object may be changed by
operations on the object, but the field will always refer to the same
object. Variables may be marked
transient to indicate that they are not
part of the persistent state of an object.
The Java programming language allows threads that access shared variables
to keep private working
copies of the variables; this allows a more efficient implementation
of multiple threads (§2.19). These
working copies need to be reconciled with the master copies in the
shared main memory only at prescribed
synchronization points, namely, when objects are locked or unlocked
(§2.19).
As a rule, to make sure that
shared variables are consistently and reliably updated, a thread should
ensure that it has exclusive access
to such variables by obtaining a lock that
conventionally enforces mutual exclusion for those shared variables.
Alternatively, a field may be declared volatile, in which case
a thread must reconcile its working copy of
the field with the master copy every time it accesses the variable.
Moreover, operations on the master copies
of one or more volatile variables on behalf of a thread are performed
by the main memory in exactly the
order that the thread requested. A final field
cannot also be declared volatile.
Initialization of Fields [2.9.2]
If a field declaration contains a variable initializer, then it has
the semantics of an assignment to the
declared variable. If the declaration is for a class variable,(a static
field), the variable initializer is
evaluated and the assignment performed exactly once, when the class
is initialized
(§2.17.4). If the
declaration is for an instance variable (a field that is not static),
the variable initializer is evaluated
and the assignment performed each time an instance of the class is
created.
Methods [2.10]
A method declares executable code that can be invoked, passing a fixed
number of values as arguments.
Every method declaration belongs to some class. A class inherits from
its direct superclass (§2.8.3) and
any direct superinterfaces (§2.13.2) all
accessible methods, with one exception: if a name is declared as a
method in the new class, then no method with the same signature (§2.10.2)
is inherited. Instead, the newly
declared method is said to override any
such method declaration. An overriding method must not conflict
with the definition that it overrides, for instance, by having a different
return type. Overridden methods of
the superclass can be accessed using a method invocation expression
involving the super keyword.
Formal Parameters[2.10.1]
The formal parameters of a method, if any, are specified by a list of
comma-separated parameter specifiers.
Each parameter specifier consisting of a type and an identifier. When
the method is invoked, the values
of the actual argument expressions initialize newly created parameter
variables (§2.5), each of the declared
type, before execution of the body of the method.
A method parameter of type float always contains an element of the
float value set (§2.4.3); similarly, a
method parameter of type double always contains an element of the double
value set. It is not permitted
for a method parameter of type float to contain an element of the float-extended-exponent
value
set that
is not also an element of the float value set, nor for a method parameter
of type double to contain an
element of the double-extended-exponent value set that is not also
an element of the double value set.
Where an argument expression corresponding to a parameter variable
is not FP-strict (§2.18),
evaluation of that actual argument expression is permitted to use values
drawn from the appropriate
extended-exponent value sets. Prior to being stored in the parameter
variable, the result of such an
expression is mapped to the nearest value in the corresponding standard
value set by method invocation
conversion (§2.6.8).
Method Signature [2.10.2]
The signature of a method consists of the name of the method and the
number and type of formal
parameters (§2.10.1) A class may not declare
two methods with the same signature.
Method Modifiers[2.10.3]
The access modifiers public, protected, and private are discussed in
Section 2.7.4.
An abstract method declaration
introduces the method as a member, providing its signature (§2.10.2),
return type, and throws clause (if any), but does not provide an implementation.
The declaration of an
abstract method m must appear within an abstract class (call it A).
Every subclass of A that is not itself
abstract must provide an implementation for m. A method declared abstract
cannot also be declared to
be private, static, final, native, strictfp, or synchronized.
A method that is declared static is called a class method. A class
method is always invoked without
reference to a particular object. A class method may refer to other
fields and methods of the class by
simple name only if they are class methods and class (static) variables.
A method that is not declared static is an instance method. An instance
method is always invoked with
respect to an object, which becomes the current object to which the
keywords this and super refer
during execution of the method body.
A method can be declared final to prevent subclasses
from overriding or hiding it. A private method
and all methods declared in a final class (§2.8.2)
are implicitly final, because it is impossible to override
them. If a method is final or implicitly final, a compiler or a runtime
code generator can safely "inline"
the body of a final method, replacing an invocation of the method with
the code in its body.
A synchronized method will acquire a
monitor lock (§2.19) before it executes. For a class (static)
method, the lock associated with the class object for the method's
class is used. For an instance method,
the lock associated with this (the object for which the method is invoked)
is used. The same per-object
lock is used by the synchronized statement.
A method can be declared strictfp to indicate that all expressions
in the method are FP-strict (§2.18).
A method can be declared native to indicate that it is implemented
in platform-dependent code, typically
written in another programming language such as C, C++, or assembly
language. A method may not be
declared to be both native and strictfp.
Static Initializers [2.11]
Any static initializersdeclared
in a class are executed
when the class is initialized (§2.17.4)
and,
together with any field initializers (§2.9.2)
for class variables, may be used to initialize the class variables
of the class (§2.17.4).
The static initializers and class variable initializers are executed
in textual order. They may not refer to class
variables declared in the class whose declarations appear textually
after the use, even though these class
variables are in scope. This restriction is designed to catch, at compile
time, most circular or otherwise
malformed initializations.
Constructors [2.12]
A constructor is used in the creation of an object that is an instance
of a class. The constructor declaration
looks like a method declaration that has no result type. Constructors
are invoked by class instance creation
expressions (§2.17.6),
by the conversions and concatenations caused by the string concatenation
operator +,
and by explicit constructor invocations from other constructors; they
are never invoked by method
invocation expressions. Constructor declarations
are not members. They are never inherited and therefore
are not subject to hiding or overriding.
If a constructor body does not begin with an explicit constructor invocation
and the constructor being
declared is not part of the primordial classObject,
then the constructor body is implicitly assumed by
the compiler to begin with a superclass constructor invocation "super(
);", an invocation of the constructor
of the direct superclass that takes no arguments.
If a class declares no constructors then a default constructor, which
takes no arguments, is automatically
provided. If the class being declared is Object, then the default constructor
has an empty body. Otherwise,
the default constructor takes no arguments and simply invokes the superclass
constructor with no arguments.
If the class is declared public, then the default constructor is implicitly
given the access modifier public.
Otherwise, the default constructor has the default access implied by
no access modifier (§2.7.4).
A class can be designed to prevent code outside
the class declaration from creating instances of the class
by declaring at least one constructor, in order
to prevent the creation of an implicit constructor, and declaring
all constructors to be private.
Constructor Modifiers[2.12.1]
Access to constructors is governed by the access modifiers public,
protected,
and private (§2.7.4).
A constructor cannot be abstract, static, final,
native,
or synchronized. A constructor cannot be
declared to be strictfp. This difference in the definitions
for method modifiers (§2.10.3) and constructor
modifiers is an intentional language design choice; it effectively
ensures that a constructor is FP-strict
(§2.18) if and only if its class is FP-strict,
so to speak.
Interfaces [2.13]
An interface is a reference type whose members are constants and abstract
methods. This type has no
implementation, but otherwise unrelated classes can implement it by
providing implementations for its
abstract methods. Programs can use interfaces to make it unnecessary
for related classes to share a
common abstract superclass or to add methods to Object.
An interface may be declared to be a direct extension of one or more
other interfaces, meaning that it
implicitly specifies all the abstract methods and constants of the
interfaces it extends, except for any
constants that it may hide, and perhaps adds newly declared members
of its own.
A class may be declared to directly implement one or more interfaces,
meaning that any instance of the
class implements all the abstract methods specified by that interface.
A class necessarily implements all
the interfaces that its direct superclasses and direct superinterfaces
do. This (multiple) interface inheritance
allows objects to support (multiple) common behaviors without sharing
any implementation.
A variable whose declared type is an interface type may have as its
value a reference to an object that is
an instance of any class that is declared to implement the specified
interface. It is not sufficient that the
class happens to implement all the abstract methods of the interface;
the class or one of its superclasses
must actually be declared to implement the interface, or else the class
is not considered to implement the
interface.
Interface Modifiers[2.13.1]
An interface declaration may be preceded by the interface modifiers
public, strictfp, and abstract.
The access modifier public is discussed in (§2.7.4).Every
interface is implicitly abstract. All members
of interfaces are implicitly public. An
interface cannot be final, because the implementation of such a
class could never be completed.
Superinterfaces [2.13.2]
If an extends clause is provided, then the interface being declared
extends each of the other named
interfaces and therefore inherits the methods and constants of each
of the other named interfaces. Any
class that implements the declared interface is also considered to
implement all the interfaces that this
interface extends and that are accessible to the class.
The implements clause in a class declaration lists the names
of interfaces that are direct superinterfaces
of the class being declared. All interfaces in the current package
are accessible. Interfaces in other
packages are accessible if the host system permits access to the package
and the interface is declared
public.Unlike class Object, which every class extends, there is no
single interface of which all interfaces
are extensions.
Interface Members [2.13.3]
The members of an interface are those members inherited from direct
superinterfaces and those members
declared in the interface. The interface inherits, from the interfaces
it extends, all members of those interfaces,
except for fields with the same names as fields it declares. Interface
members are either fields or methods.
Interface (Constant) Fields [2.13.3.1]
Every field declaration in the body of an interface
is implicitly static and final. Interfaces do not have instance
variables. Every field declaration in an interface is itself implicitly
public. A constant declaration in an
interface must not include either of the modifiers transient
or volatile.
Every field in the body of an interface must have an initialization
expression, which need not be a constant
expression. The variable initializer is evaluated and the assignment
performed exactly once, when the
interface is initialized (§2.17.4).
Interface (Abstract) Methods [2.13.3.2]
Every method declaration in the body of an interface
is implicitly abstract and implicitly public.
A method declared in the body of an interface must not be declared
static,
because static methods
cannot be abstract. A method declared in the body of an interface must
not be declared native, strictfp,
or synchronized, because those keywords describe implementation
properties rather than interface
properties; however, a method declared in an interface may be implemented
by a method that is declared
native, strictfp, or synchronized in a class that implements the interface.
A method declared in the body of
an interface must not be declared final; however, one may be
implemented by a method that is declared
final in a class that implements the interface.
Overriding, Inheritance, and Overloading in Interfaces [2.13.4]
If the interface declares a method, then the declaration of that method
is said to override any and all
methods with the same signature in the superinterfaces of the interface
that would otherwise be
accessible to code in this interface. An interface inherits from its
direct superinterfaces all methods of
the superinterfaces that are not overridden by a method declared in
the interface. If two methods of an
interface (whether declared or inherited) have the same name but different
signatures, then the method
name is said to be overloaded.
Nested Classes and Interfaces[2.14] //references to Interfaces edited out as no info is provided
JDK release 1.1 added nested classes and interfaces to the Java programming
language. Nested
classes are sometimes referred to as inner
classes. A full specification of nested classes and interfaces
will be published in the second edition of The JavaTM Language Specification.
Until then, interested
persons should refer to the Inner Classes Specification, which may
be found at http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclasses.doc.html.
Arrays [2.15]
Arrays are objects, are dynamically created, and may be assigned to
variables of type Object (§2.4.7).
All methods on arrays are inherited from class Object except the clone
method, which arrays override.
All arrays implement the interfaces Cloneable
and java.io.Serializable. An array
object contains a
number of variables. That number may be zero, in which case the array
is said to be empty. The variables
contained in an array have no names; instead they are referenced by
array access expressions that use
nonnegative integer index values. These variables are called the components
of the array. If an array
has n components, we say n is the length of the array. An array of
zero components is not the same as
the null reference (§2.4).
An array component of type float is always an element of the float
value set (§2.4.3); similarly, a
component of type double is always an element of the double value set.
A component of type float may
not be an element of the float-extended-exponent value set unless it
is also an element of the float value
set. A component of type double may not be an element of the double-extended-exponent
value set
unless it is also an element of the double value set.
Array Types [2.15.1]
All the components of an array have the same type, called the component
type of the array. If the
component type of an array is T, then the type of the array itself
is written T[]. The component type
of an array may itself be an array type. The components of such an
array may contain references to
subarrays. If, starting from any array type, one considers its component
type, and then (if that is also
an array type) the component type of that type, and so on, eventually
one must reach a component
type that is not an array type; this is called the element type of
the original array, and the components
at this level of the data structure are called the elements of the
original array.
There are three situations in which an element of an array can be an
array: if the element type is of type
Object (§2.4.7),
Cloneable,
or java.io.Serializable, then some
or all of the elements may be arrays,
because every array object can be assigned to a variable of one of
those types.
In the Java programming language, unlike in C, an array of char
is
not a String (§2.4.7),
and neither a
String nor an array of char is terminated by '\u0000' (the NUL-character).
A String object is immutable
(its value never changes), while an array of char has mutable elements.
The element of an array may be any primitive or reference type. In particular,
arrays with an interface
type as the component type are supported; the elements of such an array
may have as their value a null
reference or instances of any class type that implements the interface.
Arrays with an abstract class type
as the component type are supported; the elements of such an array
may have as their value a null
reference or instances of any subclass of this abstract class that
is not itself abstract.
Array Variables [2.15.2]
A variable of array type holds a reference to an object. Declaring a
variable of array type does not
create an array object or allocate any space for array components.
It creates only the variable itself,
which can contain a reference to an array. Because an array's length
is not part of its type, a single
variable of array type may contain references to arrays of different
lengths. Once an array object is
created, its length never changes. To make an array variable refer
to an array of different length, a
reference to a different array must be assigned to the variable.
If an array variable v has type A[], where A is a reference type, then
v can hold a reference to any
array type B[], provided B can be assigned to A (§2.6.7).
Array Creation [2.15.3]
An array is created by an array creation expression or an array initializer.
Array Access [2.15.4]
A component of an array is accessed using an array access expression.
Arrays may be indexed by int
values; short, byte, or char values may also be
used as they are subjected to unary numeric promotion
(§2.6.10) and become
int values. All arrays are 0-origin. An array with length n can
be indexed by the
integers 0 through n - 1.
All array accesses are checked at run time; an attempt to use an index
that is less than zero or greater
than or equal to the length of the array causes an ArrayIndexOutOfBoundsException
to
be thrown.
Exceptions [2.16]
When a program violates the semantic constraints of the Java programming
language, the Java virtual
machine signals this error to the program as an exception ( i.e
an attempt to index outside the bounds
of an array). Java specifies that an exception will be thrown when
semantic constraints are violated and
will cause a nonlocal transfer of control from the point where the
exception occurred to a point that can
be specified by the programmer. An exception is said to be thrown from
the point where it occurred and
is said to be caught at the point to which control is transferred.
A method invocation that completes
because an exception causes transfer of control to a point outside
the method is said to complete abruptly.
Programs can also throw exceptions explicitly, using throw statements.
This is an alternative to returning
distinguished error values, such as the integer value -1, where a negative
value would not be expected.
Every exception is represented by an instance of the class Throwable
or one of its subclasses; such an
object can be used to carry information from the point at which an
exception occurs to the handler that
catches it. Handlers are established by catch clauses of try
statements.
During the process of throwing an
exception, the Java virtual machine abruptly completes in turn, any
expressions, statements, method
and constructor invocations, static initializers, and field initialization
expressions that have begun but not
completed execution in the current thread, continuing until a
handler is found that indicates that it handles
the thrown exception by naming the class of the exception or a superclass
of the class of the exception.
If no such handler is found, then the method uncaughtException
is invoked for the ThreadGroup that
is the parent of the current thread.
The Java exception mechanism is integrated with the synchronization
model (§2.19) so locks are properly
released as synchronized statements and invocations of synchronized
methods complete abruptly.
The specific exceptions covered in this section are that subset of
the predefined exceptions that can be
thrown directly by the operation of the Java virtual machine. Additional
exceptions can be thrown by class
library or user code; these exceptions are not covered here. See The
JavaTM Language Specification for
information on all predefined exceptions.
The Causes of Exceptions[2.16.1]
An exception is thrown when 1) An abnormal execution condition was synchronously
detected by the
Java virtual machine and thrown at the point where they were specified
as a possible result of an expression
evaluation or statement execution.(For example, an operation violates
Java semantics such as indexing
outside the bounds of an array or an error occurs in loading or linking
part of the program or some limit
on a resource is exceeded such as too much memory is used) 2) A throw
statement was executed. or
3) An asynchronous exception occurred because the stop
method of class Thread or ThreadGroup
was invoked, or an internal error occurred in the virtual machine implementation.
Exceptions are represented by instances of the class Throwable
and its subclasses, collectively, called
the exception classes.
Handling an Exception[2.16.2]
When an exception is thrown, control is transferred from the code that
caused the exception to the
nearest dynamically enclosing catch clause of a try statement
that handles the exception. A statement
or expression is dynamically enclosed by a catch clause if it appears
within the try block of the try
statement of which the catch clause is a part, or if the caller of
the statement or expression is dynamically
enclosed by the catch clause. If the call is made within a given method,
the statement caller is considered
to be the method invocation expression that was executed to cause the
given method to be invoked If
within a constructor or the initializer for an instance variable, then
the caller is the class instance creation
expression or the method invocation of newInstance that was executed
to cause an object to be created.
The catch clause handles the exception if the type of its parameter
is the class of the exception or a
superclass of the class of the exception. Equivalently, a catch clause
will catch any exception object
that is an instanceof the declared parameter type.
The control transfer that occurs when an exception is thrown causes
abrupt completion of expressions
and statements until a catch clause is encountered that can handle
the exception; execution then continues
by executing the block of that catch clause. The
code that caused the exception is never resumed.
If no catch clause handling an exception can
be found, then the current thread (the thread that encountered
the exception) is terminated, but only after
all finally clauses have been executed and the method
uncaughtException has been invoked
for the ThreadGroup that is the parent of the current thread.
In situations where it is desirable to ensure that one block of code
is always executed after another,
even if that other block of code completes abruptly, a try statement
with a finally clause may be used.
If a try or catch block in a try-finally
or try-catch-finally statement completes
abruptly, then the finally
clause is executed during propagation of the exception, even if no
matching catch clause is ultimately found.
If a finally clause is executed because of abrupt completion of a try
block and the finally clause itself
completes abruptly, then the reason for the abrupt completion of the
try block is discarded and the new
reason for abrupt completion is propagated from there.
Most exceptions occur synchronously as a result of an action by the
thread in which they occur and
at a point in the program that is specified to possibly result in such
an exception. An asynchronous
exception is, by contrast, an exception that can potentially occur
at any point in the execution of a
program. Asynchronous exceptions are rare. They occur only as a result
of an invocation of the stop
method of class Thread or ThreadGroupor
from an internal error in the Java virtual machine
implementation.
A stop method may be invoked by one thread to affect another thread
or all the threads in a specified
thread group. It is asynchronous because it may occur at any point
in the execution of the other thread
or threads. An internal error is considered asynchronous so that it
may be handled using the same
mechanism that handles the stop method, as will now be described.
The Java programming language permits a small but bounded amount of
execution to occur before an
asynchronous exception is thrown. This delay is permitted to allow
optimized code to detect and throw
these exceptions at points where it is practical to handle them while
obeying the semantics of the language.
A simple implementation might poll for asynchronous exceptions
at the point of each control transfer
instruction. Since a program has a finite size, this provides a bound
on the total delay in detecting an
asynchronous exception. Since no asynchronous exception will occur
between control transfers, the
code generator has some flexibility to reorder computation between
control transfers for greater
performance.
All exceptions in the Java programming language are precise: when the
transfer of control takes place,
all effects of the statements executed and expressions evaluated before
the point from which the exception
is thrown must appear to have taken place. No expressions, statements,
or parts thereof that occur after
the point from which the exception is thrown may appear to have been
evaluated. If optimized code has
speculatively executed some of the expressions or statements which
follow the point at which the exception
occurs, such code must be prepared to hide this speculative execution
from the user-visible state of the
program.
The Exception Hierarchy [2.16.3]
The possible exceptions in a program are organized in a hierarchy of
classes, rooted at class Throwable,
a direct subclass of Object. The
classes Exception and Error
are
direct subclasses of Throwable. The
class RuntimeException is a direct
subclass of Exception.
Programs can use the preexisting exception classes in throw statements,
or define additional exception
classes as subclasses of Throwable
or of any of its subclasses, as appropriate. To take advantage of
compile-time checking for exception handlers, it is typical to define
most new exception classes as
checked exception classes, specifically
as subclasses of Exception that are not subclasses of
RuntimeException.
The Classes Exception and RuntimeException [2.16.4]
The class Exception is the superclass
of all the standard exceptions that ordinary programs may wish
to recover from. The class RuntimeException
is a subclass of class Exception. The subclasses of
RuntimeException are unchecked exception classes. The package java.lang
defines
the following
standard unchecked runtime exceptions:
ArithmeticException | An exceptional arithmetic situation has arisen, such as an integer division or integer remainder operation with a zero divisor. |
ArrayStoreException | An attempt has been made to store into an array component a value whose class is not assignment compatible with the component type of the array |
ClassCastException | An attempt has been made to cast a reference to an object to an inappropriate type |
IllegalMonitorStateException | A thread has attempted to wait on or notify other threads waiting on an object that it has not locked. |
IndexOutOfBoundsException | Either an index of some sort (such as to an array, a string, or a vector) or a subrange, specified either by two index values or by an index and a length, was out of range |
NegativeArraySizeException | An attempt was made to create an array with a negative length |
NullPointerException | An attempt was made to use a null reference in a case where an object reference was required |
SecurityException | A security violation was detected. |
The class Error and its standard
subclasses are exceptions from which ordinary programs are not ordinarily
expected to recover. The class Error
is a separate subclass of Throwable,
distinct from Exception in the
class hierarchy, in order to allow programs to use the idiom
} catch (Exception e) {
to catch all exceptions from which recovery may be possible without
catching errors from which recovery
is typically not possible. Package java.lang
defines
all the error classes described here.
The Java virtual machine throws an object that is an instance of a subclass
of LinkageError when a
loading, (§2.17.2),
linking(§2.17.3),
or initialization(§2.17.4)
error occurs.
Process | associated errors | described |
loading | ClassFormatError,
ClassCircularityError, NoClassDefFoundError, UnsupportedClassVersionError |
in (§2.17.2) |
linking | NoSuchFieldError,
NoSuchMethodError, InstantiationError, IllegalAccessError |
in (§2.17.3) |
verification | VerifyError | in (§2.17.4) |
initialization | ExceptionInInitializerError* | in (§2.17.4) |
*A virtual machine will throw the error ExceptionInInitializerError
if execution of a static initializer or of an initializer
for a static field (§2.11) results
in an exception that is not an Error
or a subclass of Error.
A LinkageError may also be thrown
at run time:
An AbstractMethodError is thrown
at run time if an abstract method is invoked.
An UnsatisfiedLinkError is thrown
at run time if the Java virtual machine cannot find an
appropriate definition of a method declared to be native.
A Java virtual machine implementation throws an object that is an instance
of a subclass of the class
VirtualMachineError when an internal
error or resource limitation prevents it from implementing the
semantics of the Java programming language. This specification defines
the following virtual machine
errors:
InternalError | An internal error has occurred in the Java virtual machine
implementation because of
a fault in the software implementing the virtual machine, a fault in the underlying host system software, or a fault in the hardware. This error is delivered asynchronously when it is detected and may occur at any point in a program. |
OutOfMemoryError | The Java virtual machine implementation has run out of either virtual or physical memory, and the automatic storage manager was unable to reclaim enough memory to satisfy an object creation request. |
StackOverflowError | The Java virtual machine implementation has run out of
stack space for a thread, typically
because the thread is doing an unbounded number of recursive invocations as a result of a fault in the executing program |
UnknownError | An exception or error has occurred, but the Java virtual machine implementation is unable to report the actual exception or error. |
Execution [2.17]
This section specifies activities that occur during execution of a program.
It is organized around the
life cycle of the Java virtual machine and of the classes, interfaces,
and objects that form a program.
It specifies the detailed procedures used in starting up the virtual
machine (§2.17.1), class and interface
type loading (§2.17.2), linking (§2.17.3),
and initialization (§2.17.4). It then specifies
the procedures for
creation of new class instances (§2.17.6). It concludes by describing
the unloading of classes
(§2.17.8) and the procedure followed when
a virtual machine exits (§2.17.9).
Virtual Machine Start-up[2.17.1]
The Java virtual machine starts execution by invoking the method main
of some specified class and
passing it a single argument, which is an array of strings. This causes
the specified class to be loaded
(§2.17.2), linked
(§2.17.3)
to other types that it uses, and initialized(§2.17.4).
The method main must be
declared public, static, and void.
The manner in which the initial class is specified to the Java virtual
machine is beyond the scope of this
specification, but it is typical, in host environments that use command
lines, for the fully qualified name
of the class to be specified as a command-line argument and for subsequent
command-line arguments to
be used as strings to be provided as the argument to the method main.
For example, using Sun's Java 2
SDK for Solaris, the command line
java Terminator Hasta la vista Baby! // class Terminator isn't highlighted in the following ed.
will start a Java virtual machine by invoking the method main of class
Terminator
(a
class in an
unnamed package) and passing it an array containing the four strings
"Hasta", "la", "vista", and "Baby!".
We now outline the steps the virtual machine may take to execute Terminator,
as an example of the
loading, linking, and initialization processes that are described further
in later sections.
The initial attempt to execute the method main
of class Terminator discovers that
the class Terminator
is not loaded-that is, the virtual machine does not currently contain
a binary representation for this class.
The virtual machine then uses a ClassLoader(§2.17.2)
to attempt to find such a binary representation.
If this process fails, an error is thrown. This loading process is
described further in (§2.17.2). After
Terminator is loaded, it must be
initialized before main can be invoked, and a type (class or interface)
must always be linked before it is initialized. Linking (§2.17.3)
involves verification, preparation,
and
(optionally) resolution.
Verification (§2.17.3)
checks that the loaded representation of Terminator
is well formed, with a proper
symbol table. Verification also checks that the code that implements
Terminator
obeys the semantic
requirements of the Java virtual machine. If a problem is detected
during verification, an error is thrown.
Preparation (§2.17.3)
involves allocation of static storage and any data structures that are
used internally
by the virtual machine, such as method tables.
Resolution (§2.17.3)
is the process of checking symbolic references from class Terminator
to other
classes and interfaces, by loading the other classes and interfaces
that are mentioned and checking that
the references are correct.
The resolution step is optional at the time of initial linkage. An
implementation may resolve a symbolic
reference from a class or interface that is being linked very early,
even to the point of resolving all symbolic
references from the classes and interfaces that are further referenced,
recursively. (This resolution may
result in errors from further loading and linking steps.) This implementation
choice represents one extreme
and is similar to the kind of static linkage that has been done for
many years in simple implementations of
the C language.
An implementation may instead choose to resolve a symbolic reference
only when it is actually used;
consistent use of this strategy for all symbolic references would represent
the "laziest" form of resolution.
In this case, if Terminator had
several symbolic references to another class, the references might be
resolved one at a time or perhaps not at all, if these references were
never used during execution of the
program.
The only requirement regarding when resolution is performed is that
any errors detected during resolution
must be thrown at a point in the program where some action is taken
by the program that might, directly
or indirectly, require linkage to the class or interface involved in
the error. In the "static" example
implementation choice described earlier, loading and linking errors
could occur before the program
is executed if they involved a class or interface mentioned in the
class Terminator or any of the further,
recursively referenced classes and interfaces. In a system that implemented
the "laziest" resolution, these
errors would be thrown only when a symbolic reference was used.
In our running example, the virtual machine is still trying to execute
the method main of class Terminator.
This is permitted only if the class has been initialized (§2.17.4).
Initialization consists of execution
of any class variable initializers and static initializers
of the class
Terminator, in textual order.But
before Terminator can be initialized,
its direct superclass must be
initialized, as well as the direct superclass of its direct superclass,
and so on, recursively. In the simplest
case, Terminator has Object as
its implicit direct superclass; if class Object has not yet been initialized,
then it must be initialized before Terminator
is initialized.
If class Terminator has another
class Super as its superclass, then
Super must be initialized before
Terminator. This requires loading,
verifying, and preparing Super, if
this has not already been done, and,
depending on the implementation, may also involve resolving the symbolic
references from Super and so
on, recursively. Initialization may thus cause loading, linking, and
initialization errors, including such errors
involving other types.
Finally, after completion of the initialization for class
Terminator (during which other consequential
loading, linking, and initializing may have occurred), the method main
of
Terminator is invoked.
Loading [2.17.2]
Loading refers to the process of finding the binary form of a class
or interface type with a particular name,
perhaps by computing it on the fly, but more typically by retrieving
a binary representation previously
computed from source code by a compiler and constructing, from that
binary form, a Class object to
represent the class or interface. The binary format of a class or interface
is normally the class file format
(see Chapter 4, "The class File Format").
The loading process is implemented by the class ClassLoader
and
its subclasses. Different subclasses of
ClassLoader may implement different loading policies.
In particular, a class loader may cache binary
representations of classes and interfaces, prefetch them based on expected
usage, or load a group of
related classes together. These activities may not be completely transparent
to a running application if,
for example, a newly compiled version of a class is not found because
an older version is cached by a
class loader. It is the responsibility of a class loader, however,
to reflect loading errors only at points
in the program where they could have arisen without prefetching or
group loading.
If an error occurs during class loading, then an instance of one of
the following subclasses of class
LinkageError will be thrown at
any point in the program that (directly or indirectly) uses the type:
ClassFormatError | The binary data that purports to specify a requested compiled class or interface is malformed. |
UnsupportedClassVersionError | A class or interface could not
be loaded because it is represented using an
unsupported version of the class file format.3 |
ClassCircularityError | A class or interface could not be loaded because it would be its own superclass or superinterface (§2.13.2). |
NoClassDefFoundError | No definition for a requested class or interface could be found by the relevant class loader |
Linking: Verification, Preparation, and Resolution [2.17.3]
Linking is the process of taking
a binary form of a class or interface type and combining it into the runtime
state of the Java virtual machine, so that it can be executed. A class
or interface type is always loaded
before it is linked. Three different activities are involved in linking:
verification,
preparation, and
resolution of symbolic references.
The Java programming language allows an implementation flexibility
as to when linking activities (and,
because of recursion, loading) take place, provided that the semantics
of the language are respected, that
a class or interface is completely verified and prepared before it
is initialized, and that errors detected
during linkage are thrown at a point in the program where some action
is taken by the program that might
require linkage to the class or interface involved in the error.
For example, an implementation may choose to resolve each symbolic
reference in a class or interface
individually, only when it is used (lazy or late resolution), or to
resolve them all at once, for example, while
the class is being verified (static resolution). This means that the
resolution process may continue, in some
implementations, after a class or interface has been initialized.
Verification ensures that the binary
representation of a class or interface is structurally correct. For
example, it checks that every instruction has a valid operation code;
that every branch instruction
branches to the start of some other instruction, rather than into the
middle of an instruction; that every
method is provided with a structurally correct signature; and that
every instruction obeys the type discipline
of the Java programming language.
If an error occurs during verification, then an instance of the following
subclass of class LinkageError will
be thrown at the point in the program that caused the class to be verified:
VerifyError | The binary definition for a class or interface failed to pass a set of required checks to verify that it cannot violate the integrity of the Java virtual machine |
Preparation involves creating the
static
fields for a class or interface and initializing such fields to the
standard default values (§2.5.1). This does
not require the execution of any Java virtual machine code;
explicit initializers for static
fields are executed as part of initialization (§2.17.4),
not preparation.
Implementations of the Java virtual machine may precompute additional
data structures at preparation
time in order to make later operations on a class or interface more
efficient. One particularly useful data
structure is a "method table" or
other data structure that allows any method to be invoked on instances
of a class without requiring a search of superclasses at invocation
time.
The binary representation of a class or interface references other
classes and interfaces and their fields,
methods, and constructors symbolically, using the fully qualified names
(§2.7.5) of the other classes
and interfaces. For fields and methods these symbolic references include
the name of the class or
interface type that declares the field or method, as well as the name
of the field or method itself,
together with appropriate type information.
Before a symbolic reference can be used it must undergo resolution,
wherein a symbolic reference is
validated and, typically, replaced
with a direct reference that can be more efficiently processed if the
reference is used repeatedly.
If an error occurs during resolution, then an instance of one of the
following subclasses of class
IncompatibleClassChangeError, or
of some other subclass, or of IncompatibleClassChangeError
itself (which is a subclass of the class LinkageError)
may be thrown at any point in the program that
uses a symbolic reference to the type:
IllegalAccessError | A symbolic reference has been encountered that specifies
a use or assignment of a field, or
invocation of a method, or creation of an instance of a class to which the code containing the reference does not have access because the field or method was declared private, protected, or default access (not public), or because the class was not declared public. This can occur, for example, if a field that is originally declared public is changed to be private after another class that refers to the field has been compiled. |
InstantiationError | A symbolic reference has been encountered that is used in a class instance creation expression, but an instance cannot be created because the reference turns out to refer to an interface or to an abstract class. This can occur, for example, if a class that is originally not abstract is changed to be abstract after another class that refers to the class in question has been compiled. |
NoSuchFieldError | A symbolic reference has been encountered that refers to a specific field of a specific class or interface, but the class or interface does not declare a field of that name. This can occur, for example, if a field declaration was deleted from a class after another class that refers to the field was compiled. |
NoSuchMethodError | A symbolic reference has been encountered that refers to a specific method of a specific class or interface, but the class or interface does not declare a method of that name and signature. This can occur, for example, if a method declaration was deleted from a class after another class that refers to the method was compiled. |
Initialization [2.17.4]
Initialization of a class consists
of executing its static initializers(§2.11)
and the initializers for static
fields (§2.9.2) declared in the class. Initialization
of an interface consists of executing the initializers for fields
declared in the interface (§2.13.3.1).
Before a class or interface is initialized, its direct superclass must
be
initialized, but interfaces implemented by the class need not be initialized.
Similarly, the superinterfaces of
an interface need not be initialized before the interface is initialized.
A class or interface type T will be
initialized immediately before one of the following occurs: 1) T is
a class and an instance of T is created.
2) T is a class and a static method of T is invoked. 3) A nonconstant
static field of T is used or assigned.
(A constant field is one that is (explicitly
or implicitly) both final and static, and that is initialized with the
value of a compile-time constant expression.
A reference to such a field must be resolved at compile time
to a copy of the compile-time constant value,
so uses of such a field never cause initialization.)
Invocation of certain methods in library classes (§3.12)
also causes class or interface initialization. See the
Java 2 platform's class library specifications (i.e. class Class
and package java.lang.reflect) for
details.
The intent here is that a type have a set of initializers that put
it in a consistent state and that this state be
the first state that is observed by other classes. The static initializers
and class variable initializers are
executed in textual order and may not refer to class variables declared
in the class whose declarations
appear textually after the use, even though these class variables are
in scope. This restriction is designed
to detect, at compile time, most circular or otherwise malformed initializations.
Before a class or interface
is initialized its superclass is initialized, if it has not previously
been initialized.
Detailed Initialization Procedure[2.17.5]
Initialization of a class or interface requires careful synchronization,
since some other thread may be trying
to initialize the same class or interface at the same time. There is
also the possibility that initialization of a
class or interface may be requested recursively as part of the initialization
of that class or interface; for
example, a variable initializer in class A might invoke a method of
an unrelated class B, which might in
turn invoke a method of class A. The implementation of the Java virtual
machine is responsible for taking
care of synchronization and recursive initialization by using the following
procedure. It assumes that the
Class object has already been verified
and prepared and that the Class object
contains state that can
indicate one of four situations:
1) This Class object is verified and prepared but not initialized.
2) This Class object is being initialized by some particular thread
T.
3) This Class object is fully initialized and ready for use.
4) This Class object is in an erroneous state, perhaps because the
verification step failed or because
initialization was attempted and failed.
The procedure for initializing a class or interface is then as follows:
1) Synchronize on the Class object
that represents the class or interface to be initialized. This involves
waiting until the current thread can obtain the
lock for that object (§8.13).
2) If initialization by some other thread is in progress for the class
or interface, then wait on this Class
object (which temporarily releases the lock). When
the current thread awakens from the wait, repeat
this step.
3) If initialization is in progress for the class or interface by the
current thread, then this must be a recursive
request for initialization. Release the lock on
the Class object and complete normally.
4) If the class or interface has already been initialized, then no
further action is required. Release the
lock on the Class object and complete normally.
5) If the Class object is in an
erroneous state, then initialization is not possible. Release the lock
on the
Class object and throw a NoClassDefFoundError.
6) Otherwise, record the fact that initialization of the Class
object is now in progress by the current thread
and release the lock on the Class object.
7) Next, if the Class object represents a class rather than
an interface, and the direct superclass of this
class has not yet been initialized, then recursively
perform this entire procedure for the uninitialized
superclass. If the initialization of the direct
superclass completes abruptly because of a thrown exception,
then lock this Class object, label it erroneous,
notify all waiting threads, release the lock, and complete
abruptly, throwing the same exception that resulted
from the initializing the superclass.
8) Next, execute either the class variable initializers and static
initializers of the class or the field initializers
of the interface, in textual order, as though they
were a single block, except that final static variables
and fields of interfaces whose values are compile-time
constants are initialized first.
9) If the execution of the initializers completes normally, then lock
this Class object, label it fully initialized,
notify all waiting threads, release the lock, and
complete this procedure normally.
10) Otherwise, the initializers must have completed abruptly by throwing
some exception E. If the class of
E
is not Error or one of its subclasses,
then create a new instance of the class
ExceptionInInitializerError,
with E as the argument, and use this object in place of E in the following
step. But if a new instance of ExceptionInInitializerError
cannot be created because an
OutOfMemoryError
occurs,
then instead use an OutOfMemoryError
object in place of E in the
following step.
11) Lock the Class object, label it erroneous, notify all waiting threads,
release the lock, and complete
this procedure abruptly with reason
E or its replacement as determined in the previous step.
In some early implementations of the Java virtual machine, an exception
during class initialization was
ignored rather than allowing it to cause an ExceptionInInitializerError
as described here.
Creation of New Class Instances [2.17.6]
A new class instance is explicitly created when one of the following
situations occurs:
1) Evaluation of a class instance creation
expression creates a new instance of the class whose name
appears in the expression.
2) Invocation of the newInstance
method of class Class creates a new
instance of the class represented
by the Class
object for which the method was invoked.
A new class instance may be implicitly created in the following situations:
1) Loading of a class or interface that contains a String
literal may create a new String object
(§2.4.8)
to
represent that literal. This may not occur if the
a String object has already been created
to represent a
previous occurrence of that literal, or if the String.intern
method
has been invoked on a String
object
representing the same string as the literal.
2) Execution of a string concatenation operator that is not part of
a constant expression sometimes creates
a new String
object to represent the result. String concatenation operators may also
create temporary
wrapper objects
for a value of a primitive type (§2.4.1).
Each of these situations identifies a particular constructor to be called
with specified arguments (possibly
none) as part of the class instance creation process.
Whenever a new class instance is created, memory space is allocated
for it with room for all the instance
variables declared in the class type and all the instance variables
declared in each superclass of the class
type, including all the instance variables that may be hidden. If there
is not sufficient space available to
allocate memory for the object, then creation of the class instance
completes abruptly with an
OutOfMemoryError. Otherwise, all
the instance variables in the new object, including those declared
in superclasses, are initialized to their default values (§2.5.1).
Just before a reference to the newly created object is returned as
the result, the indicated constructor is
processed to initialize the new object using the following procedure:
1) Assign the arguments for the constructor to newly created parameter
variables for this constructor
invocation.
2) If this constructor begins with an explicit constructor invocation
of another constructor in the same class
(using this), then evaluate the arguments and process
that constructor invocation recursively using these
same five steps. If that constructor invocation
completes abruptly, then this procedure completes
abruptly for the same reason. Otherwise, continue
with step 5.
3) If this constructor does not begin with an explicit constructor
invocation of another constructor in the
same class (using this) and is in a
class other than Object, then this
constructor will begin with an
explicit or implicit invocation of a superclass
constructor (using super). Evaluate the arguments and
process that superclass constructor invocation
recursively using these same five steps. If that
constructor invocation completes abruptly,
then this procedure completes abruptly for the same
reason. Otherwise, continue with step 4.
4) Execute the instance variable initializers for this class,
assigning their values to the corresponding
instance variables, in the left-to-right order
in which they appear textually in the source code for the
class. If execution of any of these initializers
results in an exception, then no further initializers are
processed and this procedure completes abruptly
with that same exception. Otherwise, continue
with step 5. (In some early implementations,
the compiler incorrectly omitted the code to initialize a
field if the field initializer expression
was a constant expression whose value was equal to the default
initialization value for its type. This was
a bug.)
5) Execute the rest of the body of this constructor. If that execution
completes abruptly, then this procedure
completes abruptly for the same reason. Otherwise,
this procedure completes normally.
Unlike C++, the Java programming language does not specify altered rules
for method dispatch during the
creation of a new class instance. If methods
are invoked that are overridden in subclasses in the object
being initialized, then these overriding methods
are used, even before the new object is completely created.
Finalization of Class Instances[2.17.7]
The class Object has a protected
method called finalize; this method
can be overridden by other
classes. The particular definition of finalize
that can be invoked for an object is called the finalizer
of
that object. Before the storage for an object is reclaimed by the garbage
collector, the Java virtual
machine will invoke the finalizer of that object.
Finalizers provide a chance to free up resources (such as file descriptors
or operating system graphics
contexts) that cannot be freed automatically by an automatic storage
manager. In such situations,
simply reclaiming the memory used by an object would not guarantee
that the resources it held would
be reclaimed.
The Java programming language does not specify how soon a finalizer
will be invoked, except to say that
it will happen before the storage for the object is reused. Nor does
the language specify which thread
will invoke the finalizer for any given object. If an uncaught exception
is thrown during the finalization, the
exception is ignored and finalization of that object terminates.
The finalize method declared in
class Object takes no action. However,
the fact that class Object
declares a finalize method means
that the finalize method for any class
can always invoke the finalize
method for its superclass, which is usually good practice. (Unlike
constructors, finalizers do not
automatically invoke the finalizer for the superclass; such an invocation
must be coded explicitly.)
For efficiency, an implementation may keep track of classes that do
not override the finalize method of
class Object or that override it
in a trivial way, such as
protected void finalize( ) { super.finalize( ); }
We encourage implementations to treat such objects as having a finalizer
that is not overridden and to
finalize them more efficiently. The finalize method may be invoked
explicitly, just like any other method.
However, doing so does not have any effect on the object's eventual
automatic finalization. The Java virtual
machine imposes no ordering on finalize method calls. Finalizers may
be called in any order or even
concurrently.
As an example, if a circularly linked group of unfinalized objects
becomes unreachable, then all the objects
may become finalizable together. Eventually, the finalizers for these
objects may be invoked in any order
or even concurrently using multiple threads. If the automatic storage
manager later finds that the objects
are unreachable, then their storage can be reclaimed.
Unloading of Classes and Interfaces [2.17.8]
A class or interface may be unloaded if and only if its class loader
is unreachable. The bootstrap class
loader is always reachable; as
a result, system classes may never be unloaded.
Virtual Machine Exit [2.17.9]
The Java virtual machine terminates all its activity and exits when
one of two things happens:
1) All the threads that are not daemon threads(§2.19)
terminate.
2) Some thread invokes the exit method
of class Runtime or class System,
and the exit operation is
permitted by the security manager.
A program can specify that all finalizers that have not been automatically
invoked are to be run before the
virtual machine exits.This is done by invoking the method runFinalizersOnExit
of the class System with
the argument true.4 By default finalizers are not run on exit.
Once running finalizers on exit has been
enabled it may be disabled by invoking runFinalizersOnExit
with the argument false. An invocation of
the runFinalizersOnExit method
is permitted only if the caller is allowed to exit and is otherwise rejected
by the security manager.
FP-strict Expressions[2.18]
An expression is not FP-strict if and only if it is not a compile-time
constant expression and it does not
appear within any declaration that has the strictfp modifier.
The results of all FP-strict expressions must
be those predicted by IEEE 754 arithmetic on operands represented using
single and double formats.
Within an expression that is not FP-strict, some leeway is granted
for an implementation to use an
extended exponent range to represent intermediate results; the net
effect, roughly speaking, is that a
calculation might produce "the correct answer" in situations where
exclusive use of the float value set
or double value set might result in overflow or underflow.
The intent here is that a type have a set of initializers that put
it in a consistent state and that this state be
the first state that is observed by other classes. The static initializers
and class variable initializers are
executed in textual order and may not refer to class variables declared
in the class whose declarations
appear textually after the use, even though these class variables are
in scope. This restriction is designed
to detect, at compile time, most circular or otherwise malformed initializations.
Before a class or interface
is initialized its superclass is initialized, if it has not previously
been initialized.
Threads [2.19]
While most of the preceding discussion is concerned only with the behavior
of code as executed by a
single thread, the Java virtual machine can support many threads of
execution at once. These threads
independently execute code that operates on values and objects residing
in a shared main memory.
Threads may be supported by having many hardware processors, by time-slicing
a single hardware
processor, or by time-slicing many hardware processors.
Any thread may be marked as a daemon thread.
When code running in some thread creates a new
Thread object, that new thread
is initially marked as a daemon thread if and only if the creating thread
is a daemon thread. A program can change whether or not a particular
thread is a daemon thread by
calling the setDaemon method in
class Thread. The Java virtual machine
initially starts up with a single
nondaemon thread, which typically calls the method main of some class.
The virtual machine may also
create other daemon threads for internal purposes. The Java virtual
machine exits when all non-daemon
threads have terminated (§2.17.9).
By providing mechanisms for synchronizing the concurrent activity of
threads, the Java programming
language supports the coding of programs that, though concurrent, still
exhibit deterministic behavior.
To synchronize threads the language uses monitors,
a mechanism for allowing one thread at a time to
execute a region of code. The behavior of monitors is explained in
terms of locks. There is a lock
associated with each object.
The synchronized statement performs two special actions relevant
only to multithreaded operation:
1) After computing a reference to an object but before executing its
body, it locks a lock associated with
the object.
2) After execution of the body has completed, either normally or abruptly,
it unlocks that same lock. As
a convenience, a method may
be declared synchronized; such a method behaves as if its body were
contained in a synchronized
statement.
The methods wait,
notify, and
notifyAll of class Object
support an efficient transfer of control from
one thread to another. Rather than simply "spinning" (repeatedly locking
and unlocking an object to see
whether some internal state has changed), which consumes computational
effort, a thread can suspend
itself using wait until such time
as another thread awakens it using notify
or notifyAll. This is especially
appropriate in situations where threads have a
producer-consumer relationship (actively cooperating on
a common goal) rather than a mutual exclusion
relationship (trying to avoid conflicts while sharing a
common resource).
As a thread executes code, it carries out a sequence of actions. A
thread may use the value of a variable
or assign it a new value. (Other actions include arithmetic operations,
conditional tests, and method
invocations, but these do not involve variables directly.) If two or
more concurrent threads act on a
shared variable, there is a possibility that the actions on the variable
will produce timing-dependent results.
This dependence on timing is inherent in concurrent
programming and produces one of the few situations
where the result of a program is not determined
solely by The JavaTM Language Specification.
Each thread has a working memory, in which it
may keep copies of the values of variables from the main
memory that are shared between all threads.
To access a shared variable, a thread usually first obtains a
lock and flushes its working memory. This guarantees that shared values
will thereafter be loaded from
the shared main memory to the working memory of the thread. By unlocking
a lock, a thread guarantees
that the values held by the thread in its working memory will be written
back to the main memory.
The interaction of threads with the main memory, and thus with each
other, may be explained in terms of
certain low-level actions. There are rules about the order in which
these actions may occur. These rules
impose constraints on any implementation of the Java programming language.
A programmer may rely
on the rules to predict the possible behaviors of a concurrent program.
The rules do, however, intentionally
give the implementor certain freedoms. The intent is to permit certain
standard hardware and software
techniques that can greatly improve the speed and efficiency of concurrent
code.
Briefly put, the important consequences of the rules are the following:
1) Proper use of synchronization constructs will allow reliable transmission
of values or sets of values
from one thread to another through shared variables.
2) When a thread uses the value of a variable, the value it obtains
is in fact a value stored into the variable
by that thread or by some other thread. This
is true even if the program does not contain code for
proper synchronization. For example, if two
threads store references to different objects into the same
reference value, the variable will subsequently
contain a reference to one object or the other, not a
reference to some other object or a corrupted
reference value. (There is a special exception for long
and double values; see §8.4.)
3) In the absence of explicit synchronization, an implementation
is free to update the main memory in an
order that may be surprising. Therefore, the
programmer who prefers to avoid surprises should use
explicit synchronization.
The details of the interaction of threads with the main memory, and
thus with each other, are discussed in
detail in Chapter 8, "Threads and Locks."
2 Note that a local variable is not initialized on its creation and is considered to hold a value only once it is assigned (§2.5.1).
3 UnsupportedClassVersionError, a subclass of ClassFormatError,
was introduced in the Java 2 platform, v1.2, to
enable easy identification of a ClassFormatError caused
by an attempt to load a class represented using an unsupported
version of the class file format.
4 The method runFinalizersOnExit was first implemented
in JDK release 1.1 but has been deprecated in the Java 2
platform, v1.2.