Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Fundamentals › Java

static

What is static, and what changes when you add it?

static is a keyword that you put in front of a field (a variable declared inside a class) or a method (this lab looks only at fields). A field without static is created separately inside each object, every time you create an object. If you add static, there is only one in the class instead of one per object, and all the objects of that class share that one. So you can use a static field without any object, by the class name, as in Counter.count. A field without static must belong to some object, so you cannot use it by the class name alone. In this lab you add and remove static in front of count and change how it is read, and the same code is compiled and run each time. You see what the compiler reports, what is printed, and where count lives.

Checked against real execution · Four combinations were compiled with javax.tools and run on JDK 21 and checked against the model. Where count lives was measured by running a source in which only the reading line was changed to a.count and b.count

Concepts in this lab

static
This keyword marks a member as belonging to the class, not to an instance. A static field exists only once per class and is shared by all instances, and a static method is called without creating an object. A static field whose value changes is effectively a global variable, so it easily causes problems when several threads access it at the same time.
Field
A field is a variable that is declared inside a class. In class Card { String owner; }, owner is a field of type String. A field without static is also called an instance variable, and one is created separately for each object whenever a new object of that class is created. So even objects created from the same class can have different values in their fields. If you do not give a value, the field gets a default value, and for a field of a reference type it is null.
Instance variables and static fields
These are the two kinds of field in a class. A field that is not declared static is an instance variable, and a separate variable for it is created inside the object every time you create an object of the class. A field declared static has exactly one variable however many objects you create (even if you create none), and all the objects of the class share that one. You can use a static field by the class name, as in Counter.count. Using an instance variable by the class name is a compile-time error, because you must say which object it belongs to. If you read a static field through an object, as in a.count, it still compiles, and what is read is the one variable in the class, not a variable inside the object.
Classes and objects
A class is a blueprint that states which fields (data) and methods (behavior) something has, and an object is the actual thing that is created in memory (on the heap) by using new with that blueprint. An object is also called an instance. You can create many objects from one class, and each object has its own field values. A variable holds not the object itself but a reference to where the object is.

Try these

Running with the default settings

  1. The compiler reads the source from the top. First it reads the declaration of count in the Counter class. static is in front of count, so count is decided to be a variable that exists once in the Counter class, not one that is created for each object.
  2. The compiler checks the line System.out.println(Counter.count); in main. Counter.count refers to count by the class name. count is static, so there is one in the Counter class, and you can refer to it without any object. This is not an error.
  3. Compilation succeeds and the Main class file that will run is created.
  4. You run it with java Main. main starts, and the first new Counter() creates a Counter object and puts it in the variable a. count is static, so no count variable is created inside this object. The Counter class has one count variable, and its value is 0.
  5. The second new Counter() creates one more Counter object and puts it in the variable b. There are two objects now, but no new count variable was created. There is still only one in the Counter class.
  6. The call a.up() runs. count = count + 1; in the up method reads count, adds 1, and stores it again. This count is the one variable in the Counter class, so its value goes from 0 to 1. If you read it through the object b, its value is 1 right now too.
  7. The call b.up() runs. It is the same count = count + 1; . The up of b also adds to the same one variable, so the value goes from 1 to 2. What b adds is on top of what a added.
  8. The line System.out.println(Counter.count); runs. Counter.count is the one count in the Counter class, and its value 2 is printed.

Result of this run

Code

class Counter {
    // static: there is only one in the Counter class, not one per object
    static int count = 0;
    void up() {
        count = count + 1;
    }
}

public class Main {
    public static void main(String[] args) {
        Counter a = new Counter();
        Counter b = new Counter();
        a.up();
        b.up();
        System.out.println(Counter.count);
    }
}

Assumptions of this model

The compiler and runtime are JDK 21.0.12.1 (javac 21). For all four combinations, the same source as the code on the screen (identical line by line) was compiled with javax.tools. The ones that compiled had their class files loaded by a new class loader and Main.main was run, and the results were checked against the standard output that was captured. What was checked is whether compilation succeeded, the diagnostic code of the first error (compiler.err.non-static.cant.be.ref, shown on the screen as the error description), the number of errors and the lines where they occurred, whether the Main class file was created, and the output when run. The "Where count lives" column was checked with values from a separately compiled and run source in which, with the same declaration, only the reading line was changed to print a.count and b.count (2 and 2 with static, 1 and 1 without). Even in the combination that gets a compile-time error, the source with only the reading line changed compiles and runs. The values right after a.up(), which the step sentences rely on (a.count and b.count are 1 and 1 with static, and 1 and 0 without), were also checked by a separate run. The documentation checked was the Java Language Specification (Java SE 21), opened directly. The rule that a static field has exactly one variable however many objects are created, and that a field that is not static gets a variable in each object every time one is created, follows section 8.3.1.1. The rule that when a static field is read through an object the expression is evaluated, the result is discarded, and the value of the field is used follows section 15.11.1. The rule that using a field that is not static by the class name is a compile-time error follows section 6.5.6.2. The kind of error, the number of errors, and the line where each occurs are not set by the specification. They were observed with this javac. When a static field is read through an object as in a.count, this javac gave no warning with the default settings, and with the -Xlint:all option it gave one warning on that line, "static variable should be qualified by type name, Counter, instead of by an expression". The warning is not set by the specification. It is the behavior of this javac. static can also be put on methods and initializer blocks. Inside a static method there is no target object, so you cannot use a field that is not static by its name alone, but this lab covered only static fields. It also did not cover when the value of a static field is set, or the problems when several threads change it together. The relation between static methods and members that are not static is covered in "In-depth lab: static and instance".

Previous lab: Constructors · Next lab: Inheritance

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these