FieldModifiers:
FieldModifier
FieldModifiersFieldModifier FieldModifier: one of
public protected private
final static transient volatile
The access modifiers public
, protected
, and private
are discussed in §6.6. A
compile-time error occurs if the same modifier appears more than once in a field
declaration, or if a field declaration has more than one of the access modifiers
public
, protected
, and private
. If two or more (distinct) field modifiers
appear in a field declaration, it is customary, though not required, that they appear
in the order consistent with that shown above in the production for FieldModifier.
If a field is declared static
, there exists exactly one incarnation of the field, no
matter how many instances (possibly zero) of the class may eventually be created.
A static
field, sometimes called a class variable, is incarnated when the class is
initialized (§12.4).
A field that is not declared static
(sometimes called a non-static
field) is called an instance variable. Whenever a new instance of a class is created, a new variable associated with that instance is created for every instance variable declared in that class or any of its superclasses.
class Point { int x, y, useCount; Point(int x, int y) { this.x = x; this.y = y; } final static Point origin = new Point(0, 0); }
class Test { public static void main(String[] args) { Point p = new Point(1,1); Point q = new Point(2,2); p.x = 3; p.y = 3; p.useCount++; p.origin.useCount++; System.out.println("(" + q.x + "," + q.y + ")"); System.out.println(q.useCount); System.out.println(q.origin == Point.origin); System.out.println(q.origin.useCount); } }
(2,2) 0 true 1
showing that changing the fields x
, y
, and useCount
of p
does not affect the fields
of q
, because these fields are instance variables in distinct objects. In this example,
the class variable origin
of the class Point
is referenced both using the class
name as a qualifier, in Point.origin
, and using variables of the class type in
field access expressions (§15.10), as in p.origin
and q.origin
. These two ways
of accessing the origin
class variable access the same object, evidenced by the
fact that the value of the reference equality expression (§15.20.3):
q.origin==Point.origin
is true
. Further evidence is that the incrementation:
p.origin.useCount++;
causes the value of q.origin.useCount to be 1
; this is so because p.origin
and
q.origin
refer to the same variable.
A field can be declared final
, in which case its declarator must include a variable
initializer or a compile-time error occurs. Both class and instance variables
(static
and non-static
fields) may be declared final
.
Any attempt to assign to a final
field results in a compile-time error. Therefore, once a final
field has been initialized, it always contains the same value. If a final
field holds a reference to an object, then the state of the object may be changed by operations on the object, but the field will always refer to the same object. This applies also to arrays, because arrays are objects; if a final
field holds a reference to an array, then the components of the array may be changed by operations on the array, but the field will always refer to the same array.
Declaring a field final
can serve as useful documentation that its value will not change, can help to avoid programming errors, and can make it easier for a compiler to generate efficient code.
class Point { int x, y; int useCount; Point(int x, int y) { this.x = x; this.y = y; } final static Point origin = new Point(0, 0); }
the class Point
declares a final
class variable origin
. The origin
variable
holds a reference to an object that is an instance of class Point
whose coordinates
are (0, 0). The value of the variable Point.origin
can never change, so it always
refers to the same Point
object, the one created by its initializer. However, an
operation on this Point
object might change its state-for example, modifying its
useCount
or even, misleadingly, its x
or y
coordinate.
Variables may be marked transient
to indicate that they are not part of the persistent state of an object. If an instance of the class Point
:
class Point { int x, y; transient float rho, theta; }
were saved to persistent storage by a system service, then only the fields x
and y
would be saved. This specification does not yet specify details of such services;
we intend to provide them in a future version of this specification.
As described in §17, the Java language allows threads that access shared variables to keep private working copies of the variables; this allows a more efficient implementation of multiple threads. These working copies need be reconciled with the master copies in the shared main memory only at prescribed synchronization points, namely when objects are locked or unlocked. As a rule, to ensure that shared variables are consistently and reliably updated, a thread should ensure that it has exclusive use of such variables by obtaining a lock that, conventionally, enforces mutual exclusion for those shared variables.
Java provides a second mechanism that is more convenient for some purposes: 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.
If, in the following example, one thread repeatedly calls the method one
(but no more than Integer.MAX_VALUE
(§20.7.2) times in all), and another thread repeatedly calls the method two
:
class Test {
static int i = 0, j = 0;
static void one() { i++; j++; } static void two() { System.out.println("i=" + i + " j=" + j); }
}
then method two
could occasionally print a value for j
that is greater than the
value of i
, because the example includes no synchronization and, under the rules
explained in §17, the shared values of i
and j
might be updated out of order.
One way to prevent this out-or-order behavior would be to declare methods one
and two
to be synchronized
(§8.4.3.5):
class Test {
static int i = 0, j = 0;
static synchronized void one() { i++; j++; } static synchronized void two() { System.out.println("i=" + i + " j=" + j); }
}
This prevents method one
and method two
from being executed concurrently, and
furthermore guarantees that the shared values of i
and j
are both updated before
method one
returns. Therefore method two
never observes a value for j
greater
than that for i
; indeed, it always observes the same value for i
and j
.
Another approach would be to declare i
and j
to be volatile
:
class Test {
static volatile int i = 0, j = 0;
static void one() { i++; j++; } static void two() { System.out.println("i=" + i + " j=" + j); }
}
This allows method one
and method two
to be executed concurrently, but guarantees that accesses to the shared values for i
and j
occur exactly as many times,
and in exactly the same order, as they appear to occur during execution of the program text by each thread. Therefore, method two
never observes a value for j
greater than that for i
, because each update to i
must be reflected in the shared
value for i
before the update to j
occurs. It is possible, however, that any given
invocation of method two
might observe a value for j
that is much greater than the
value observed for i
, because method one
might be executed many times between
the moment when method two
fetches the value of i
and the moment when
method two
fetches the value of j
.
See §17 for more discussion and examples.
A compile-time error occurs if a final
variable is also declared volatile
.