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.