Classes as Types
Classes have a dual role: they can contain static members and describe objects.
A Word With Two Jobs
A common first impression is that a Java class has only one job. Some learners think a class is a container that holds static variables and subroutines, while others think a class is a description of what objects look like. Both impressions are incomplete. In Java, a class can serve both purposes at once.
This dual role is useful, but it can also be confusing. The word class refers both to a container and to a description of object structure. In this article you will trace what happens when a class name is used as a type, what an object gets from its class, and how the static part of a class differs from the part that each object carries for itself.
Using a Class Name as a Type
A class name can be used as a type. When you declare a variable with a class name as its type, that variable is meant to hold objects of that class. The type tells the program what kind of object the variable can refer to, and therefore which variables and subroutines can be reached through it.
The diagram shows the key idea. The variable is declared with the class Book as its type, and it refers to a Book object. The variable does not contain the whole object's contents as a separate standalone thing. It is the handle through which the program reaches the object. Declaring the variable with a class type is what lets the program later use that object's variables and subroutines through the variable.
What an Object Gets From Its Class
The class defines an object's variables and subroutines. Each object created from the class has the variables and subroutines that the class describes, and each object can hold its own values for those variables. The class is the description; the object is one instance that follows that description.
- static int
totalBooksbelongs to the class itself - String title is a variable that each Book object has for itself
describe()is a subroutine that every Book object contains
This small class plays both roles described in the source. The static variable totalBooks is part of the class as a container. The variable title and the subroutine describe are the description of what every Book object contains. Notice that the class does not say what title is for any particular book. That is left for each object to decide when it is created.
Shared Structure Versus Individual Values
When several objects come from the same class, they share the same structure but not necessarily the same values. The class is the single blueprint: the names of the variables and the subroutines are defined once. Each object then keeps its own values for its instance variables. Two Book objects both have a title variable and a describe subroutine, but the first might hold Dune while the second holds Emma.
Tracing two Book objects
Create two Book objects from the class above and determine which parts are shared and which are individual.
- 1Create the first object
Book first = new
Book("Dune"); The constructor sets title to Dune for this object only. - 2Create the second object
Book second = new
Book("Emma"); The constructor sets title to Emma for this different object. - 3Check the individual values
first.title is Dune and second.title is Emma. Each object keeps its own title value.
- 4Check the shared structure
Both objects have the same title variable and the same describe subroutine, because both were built from the Book class description.
- 5Check the static value
totalBooksis not stored separately in each object. It is part of the class, so both constructor calls update the one shared count.
The structure (variable names and subroutines) is shared through the class. The values of title are individual to each object. The count totalBooks is shared at the class level.
Book: Dune
Book: Emma
2Static Members Belong to the Class Itself
A static variable or static subroutine belongs to the class itself rather than to any one object. This is the container side of the dual role. The class can hold these static members whether or not any objects have been created, and it can also serve as the template from which objects are created. The concept map below shows both roles side by side.
What do you think happens?
After the two Book objects from the earlier example are created, what does System.out.println(Book.totalBooks) print?
Reveal answer
2
Each call to the Book constructor adds one to the static totalBooks variable. Both objects were created from the same class, so the single class-level count ends at 2. The name is written as Book.totalBooks because the variable belongs to the class rather than to a particular object.
Uniform Use of Objects
Because a class defines the variables and subroutines its objects contain, objects that share a common characteristic can be used in the same way. The source illustrates this with the PrintStream example: different objects can share a common subroutine such as println. Code that calls println on one PrintStream object can be written the same way as code that calls it on another, because both objects come from a class that defines that subroutine.
The practical benefit is that code can work with objects through the shared description they all follow. You do not need to rewrite the calling code for each individual object, as long as the objects share the structure the code relies on.
Mistakes Beginners Make With Classes as Types
Common mistake Thinking a class has only one job
Why it failsThe source is explicit that a class can both contain static variables and subroutines and describe objects. Treating it as only one of these hides how the same class name is used in both ways.
Do this insteadKeep both roles in mind: static members belong to the class itself, and the class also describes what each object contains.
Common mistake Assuming each object has its own copy of a static variable
What it looks likestatic int totalBooks is updated by every Book constructor call, so it does not reset for each new object.
Why it failsA static variable belongs to the class, not to any one object, so all objects of that class refer to the same static value.
Do this insteadRead static members as class-level data and instance variables as per-object data.
Common mistake Expecting a variable declared with a class type to contain no link to an object
Why it failsA variable declared with a class as its type is used to hold objects of that class, and it is through that variable that the object's variables and subroutines are reached.
Do this insteadThink of the variable as the way the program reaches an object of that class, as shown in the first diagram.
Common mistake Confusing the variable name with the class name when calling a static member, such as writing a count through an object name instead of the class name
Trace the Count
Using the Book class from this article, add a third object with the title Ulysses. Before running anything, predict what Book.totalBooks prints after the third object is created, and what third.describe() prints.
Hints
- Each call to the Book constructor adds one to
totalBooks, and that variable belongs to the class. - The title of the third object is set only for that object, just as Dune and Emma were set for theirs.
describe()prints the title of the object it is called on.
Key Takeaways
- A Java class has a dual role: it can contain static variables and subroutines that belong to the class itself, and it can describe the objects created from it.
- A class name can be used as a type for variables that hold objects of that class.
- The class defines an object's variables and subroutines, while each object can hold its own values for those variables.
- Objects of the same class share the same structure, but their individual variable values can differ.
- Because objects that share a common characteristic share subroutines such as println in the
PrintStreamexample, they can be used in the same way by code.