Now that we know about classes and objects we know enough to talk about a few odds and ends that are important but donβt really fit in anywhere else.
To start with, weβll finally get to understand what the static keyword, which weβve been using without a lot of explanation, is actually for. Weβll learn about how the scope of variables controls where we can refer to what variables, about how to use the final modifier to make some variables immutable, and how to use the access modifiers public and private to control what code can access the variables and methods in our classes,
In SectionΒ 3.1Β Writing methods when we first learned to write methods we made all our methods static. Then in SectionΒ 8.4Β Instance methods we learned about instance methods. As you may have already figured out the important difference between static and instance methods is that instance methods must be invoked on a specific objectβan instance of the classβin order to be able to access the instance variables stored in that object.
Methods declared with the static modifier, on the other hand donβt have access to instance variables because they are not invoked on any particular object. They are also called class methods because we can think of them as belonging to the class as a whole, rather than to any specific instance of the class.
Within a class we can call static methods defined in that class by their simple name, even from instance methods. But code in static methods will not have access to this so wonβt be able to reference instance variables or call non-static methods without being given an instance to call them on.
To call static methods from a different class, we call them on the class itself, as we first saw in SectionΒ 3.3Β The Math class when we learned how to call the staticmethods in the Math class. Class methods are called using the class name followed by the dot (.) operator and the method name, example, Math.sqrt(25); calls the sqrt method in the Math class.
Note that the static method printStatic has errors in it because it cannot access the instance variables and methods. Fix printStatic by giving it a Person parameter and then accessing the instance variables and methods of that object. Then fix the method call to printStatic in main to pass in an object as an argument.
This example also shows how instance methods easily share data variables without the need for parameters. Whereas, static methods cannot directly access the instance methods. They are often used for utility methods, like Math operations, that do not need access to the instance variables of an object.
Like class methods, class variables are declared with the static modifier before the variable type. Class variables belong to the class as a whole rather than to a specific object so each static variable is shared by all instances of the class. Class variables that are declared public can be accessed by code outside of the class by using the class name and the dot operator. Math.PI is an example of such a variable. Within a class all code can access static variables by their simple name.
In the following class Person, there is a static variable called personCounter that is incremented each time the Person constructor is called to initialize a new Person object. The static method printCounter prints out its value. You can also watch how it works in the Java visualizer by clicking the CodeLens button below.
public class Temperature {
private double temperature;
public static double maxTemp = 0;
public Temperature(double t) {
temperature = t;
if (t > maxTemp) {
maxTemp = t;
}
}
public static void main(String[] args) {
Temperature t1 = new Temperature(75);
Temperature t2 = new Temperature(100);
Temperature t3 = new Temperature(65);
System.out.println("Max Temp: " + Temperature.maxTemp);
}
}
Max Temp: 0
maxTemp is changed in each call to the Temperature() constructor.
There is a compiler error because the static variable maxTemp cannot be used inside a non-static constructor.
Non-static methods and constructors can use any instance or static variables in the class.
Max Temp: 100
Yes, maxTemp is initialized to 0 and then changed to 75 and then 100 by the constructor calls.
Max Temp: 75
maxTemp will be changed to 100 by the second constructor call since 100 > 75.
Max Temp: 65
maxTemp will not be changed to 65 by the third constructor call because 67 is not > 100.
Subsection9.3.3Scope: class, method, and block level
The scope of a variable is defined the textual part of a program where a variable can be referred to. The scope of a variable is determined by where you declare the variable when you write your programs. Roughly speaking each set of curly braces ({}) encloses a scope. Thus scopes can be nested within each other.
The largest scope is called class scope and consists of the whole body of a class between the opening and closing {}βs of the class. So all static and non-static variables declared at the top-level of a class are in class scope and can be referred to any code anywhere else in the same class. (Note, however, that being in scope is not the only requirement; code in a static method canβt access an instance variable in the same class not because itβs not in scope but because instance variables canβt be accessed by static code.)
The next smaller scope, method scope, is defined by the body of a method or constructor. It includes the parameter declarations even though they are outside the curly braces the define the body of a method or constructor. This is why variables defined as method parameters or declared within the body of a method can only be referred to by code within that same method. Itβs also why two different methods can have parameters with the same name without conflict because each method defines its own scope.
Finally we have block scope which is defined by control constructs within methods and constructors that have their own bodies such as for and while loop and if statements. The curly braces that mark the bodies of while loops and if statements create a block scope in which new variables can be declared that can only be referred to within those curly braces. And for loops define a scope that includes both the body within the curly braces but also the initializer, condition, and updater in the loop header. This is why when we write a loop like this we can only refer to i within the loop:
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
// can't access i here, out of scope
Block scopes can be nested within each other. For instance in the nested loops below, the code of the inner loop can refer to the variable i defined in the outer loop but code outside the inner loop canβt refer to j.
for (int i = 0; i < 10; i++) {
for (int j = i; j < 10; j++) {
System.out.println("i: " + i + "; j: " + j);
}
// can't access j here, out of scope
}
// can't access i here, out of scope
The image below shows the three basic levels of scope.
Java inherits its basic scope model from Algol 60, invented in the 1960s. In most languages in that lineage variables in a nested scope with the same name as a variable defined in an enclosing scope shadow the variable, meaning that within the inner scope the name refers to the variable declared in the the inner scope and the variable from the outer scope is not accessible. This is sometimes convenient but can also lead to subtle bugs.
Javaβs designers, under the assumption that eliminating the bugs caused by accidentally shadowing variables is worth the slight inconvenience of having to pick all unique names for variables within a single method scope, disallowed shadowing of method and block scoped variables.
Java does allow locally scoped variables shadow to shadow class scoped variables which is why we can define constructors with parameter names that are the same as the names of instance variables and then use the this.x = x to set the instance variable to the value of a parameter with the same name.
Try the following code to see that you cannot access the variables outside of their scope levels in the toString() method. Explain to someone sitting next to you why you canβt access these. Try to fix the errors by either using variables that are in scope or moving the variable declarations so that the variables have larger scope.
In this example, the local variable is used instead of the instance variable of the same name. What will the code print out? Try it with the CodeLens button.
The keyword final is used for a few things in Java but the only one we need to know about is as a modifier in a variable declaration. When we include final before the type in a variable declaration it tells Java that we want the variable to be immutable. Thus if we declare a variable to be final and later write code that tries to assign a new value to the variable, the compiler will give us an error.
There are two main ways to use final variables. Perhaps the simplest is when we want to define a constant value in which case we usually combine it with static to make a static final variable. For instance the variable PI is a public static final variable in the Math class which means it can be referenced by code anywhere as Math.PI . And since itβs final we donβt have to worry about some code accidentally redefining \(Ο\text{.}\) Constants defined this way are typicall given all uppercase names.
The other main way to use final is when we are defining a class whose instances are immutable. If we declare all the classβs instance variables to be final then the instances will be immutable. As a convinence we can declare variables as final without providing an initial value as long as they are initialized in every constructor. For instance, if we wanted our Address class to have immutable instances we might write it like this:
class Address {
private final String street;
private final String city;
private final String state;
private final String zipcode;
public Address(String street, String city, String state, String zipcode) {
this.street = street;
this.city = city;
this.state = state;
this.zipcode = zipcode;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getState() {
return state;
}
public String getZipcode() {
return zipcode;
}
}
In a class like this, with no setters defined, as long as the instance variables are private, the instances would effectively be immutable even without the final modifiers because thereβs no code in the class that ever changes them and code outside the class canβt. But it can still be useful to mark variables as final both to make your intent clear (even to yourself) and also to get help from the compiler if you forget and write some code that tries to change the value of something thatβs not supposed to change.
Try the following code and notice the syntax error when we try to change the constant PI. Put the comment symbols // in front of that line to remove the error and run it again.
In ChapterΒ 8Β Classes we covered the main uses of the public and private accessibility modifiers. These modifiers are another way to control what code is allowed to use a variable or method. In particular, they control whether variables, constructors, and methods in one class can be accessed or invoked by code in other classes.
This is a bit different from the scope of variables because code in different classes are never in the same scope. This is about whether some code can use obj.foo or obj.bar() to access a variable named foo or to invoke a method named bar defined in whatever class obj is an instance of.
The short version of how to use these two modifiers, which are a good set of rules for basic Java programming and everything you need to know for the AP exam is:
The first rule is basically always true. It is far better to keep instance variables private and then write getters and setters as appropriate both to keep fine grained control over what code can modify instances of your class but also to not tie yourself down to a particular set of variables as the way you want to hold the data that makes up instances of a class.
The rule also applies to static variables unless they are also final in which case they can be made public if they are meant to defined a value that is useful outside of the class, like Math.PI.
Constructors and methods, however, require more judgement. Often as we break up big methods into smaller methods we will write methods that shouldnβt be used directly by code outside the class; those methods should be declared private so we know they are only used within the class. And sometimes, though less commonly, we will have constructors that are only meant to be used internally. Those constructors can also be made private. Though do note if a class doesnβt have anypublic constructors, there will be no way for any code outside the class to make instances of it.
Debug the following program that has scope violations. You may need to add methods or use methods that are in the class Fraction appropriately. Then, add comments that label the variable declarations as class, method, or block scope.
Subsection9.3.7Coding Challenge: Static Song and counter
In SectionΒ 3.1Β Writing methods, we wrote a class with methods to print out the song The Ants Go Marching. Hereβs the song with three verses, where the chorus is the last two lines of each verse:
Verse 1:
The ants go marching one by one, hurrah, hurrah
The ants go marching one by one, hurrah, hurrah
The ants go marching one by one
The little one stops to suck a thumb
And they all go marching down to the ground
To get out of the rain, BOOM! BOOM! BOOM! BOOM!
Verse 2:
The ants go marching two by two, hurrah, hurrah
The ants go marching two by two, hurrah, hurrah
The ants go marching two by two
The little one stops to tie a shoe
And they all go marching down to the ground
To get out of the rain, BOOM! BOOM! BOOM! BOOM!
Verse 3:
The ants go marching three by three, hurrah, hurrah
The ants go marching three by three, hurrah, hurrah
The ants go marching three by three
The little one stops to climb a tree
And they all go marching down to the ground
To get out of the rain, BOOM! BOOM! BOOM! BOOM!
Letβs create a class to print out the song with two methods chorus() and verse, where the verse takes two parameters for the numbers and the action. Notice that this is a class where there are no instance variables and we donβt really need to generate multiple objects. With students or pets, it makes sense to have multiple objects. With a class printing out a song, we can just make the methods static and have just one copy of them.
Create a class called Song with two static methods: chorus() and verse(). The chorus method prints out the last two lines of each verse. The verse method takes two parameters, a number and an action, and prints out the verse with the number and action.
Add a public static variable called numVerses to the class that keeps track of the number of verses. Increment this variable in the method verse and print it out at the beginning of the verse.
This class should print out the The Ants Go Marching Song using two static methods: chorus() and verse(). The chorus method should print out the last two lines of each verse. The verse method should take two parameters, a number and an action, and print out the verse with the number and action. Add a public static variable called numVerses that keeps track of the number of verses. Increment this variable in the method verse and print it out at the beginning of the verse.
(AP 3.7.A.1) Class methods cannot access or change the values of instance variables or call instance methods without being passed an instance of the class via a parameter.
(AP 3.7.B.1) Class variables belong to the class, with all objects of a class sharing a single copy of the class variable. Class variables are designated with the static keyword before the variable type.
(AP 3.7.B.2) Class variables that are designated public are accessed outside of the class by using the class name and the dot operator, since they are associated with a class, not objects of a class.
(AP 3.8.A.1) Local variables are variables declared in the headers or bodies of blocks of code. Local variables can only be accessed in the block in which they are declared.
(AP 3.8.A.1) Parameters to constructors or methods are also considered local variables. These variables may only be used within the constructor or method and cannot be declared to be public or private.
(AP 3.8.A.2) When there is a local variable or parameter with the same name as an instance variable, the variable name will refer to the local variable instead of the instance variable within the body of the constructor or method.
public class Party {
private int boxesOfFood;
private int numOfPeople;
public Party(int people, int foodBoxes) {
numOfPeople = people;
boxesOfFood = foodBoxes;
}
public void orderMoreFood(int additionalFoodBoxes) {
int updatedAmountOfFood = boxesOfFood + additionalFoodBoxes;
boxesOfFood = updatedAmountOfFood;
}
public void eatFoodBoxes(int eatenBoxes) {
boxesOfFood = updatedAmountOfFood - eatenBoxes;
}
}
The class is missing an accessor method.
There is a scope violation.
The instance variables boxesOfFood and numOfPeople should be designated public instead of private.
There is a scope violation. Instance variables are usually private.
The return type for the Party constructor is missing.
There is a scope violation. Constructors do not have return types.
The variable updatedAmountOfFood is not defined in eatFoodBoxes method.
There is a scope violation. The updatedAmountOfFood variable is a local variable in another method.
public class Movie {
private int currentPrice;
private int movieRating;
public Movie(int p, int r) {
currentPrice = p;
movieRating = r;
}
public int getCurrentPrice() {
int currentPrice = 16;
return currentPrice;
}
public void printPrice() {
System.out.println(getCurrentPrice());
}
}
Which of the following reasons explains why the printPrice method is βbrokenβ and only ever prints out a value of 16?
The private variables currentPrice and movieRating are not properly initialized.
The constructor will initialize them.
The private variables currentPrice and movieRating should have been declared public.
Instance variables should be private.
The printPrice method should have been declared as private.
Methods are usually public.
currentPrice is declared as a local variable in the getCurrentPrice method and set to 16, and will be used instead of the instance variable currentPrice.
Correct!
The currentPrice instance variable does not have a value.