So far, all of the variables used have been declared at the start of the main( ) method.
However, Java allows variables to be declared within any block. As explained in
Chapter 2, a block is begun with an opening curly brace and ended by a closing curly
brace. A block defines a scope. Thus, each time you start a new block, you are creating
a new scope. As you probably know from your previous programming experience, a
scope determines what objects are visible to other parts of your program. It also determines
the lifetime of those objects.
Most other computer languages define two general categories of scopes: global
and local. However, these traditional scopes do not fit well with Java’s strict, objectoriented
model. While it is possible to create what amounts to being a global scope,
it is by far the exception, not the rule. In Java, the two major scopes are those defined
by a class and those defined by a method. Even this distinction is somewhat artificial.
However, since the class scope has several unique properties and attributes that do not
apply to the scope defined by a method, this distinction makes some sense. Because of
the differences, a discussion of class scope (and variables declared within it) is deferred
until Chapter 6, when classes are described. For now, we will only examine the scopes
defined by or within a method.
The scope defined by a method begins with its opening curly brace. However, if
that method has parameters, they too are included within the method’s scope. Although
this book will look more closely at parameters in Chapter 5, for the sake of this discussion,
they work the same as any other method variable.
As a general rule, variables declared inside a scope are not visible (that is, accessible)
to code that is defined outside that scope. Thus, when you declare a variable within a
scope, you are localizing that variable and protecting it from unauthorized access and/or
modification. Indeed, the scope rules provide the foundation for encapsulation.
Scopes can be nested. For example, each time you create a block of code, you are
creating a new, nested scope. When this occurs, the outer scope encloses the inner scope.
This means that objects declared in the outer scope will be visible to code within the
inner scope. However, the reverse is not true. Objects declared within the inner scope
will not be visible outside it.
To understand the effect of nested scopes, consider the following program:
// Demonstrate block scope.
class Scope {
public static void main(String args[]) {
int x; // known to all code within main
x = 10;
if(x == 10) { // start new scope
int y = 20; // known only to this block
// x and y both known here.
System.out.println("x and y: " + x + " " + y);
x = y * 2;
}
// y = 100; // Error! y not known here
// x is still known here.
System.out.println("x is " + x);
}
}
As the comments indicate, the variable x is declared at the start of main( )’s scope and
is accessible to all subsequent code within main( ). Within the if block, y is declared.
Since a block defines a scope, y is only visible to other code within its block. This is
why outside of its block, the line y = 100; is commented out. If you remove the leading
comment symbol, a compile-time error will occur, because y is not visible outside of its
block. Within the if block, x can be used because code within a block (that is, a nested
scope) has access to variables declared by an enclosing scope.
Within a block, variables can be declared at any point, but are valid only after they
are declared. Thus, if you define a variable at the start of a method, it is available to all
of the code within that method. Conversely, if you declare a variable at the end of a
block, it is effectively useless, because no code will have access to it. For example, this
fragment is invalid because count cannot be used prior to its declaration:
// This fragment is wrong!
count = 100; // oops! cannot use count before it is declared!
int count;
Here is another important point to remember: variables are created when their
scope is entered, and destroyed when their scope is left. This means that a variable
will not hold its value once it has gone out of scope. Therefore, variables declared
within a method will not hold their values between calls to that method. Also, a
variable declared within a block will lose its value when the block is left. Thus, the
lifetime of a variable is confined to its scope.
If a variable declaration includes an initializer, then that variable will be
reinitialized each time the block in which it is declared is entered. For example,
consider the next program.
// Demonstrate lifetime of a variable.
class LifeTime {
public static void main(String args[]) {
int x;
for(x = 0; x < 3; x++) {
int y = -1; // y is initialized each time block is entered
System.out.println("y is: " + y); // this always prints -1
y = 100;
System.out.println("y is now: " + y);
}
}
}
The output generated by this program is shown here:
y is: -1
y is now: 100
y is: -1
y is now: 100
y is: -1
y is now: 100
As you can see, y is always reinitialized to –1 each time the inner for loop is
entered. Even though it is subsequently assigned the value 100, this value is lost.
One last point: Although blocks can be nested, you cannot declare a variable to have
the same name as one in an outer scope. In this regard, Java differs from C and C++.
Here is an example that tries to declare two separate variables with the same name. In
Java, this is illegal. In C/C++, it would be legal and the two bars would be separate.
// This program will not compile
class ScopeErr {
public static void main(String args[]) {
int bar = 1;
{ // creates a new scope
int bar = 2; // Compile-time error – bar already defined!
}
}
}