Variables-Defining State

   

Variables ”Defining State

Obviously, variables are an integral part of programs and, thus, classes as well. In Chapter 3, you examined the various types of variables, but now you must also consider how they are employed in your programs and the different roles they can assume.

When creating variables, whether they are as simple as integers or as complex as derived classes, you must consider how they will be used, what code will require access to the variables, and what degree of protection you want to provide to these variables.

The capability to access a given variable is dependent on two things: the access specifier used when creating the variable and the location of the variable declaration within a class.

See "Literals: Assigning Values,"

Fields Versus Method Variables

Everything in Java is declared within a class, but variables within a class fall into two categories based on their scope. A class definition can include variables that belong to the class itself and also variables that belong to specific methods .

Those variables declared outside of any methods, but within a given class (usually immediately after the class declaration and before any methods), are referred to as fields of the class. Fields are accessible within all methods of the class. Non-private fields are also accessible to other classes.

You can also declare variables within a method. These variables are local to the method and can only be accessed within that method and not by any other class. Consequently, there is no need for access specifiers when declaring a method variable, so they are not allowed by the compiler.

Although it is possible to make every field accessible to every class, this is not a prudent practice. First of all, you would be defeating a great deal of the purpose of creating your program from classes. You choose appropriate class names instead of class1, class2, class3, and so on to create a clean program that is easy to code, follow, and debug. For the same reason, by creating various levels of protection, you encapsulate your code into self- sufficient and more logical chunks .

Furthermore, because OOP encourages and facilitates the reuse of code that you have written beforehand, careful assignment of access restrictions to code you write now prevents you from later doing something that you shouldn't.

(Keep in mind that preventing access to a field does not prevent the use of it.) For example, if you were creating a Circle class, there would most likely be several fields that would keep track of the properties of the class, such as radius, area, border color , and so on ”many of which might be dependent on each other. Although it might seem logical to make the radius field public (accessible by all other classes), consider what would happen if a few weeks later you decided to write the code shown in Listing 7.2.

Listing 7.2 Circle.java ” Code Fragment Showing Direct Access to a Field
 import java.awt.*; public class Circle {   public int radius, area;   public Color borderColor; ... } class GraphicalInterface {   Circle ball;   ...   void animateBall() {     for (int updateRadius = 0; updateRadius <= 10; updateRadius++){       ball.radius = updateRadius;       paintBall(ball.area, ball.borderColor);       ...     }   }   void paintBall(int area,Color color){     ...   } } 

This code would not produce the desired result. Although the

 ball.radius = updateRadius; 

statement would change the radius, it would not affect the area field (remember that the area of a circle is a function of the radius and is equal to p times the radius squared). As a result, you would be supplying the paintBall() method with incorrect information.

Because the area field depends on the radius, it should be updated whenever the radius is changed. Your first thought might be to ask why area is a field at all instead of just being the return value of an area() method that is computed based on the radius when needed. Using a field that is derived from one or more other fields is a common approach when addressing performance. Assume, in this case, that performance is critical when the area of the circle is needed. Calling a method that computes the area each time is extra overhead if the radius has not changed since the last call. Declaring a field to hold the area offers a better solution when the radius and area fields are properly protected as shown in Listing 7.3.

