Unit 7: Class Fields
Learning Objectives
After this unit, students should be able to:
- distinguish clearly between instance fields and class (static) fields, both conceptually and syntactically.
- explain when and why a field should be declared static, final, or both.
- define and access class fields correctly using class names.
- recognize common use cases of class fields (constants, shared configuration, precomputed values).
- refactor code to eliminate “magic numbers” using appropriate class fields.
Overview
In earlier units, we treated objects as the fundamental building blocks of a program, each with its own state. However, not all values naturally belong to individual objects. Some values, such as mathematical constants or configuration parameters, are shared universally and remain the same across all instances.
This unit introduces class (static) fields, which belong to a class rather than any specific object. You will learn how Java supports shared state through the static keyword, how this differs from instance fields, and how class fields are commonly used to define constants and utility values in well-designed programs.
Why Class Fields Exist
Let's revisit the following implementation of Circle.
| Circle v0.3 | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
In the code above, we use the constant \(\pi\) but hardcode it as 3.141592653589793. Hardcoding such a magic number is considered poor coding style. This constant can appear in more than one place. If we hardcode such a number and want to change its precision later, we would need to trace down and change every occurrence. Every time we need to use \(\pi\), we have to remember or look up what is the precision that we use. Not only does this practice introduce more work, it is also likely to introduce bugs.
In C, we define \(\pi\) as a macro constant M_PI. But how should we do this in Java? This is where the idea that a program consists of only objects with internal states that communicate with each other can feel a bit constraining. The constant \(\pi\) is universal and does not really belong to any object (the value of \(\pi\) is the same for every circle!).
Another example is the method sqrt(), which computes the square root of a given number. sqrt is a general function that is not associated with any object as well. We will study such class methods in the next unit; they follow the same class-level principle as class fields.
A solution to this is to associate these global values and functions with a class instead of with an object. For instance, Java predefines a java.lang.Math class1 that is populated with constants PI and E (for Euler's number \(e\)), along with a long list of mathematical functions. To associate a method or a field with a class in Java, we declare them with the static keyword. We can additionally add the keyword final to indicate that the value of the field will not change and public to indicate that the field is accessible from outside the class. In short, the combination of public static final modifiers is used for constant values in Java.
1 2 3 4 5 6 | |
We call static fields that are associated with a class as class fields and fields that are associated with an object as instance fields. Note that, a static class field needs not be final and it needs not be public. Class fields are useful for storing pre-computed values or configuration parameters associated with a class rather than individual objects.
Because it is associated with the class rather than an instance, a static field has exactly one shared instance during the entire execution of the program.
They introduce implicit shared state. Any method that modifies a class field affects all objects of that class.
Class fields are not a violation of object-oriented design. Instead, they acknowledge that some state conceptually belongs to a class as a whole, not to individual objects. If a field’s value would be identical across all instances, it is a strong candidate for being a class field.
Caution: because class fields introduce global shared state, they can make programs harder to reason about if overused.
Using Class Fields
A class field behaves just like a global variable and can be accessed in the code, anywhere the class can be accessed. Since a class field is associated with a class rather than an object, we access it through its class name.
1 2 3 | |
Below is an example of a non-constant class field. We introduce a class field circleCount to count how many Circle objects have been created so far.
| Circle v0.3b | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Here, there is only exactly one instance of circleCount regardless of how many instances of Circle we have created. In fact, we need not create any instance of Circle at all to be able to use circleCount.
| Demonstrating of shared class field | |
|---|---|
1 2 3 4 | |
In Java, it is fine to access a class field through an instance, but it is discouraged because it can be misleading. The following code is legal, but it is better to access circleCount through the class name Circle instead of the instance reference.
| Class method (invoking via instance) | |
|---|---|
1 2 3 | |
Class Fields and Methods in Python
Note that, in Python, any variable declared within a class block is a class field:
1 2 3 | |
In the above example, x and y are class fields, not instance fields.
-
The class
Mathis provided by the packagejava.langin Java. A package is simply a set of related classes (and interfaces, but I have not told you what is an interface yet). The packagejava.lang.Mathis automatically imported by the Java compiler. ↩