Static Fields and
Methods
In all sample programs that you have seen, the
main
method is tagged with the
static
modifier. We are now ready to discuss the meaning of this modifier.
Static Fields
If you define a field as
static
, then there is only one such field per class. In contrast, each object has its own copy of all instance fields. For example, let's suppose we want to assign a unique identification number to each employee. We add an instance field
id
and a static field
nextId
to the
Employee
class:
class Employee
{
. . .
private int id;
private static int nextId = 1;
}
Now, every employee object has its own
id
field, but there is only one
nextId
field that is shared among all instances of the class. Let's put it another way. If there are one thousand objects of the
Employee
class, then there are one thousand instance fields
id
, one for each object. But there is a single static field
nextId
. Even if there are no employee objects, the static field
nextId
is present. It belongs to the class, not to any individual object.
In most object-oriented programming languages, static fields are called
class fields
. The
term
"static" is a meaningless holdover from C++.
|
Let's implement a simple method:
public void setId()
{
id = nextId;
nextId++;
}
Suppose you set the employee identification number for
harry
:
harry.setId();
Then the
id
field of
harry
is set, and the value of the static field
nextId
is incremented:
harry.
id = . . .;
Employee.
nextId++;
Constants
Static
variables
are quite rare. However, static constants are more common. For example, the
Math
class defines a static constant:
public class Math
{
. . .
public static final double PI = 3.14159265358979323846;
. . .
}
You can access this constant in your programs as
Math.PI
.
If the keyword
static
had been omitted, then
PI
would have been an instance field of the
Math
class. That is, you would need an object of the
Math
class to access
PI
, and every object would have its own copy of
PI
.
Another static constant that you have used many times is
System.out
. It is declared in the
System
class as:
public class System
{
. . .
public static final PrintStream out = . . .;
. . .
}
As we mentioned several times, it is never a good idea to have public fields because everyone can modify them. However, public constants (that is,
final
fields) are ok. Since
out
has been declared as
final
, you cannot
reassign
another print stream to it:
out = new PrintStream(. . .); // ERROR--out is final
If you look at the
System
class, you will notice a method
setOut
that lets you set
System.out
to a different stream. You may
wonder
how that method can change the value of a
final
variable. However, the
setOut
method is a
native
method, not implemented in the Java programming language. Native methods can bypass the access control mechanisms of the Java language. This is a very unusual workaround that you should not emulate in your own programs.
|
Static Methods
Static methods are methods that do not
operate
on objects. For example, the
pow
method of the
Math
class is a static method. The expression:
Math.pow(x, y)
computes the power
x
y
. It does not use any
Math
object to carry out its task. In other words, it has no implicit parameter.
In other words, you can think of static methods as methods that don't have a
this
parameter.
Because static methods don't operate on objects, you cannot access instance fields from a static method. But static methods can access the static fields in their class. Here is an example of such a static method:
public static int getNextId()
{
return nextId; // returns static field
}
To call this method, you supply the
name
of the class:
int n = Employee.getNextId();
Could you have omitted the keyword
static
for this method? Yes, but then you would need to have an object reference of type
Employee
to invoke the method.
It is legal to use an object to call a static method. For example, if
harry
is an
Employee
object, then you can call
harry.getNextId()
instead of
Employee.getnextId()
. However, we find that notation confusing. The
getNextId
method doesn't look at
harry
at all to compute the result. We recommend that you use class
names
, not objects, to invoke static methods.
|
You use static methods in two situations:
-
When a method doesn't need to access the object state because all needed parameters are supplied as explicit parameters (example:
Math.pow
);
-
When a method only needs to access static fields of the class (example:
Employee.getNextId
).
Static fields and methods have the same functionality in Java and C++. However, the syntax is slightly different. In C++, you use the
::
operator to access a static field or method outside its scope, such as
Math::PI
.
The term "static" has a
curious
history. At first, the keyword
static
was introduced in C to denote local variables that don't go away when exiting a block. In that context, the term "static" makes sense: the variable stays around and is still there when the block is entered again. Then
static
got a second meaning in C, to denote global variables and functions that cannot be accessed from other files. The keyword
static
was simply reused, to avoid introducing a new keyword. Finally, C++ reused the keyword for a third, unrelated interpretation, to denote variables and functions that belong to a class but not to any particular object of the class. That is the same meaning that the keyword has in Java.
|
Factory Methods
Here is another common use for static methods. Consider the methods
NumberFormat.getNumberInstance()
NumberFormat.getCurrencyInstance()
that we discussed in Chapter 3. Each of these methods returns an object of type
NumberFormat
. For example,
NumberFormat formatter = NumberFormat.getCurrencyInstance();
System.out.println(formatter.format(salary));
// prints salary with currency symbol
As you now know, these are static methods—you call them on a class, not an object. However, their purpose is to generate an object of the same class. Such a method is called a
factory method
.
Why don't we use a constructor instead? There are two reasons. You can't give names to constructors. The constructor name is always the same as the class name. In the
NumberFormat
example, it makes sense to have two separate names for getting number and currency formatter objects. Furthermore, the factory method can return an object of the type
NumberFormat
, or an object of a subclass that inherits from
NumberFormat
. (See Chapter 5 for more on inheritance.) A constructor does not have that flexibility.
The
main
Method
Note that you can call static methods without having any objects. For example, you never construct any objects of the
Math
class to call
Math.pow
.
For the same reason, the
main
method is a static method.
public class Application
{
public static void main(String[] args)
{
// construct objects here
. . .
}
}
The
main
method does not operate on any objects. In fact, when a program starts, there aren't any objects yet. The static
main
method executes, and constructs the objects that the program needs.
Every class can have a
main
method. That is a handy trick for unit testing of classes. For example, you can add a
main
method to the
Employee
class:
class Employee
{
public Employee(String n, double s,
int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar
= new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
. . .
public static void main(String[] args) // unit test
{
Employee e = new Employee("Romeo", 50000);
e.raiseSalary(10);
System.out.println(e.getName() + " " + e.getSalary());
}
. . .
}
If you want to test the
Employee
class in isolation, you simply execute
java Employee
If the employee class is a part of a larger application, then you start the application with
java Application
and the
main
method of the
Employee
class is never executed.
|
The program in Example 4-3 contains a simple version of the
Employee
class with a static field
nextId
and a static method
getNextId
. We fill an array with three
Employee
objects and then print the employee information. Finally, we print the number of identification
numbers
assigned.
Note that the
Employee
class also has a static
main
method for unit testing. Try running both
java Employee
and
java StaticTest
to execute both
main
methods.
Example 4-3 StaticTest.java
1. public class StaticTest
2. {
3. public static void main(String[] args)
4. {
5. // fill the staff array with three Employee objects
6. Employee[] staff = new Employee[3];
7.
8. staff[0] = new Employee("Tom", 40000);
9. staff[1] = new Employee("Dick", 60000);
10. staff[2] = new Employee("Harry", 65000);
11.
12. // print out information about all Employee objects
13. for (int i = 0; i < staff.length; i++)
14. {
15. Employee e = staff[i];
16. e.setId();
17. System.out.println("name=" + e.getName()
18. + ",id=" + e.getId()
19. + ",salary=" + e.getSalary());
20. }
21.
22. int n = Employee.getNextId(); // calls static method
23. System.out.println("Next available id=" + n);
24. }
25. }
26.
27. class Employee
28. {
29. public Employee(String n, double s)
30. {
31. name = n;
32. salary = s;
33. id = 0;
34. }
35.
36. public String getName()
37. {
38. return name;
39. }
40.
41. public double getSalary()
42. {
43. return salary;
44. }
45.
46. public int getId()
47. {
48. return id;
49. }
50.
51. public void setId()
52. {
53. id = nextId; // set id to next available id
54. nextId++;
55. }
56.
57. public static int getNextId()
58. {
59. return nextId; // returns static field
60. }
61.
62. public static void main(String[] args) // unit test
63. {
64. Employee e = new Employee("Harry", 50000);
65. System.out.println(e.getName() + " " + e.getSalary());
66. }
67.
68. private String name;
69. private double salary;
70. private int id;
71. private static int nextId = 1;
72. }
|