Listing 7.3 Circle.java ” Providing Access to the Circle Fields Through Methods
 import java.awt.Color; class Circle {   public Color borderColor;   private int radius;   private double area;   public void setRadius (int rad){     if ( rad != radius ) {       radius = rad;       area = 2.0 * Math.PI * radius;     }   }   public int getRadius(){     return radius;   }   public double area (){     return area;   } } class GraphicalInterface {   Circle ball; //   ...   void animateBall() {     for (int updateRadius = 0; updateRadius <= 10; updateRadius++){       ball.setRadius (updateRadius);       paintBall(ball.area(), ball.borderColor);       ...     }   } } 

As shown in the preceding listing, the area and radius fields cannot be directly manipulated from outside the class because they have private access. When the radius is changed through a call to setRadius(), which is the only way it can be changed from outside the class, the area field is updated accordingly . This way the area of the circle is always correctly represented by the field and is never computed unnecessarily.

Although it is important to consider the level of access that other objects will have to your fields, it is also important to consider the scope of the fields and method variables within your class. Scope describes the part of a program within which a variable can be referred to using its simple name . In general, every variable is within scope only within the block (delimited by the curly braces { and } ) in which it is declared. However, there are some slight exceptions to this rule. Examine Listing 7.4.

Listing 7.4 CashRegister.java ” Variables Have Scope Based on Where They Are Declared
 public class CashRegister {   public int total;   int salesValue[];   Outputlog log;   void printReceipt(int totalSale) {     Tape.println("Total Sale = $"+ totalSale);     Tape.println("Thank you for shopping with us.");   }   void sellItem(int value) {     log.sale(value);     total += value;   }   int totalSales() {     int numSales, total = 0;     numSales = log.countSales();     for (int i = 1; i <= numSales; i++) {       total += salesValue[i];     }     return total;   } } 

Now examine some of the variables and their scope:

Variable Name Declared As Scope
total Field of CashRegister class Entire class
total Local to totalSales() method Within totalSales()
log Field of CashRegister class Entire class
value Parameter to sellItem() Within sellItem()
i Local to totalSales() within for loop Within the for loop

There are several things to note from the table. Start with the simplest variable, log. log is a field of the CashRegister class and is, therefore, visible throughout the entire class.

Every method in the class (as well as other classes in the same package) can access log. The method parameter value is local to the method sellItem() in which it is declared. All statements in sellItem() can access value but it can't be accessed by any other methods. The variable i is similar, but has a different scope based on where it is declared within the method that contains it. Like log and value that exist only within the block in which they are defined, i exists only within the for statement in which it is defined. In fact, if you consider a complex for loop like that shown in the following example, i is recreated (in this case, 10 times):

 for (int x = 0; x<10 ;x++){   for (int i =0;i < numSales; i++ )     ... } 

To understand why this is the case, consider how this code might look if you "unwound it" into a while loop as in the following:

 {   int x = 0; //declare x and set its initial value   while (x <10) {     {  //start the next for loop        int i = 0; //declare i and set its initial value        while (i < numSales) {          ... //do whatever is in the inner for loop          i++; //perform the increment of i        }     }     x++; //increment x   } } 

As you can see, even though the for loop looks simple, the scope of the variables is actually complicated if you add all the implied braces.

The remainder of the scope table is complicated by the presence of two total variables with overlapping scope. Although the total field is accessible to all methods, the use of total might seem confusing in the totalSales() method. In Java, a locally declared variable takes precedence over a field relative to scope resolution. This is known as class variable hiding.

So in this case, the identifier total in totalSales() refers only to the local variable and not to the field. This means that after exiting the totalSales() method, the total class variable is unchanged. In such a situation, you can access the class variable by using the this keyword that is discussed later in more detail. For now, know that you could set the class variable total to the value of the local variable total with this statement:

 this.total = total; 

Although using the same identifier as both a field and a method variable name is supported, it is confusing to other programmers working with your code and is arguably error prone. It is preferable to choose a different (and more descriptive) identifier. The local variable in totalSales() could just as easily been named totalSales and any confusion would have been avoided.

Access Specifiers

Similar to class and method declarations, access specifiers for field declarations determine how accessible variables are to other classes. It is important to remember that access specifiers for variables apply only to fields. It makes little sense to speak of access specifiers for variables within methods because they exist only while the method is executing. Afterwards, they are collected to free up memory for other variables.

Why Not Make All Local Variables Fields?

Given that all class variables (fields) are accessible to all methods in a class, why not make all variables within a class fields?

The first reason is that you would be wasting a great deal of memory. Although local variables (those variables declared within the methods themselves ) exist only while the method is executing, fields must exist for the lifetime of a class instance. Consequently, allocating memory for variables needed only by individual methods at the class level is a poor use of resources.

The second reason is that declaring all your variables as fields would make your class declarations cumbersome and hard to follow. If you are going to be using a counter only in one method, why not declare it in that method? Matching a variable's declaration to its logical scope makes the intent of that variable's use much more clear. Using local variables within methods is a way to encapsulate information. There is no reason to expose the details of a method's implementation to the rest of the class.

Default

If no access specifier is included in a field declaration, the field is assigned a default level of access. This default level makes fields accessible within the current class and to classes within the same package. No access is granted to classes outside the package, including subclasses. An example default access declaration follows :

 int size; 
public

Identical to the public access specifier for methods, the public specifier makes fields visible to all classes, regardless of their package. For example:

 public int size; 

You should make an effort to limit public fields and provide methods for controlled manipulation instead.

protected

The protected specifier is an extension of the default access level that differs only in how subclasses are treated. Protected fields can be accessed by all classes within the same package and by all subclasses, including those declared in other packages. For example:

 protected int size; 
private

The highest degree of protection is achieved with the private specifier. Private fields are only accessible to methods within the current class. They are not accessible to any other classes, including subclasses of the current class. For example:

 private int size; 

Modifiers

Field declarations can also include modifiers that affect their use. These optional modifiers are static, final, transient, and volatile.

static

As with methods, placing the static modifier in front of a field declaration makes the field static. Static fields are shared by all instances of a class rather than having an individual copy allocated to each instance. Consequently, changing a static field affects the state of that field seen by all instances of a class. Static fields can be modified in both static and non-static methods. For example:

 static int size; 

See Chapter 4, "Methods and Exceptions,"

final

The final modifier tells the compiler that the value of a variable can be assigned only once, making it a constant. Typically, you will assign the value of a final variable as part of the declaration. For example:

 final int MAX_LOGIN_FAILURES = 3; 

Java also supports the concept of a blank final where a final variable is declared without a value assignment and is initialized later before its first use.

transient

It is common for the programs you write to require that certain information be persisted , or saved, for later use by another program or another execution of the same program. This persistence can be accomplished in several ways, including writing to a database or making use of Java's support for the serialization of objects as discussed in Chapter 22, "Object Serialization." Regardless of the method chosen , persistence of a class instance is accomplished by saving the states of the class fields so they can be later retrieved and used to create an equivalent instance. However, it is possible that not all the fields need to be saved to accomplish this. Remember the earlier example of a Circle class with radius and area fields. Here the value of the area field is derived from the radius and only exists as a separate field for performance purposes. Saving the value of the area field when a Circle instance is persisted is unnecessary overhead because it isn't needed to restore the instance. Instead, the area field can be declared as transient. The transient modifier lets the compiler know that a given field does not need to be persisted. For example:

 private transient double area; 
volatile

The volatile modifier indicates that multiple threads can modify a variable and that certain compiler optimizations should be prevented to ensure that changes to the variable are handled properly.

Instance Fields

This chapter introduced the term "field" when referring to a variable defined within a class. You should also be familiar with the term instance field, which is used to describe a non-static field of a class. Instance field is meant to imply that a field belongs to a single instance of a class. When an instance field is modified, the change in state is seen only by the associated class instance. Because instance fields exist only within the context of a class instance, they cannot be accessed by static class methods.

Note

A common error is to attempt to access an instance field within the main() method of a class. Although the main() method is a special case because of the entry point it provides for an application, it is nonetheless a static method. It is typical for a main() method to create an instance of its enclosing class and then use the fields and methods of that instance to perform its work.


Initialization

You can explicitly initialize an instance field as part of its declaration or it will be assigned a default value automatically. If you do not specify an initial value, reference types are set to null and primitives are initialized to zero or false, as appropriate. If you declare a field with an initialization statement, that statement is executed after the superclass constructor is called for the object but before its own constructor. Initialization statements can include method calls but only if the call cannot throw an exception that must be handled.

You also have the option of using an instance initializer block to initialize instance fields. This is a block of code enclosed in curly braces that can appear anywhere in a class declaration that a field declaration can appear. The code in an instance initializer is executed whenever any constructor in the class is called just as if it had been included at the beginning of each constructor. This mechanism is useful when you need to perform an initialization that is too complex to be done as part of a field declaration. Rather than placing this code in every constructor (or a method called by every constructor), you can place an instance initializer block next to the fields it initializes. An example initializer block is shown in the following code segment that initializes an array with 100 random numbers :

 ...   private double[] randomValue = new double[100];   {     for (int i=0; i<randomValue.length; i++) {       randomValue[i] = Math.random();     }   } ... 

Class Fields

Class fields are the complement to instance fields. A class field is a field defined with the static modifier. As discussed previously when this modifier was introduced, static fields are shared by all instances of a class. A class field belongs to the class as a whole and not to a particular instance. A common example of a class field is a counter that keeps track of the number of instances of a class that have been instantiated . The class constructors each increment this field when called as a way to maintain information related to the class as a whole. A public class field is the closest Java gets to the concept of a global variable.

Given that class fields are not associated with a class instance, they cannot be initialized in a constructor or an instance initializer block. For most applications, you can initialize a class field as part of its declaration. You might also make use of one or more static initializer blocks within the class declaration as shown in the following example:

 ... static Circle specialCircle; ... static {   specialCircle = new Circle();   specialCircle.setRadius(3);   specialCircle.borderColor = Color.blue; } ... 

A static initializer block is enclosed in curly braces just like an instance initializer, but it is preceded by the static modifier. Because these initializers affect only static fields, they are executed once during class initialization rather than being executed as part of each constructor call.

Note

Because class fields are not associated with a particular class instance, it isn't necessary for a class instance to be created before the class fields can be accessed from other classes. The normal access rules apply, so this only works for non-private class fields.


Declaring a Constant

A constant is a field whose value cannot change during program execution. Although const is a reserved word in Java, it plays no part in the declaration of constants within your programs (actually it isn't used for anything). Also, unlike C and C++, the Java compiler does not use a preprocessor so there is no support for an equivalent to #define statements. Instead, constants in Java are class fields declared with both the static and final modifiers. As discussed previously, applying the final modifier to a field prevents its value from being changed. This alone is sufficient to create a constant, but when the declaration is at a class level instead of within a method, it is also standard practice to define the field as static. The value of a constant cannot change, so declaring it as static avoids the unnecessary overhead of maintaining a copy per class instance. Here's an example constant declaration:

 public static final int NUM_ADDRESS_LINES = 3; 

If the value of a constant cannot change, why not use the value itself within the program? The answer to this question is twofold:

  • Although you cannot change the value of a constant during execution, as a programmer, you might later realize that an assigned value was not as constant as you thought. If program requirements change and the value of a constant needs to be updated, changing the assignment statement in one place is far simpler than searching for all occurrences of a hard-coded value and changing each one.

  • By using constants, your code becomes cleaner and easier to follow. Although the literal "3" might make perfect sense to you when you first code a for loop, using NUM_ADDRESS_LINES instead increases the likelihood that the code will make sense to another developer or even you several months later.

Note

By convention, constants are named with all capital letters and underscores are used to separate multiple words.


Implementing an Enumerated Type

An enumerated type provides a way to restrict the values of a variable to a well-defined set. You can think of the values allowed by a particular enumerated type as a set of constants. Java provides no direct support for enumerated types, so you have to do some work to create your own using a class as shown in Listing 7.5.

Listing 7.5 StopLightColor.java ” A Class That Represents an Enumerated Type
 public class StopLightColor {   // declare static instances to represent all allowed states   public static final StopLightColor GREEN = new StopLightColor("Green");   public static final StopLightColor YELLOW = new StopLightColor("Yellow");   public static final StopLightColor RED = new StopLightColor("Red");   private String color;   // constructor is private to prevent any extraneous values   private StopLightColor(String colorName) {     color = colorName;   }   public String toString() {     return color;   } } 

Listing 7.5 shows a way to improve the previous example of a class that represents a stoplight. In the StopLight class, an integer field named greenYellowRed represented the state of the light with values between 0 and 2 used to indicate the current light color. Using an integer value is not intuitive in this case and requires careful coding to prevent the assignment of invalid values. A better approach is to use a reference to a StopLightColor instance to represent this state. There are several characteristics of StopLightColor to notice. First, three static instances of the class are defined as fields of the class itself. This might look strange but it is common practice to provide access to constant class instances using this approach. Second, notice that the constructor, which is a special method described in the next section, for the class is declared as private. A constructor is the method called when a class instance is created. Given that a private method can only be called by the class that defines it, this constructor definition makes it impossible to create any instances of StopLightColor other than those created within the class itself. Therefore, the only instances that can ever exist are the GREEN, YELLOW, and RED instances defined as public constants. If you were to now change the greenYellowRed field in StopLight from an int to a StopLightColor, you would prevent the possibility of an invalid state being assigned while making the class easier to understand as well.

   


Special Edition Using Java 2 Standard Edition
Special Edition Using Java 2, Standard Edition (Special Edition Using...)
ISBN: 0789724685
EAN: 2147483647
Year: 1999
Pages: 353

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net