static Fields
8.3.1.1. static
Fields
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
is called an instance variable, and sometimes called a non-static
field. Whenever a new instance of a class is created (§12.5), a new variable associated with that instance is created for every instance variable declared in that class or any of its superclasses.
The declaration of a class variable introduces a static context (§8.1.3), which limits the use of constructs that refer to the current object. Notably, the keywords this
and super
are prohibited in a static context (§15.8.3, §15.11.2), as are unqualified references to instance variables, instance methods, and type parameters of lexically enclosing declarations (§6.5.5.1, §6.5.6.1, §15.12.3).
References to an instance variable from a static context or a nested class or interface are restricted, as specified in §6.5.6.1.
Example 8.3.1.1-1. static
Fields
class Point { int x, y, useCount; Point(int x, int y) { this.x = x; this.y = y; } static final 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); } }
This program prints:
(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.11), 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.21.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.
Example 8.3.1.1-2. Hiding of Class Variables
class Point { static int x = 2; } class Test extends Point { static double x = 4.7; public static void main(String[] args) { new Test().printX(); } void printX() { System.out.println(x + " " + super.x); } }
This program produces the output:
4.7 2
because the declaration of x
in class Test
hides the definition of x
in class Point
, so class Test
does not inherit the field x
from its superclass Point
. Within the declaration of class Test
, the simple name x
refers to the field declared within class Test
. Code in class Test
may refer to the field x
of class Point
as super.x
(or, because x
is static
, as Point.x
). If the declaration of Test.x
is deleted:
class Point { static int x = 2; } class Test extends Point { public static void main(String[] args) { new Test().printX(); } void printX() { System.out.println(x + " " + super.x); } }
then the field x
of class Point
is no longer hidden within class Test
; instead, the simple name x
now refers to the field Point.x
. Code in class Test
may still refer to that same field as super.x
. Therefore, the output from this variant program is:
2 2
Example 8.3.1.1-3. Hiding of Instance Variables
class Point { int x = 2; } class Test extends Point { double x = 4.7; void printBoth() { System.out.println(x + " " + super.x); } public static void main(String[] args) { Test sample = new Test(); sample.printBoth(); System.out.println(sample.x + " " + ((Point)sample).x); } }
This program produces the output:
4.7 2 4.7 2
because the declaration of x
in class Test
hides the definition of x
in class Point
, so class Test
does not inherit the field x
from its superclass Point
. It must be noted, however, that while the field x
of class Point
is not inherited by class Test
, it is nevertheless implemented by instances of class Test
. In other words, every instance of class Test
contains two fields, one of type int
and one of type double
. Both fields bear the name x
, but within the declaration of class Test
, the simple name x
always refers to the field declared within class Test
. Code in instance methods of class Test
may refer to the instance variable x
of class Point
as super.x
.
Code that uses a field access expression to access field x
will access the field named x
in the class indicated by the type of reference expression. Thus, the expression sample.x
accesses a double
value, the instance variable declared in class Test
, because the type of the variable sample
is Test
, but the expression ((Point)sample).x
accesses an int
value, the instance variable declared in class Point
, because of the cast to type Point
.
If the declaration of x
is deleted from class Test
, as in the program:
class Point { static int x = 2; } class Test extends Point { void printBoth() { System.out.println(x + " " + super.x); } public static void main(String[] args) { Test sample = new Test(); sample.printBoth(); System.out.println(sample.x + " " + ((Point)sample).x); } }
then the field x
of class Point
is no longer hidden within class Test
. Within instance methods in the declaration of class Test
, the simple name x
now refers to the field declared within class Point
. Code in class Test
may still refer to that same field as super.x
. The expression sample.x
still refers to the field x
within type Test
, but that field is now an inherited field, and so refers to the field x
declared in class Point
. The output from this variant program is:
2 2 2 2
参考:https://docs.oracle.com/javase/specs/index.html
https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.3.1.1
####################################