stage direction


Before we can ask the Java interpreter to build really complex things for us we first have much to learn about its capabilities. We need to know what it already understands before we can help it to understand more.

So to learn more about our tools, let's build a lamp.

Every lamp must be able to at least turn on and turn off, and know whether it's on or off. To model the lamp's behavior we need methods to turn it on and off. To model the lamp's state (that of being on or off) we need a variable, say, lampIsOn, to remember its current state. Since this variable only needs two states it's reasonable to make it a boolean, with the understanding that when it's true, the lamp is on, and when it's false, the lamp is off.

The program is in two parts. First, here is a class defining what it means to be a lamp:


class Lamp
   {
   /*
   Define a lamp that can turn on and off.
   */

   //this lamp is either on or off
   boolean lampIsOn;

   void turnOn()
      {
      /*
      Turn this lamp on.
      */

      lampIsOn = true;
      }

   void turnOff()
      {
      /*
      Turn this lamp off.
      */

      lampIsOn = false;
      }
   }

And here is a class that uses such lamps:


import Lamp;

class FiddleWithLamps
   {
   /*
   Create some lamps and fiddle with them.
   */

   public static void main(String[] parameters)
      {
      Lamp kandinsky;
      kandinsky = new Lamp();
      kandinsky.turnOn();
      kandinsky.turnOff();
      kandinsky.lampIsOn = true;

      Lamp bauhaus;
      bauhaus = new Lamp();
      bauhaus.turnOn();
      bauhaus.turnOff();
      }
   }


 
Programs and Classes

Unlike our first program, FredsScript, this program has two classes, rather than only one. Further, class Lamp doesn't have a main() method, while class FiddleWithlamps does.

Every Java program we write must have at least one class where we declare a main() method as follows:


   public static void main(String[] parameters)

As we've seen, such a method is Act I, Scene I for the whole play. Not every class, however, needs a main() method. For example, if class A objects can create class B objects, and if class B objects can create class C objects, then neither class B nor class C needs a main() method. Class A may not need one either, if yet other classes of objects can create objects of its class.

Writing a Java program is like writing a play script then separating each actor's lines and stage directions into separate chunks. These chunks are Java classes; they become the actors' separate scripts. Each such script defines a role that an actor may portray, and we can create as many actors as we wish to play the same role. Each such actor will follow its script exactly.
 
Importing Classes

When defining a class that needs other classes to function, we should precede the class definition with a statement specifying any other class, or classes, the class needs. We signal that to the Java interpreter with the word import. Class FiddleWithLamps needs class Lamp to work properly, so we should import class Lamp, like so:


   import Lamp;

This statement alerts the Java interpreter that FiddleWithLamps uses Lamp objects. Such import statements must appear before the definition of the class that needs the classes we're importing.
 
Controlling Access

Class Lamp isn't a good model of a real lamp. For example, objects can turn a Lamp object on or off in two different ways. One object could ask a lamp to execute its turnOn() method, while another could set the object's lampIsOn variable to false. The first object thinks the lamp is on, the second thinks it's off. Which is right?

Altering the lamp's lampIsOn variable without going through either of the two methods seems like hotwiring a car rather than using its key. We can ensure that no non-Lamp object can turn a lamp on or off that way by making its lampIsOn variable private. While we're at it, let's make both Lamp methods public. This guarantees that any object that can create a lamp can also ask that lamp to execute either of its Lamp methods.


class Lamp
   {
   /*
   Define a lamp that can turn on and off.
   */

   //this lamp is either on or off
   private boolean lampIsOn;

   public void turnOn()
      {
      /*
      Turn this lamp on.
      */

      lampIsOn = true;
      }

   public void turnOff()
      {
      /*
      Turn this lamp off.
      */

      lampIsOn = false;
      }
   }

Objects of classes that use Lamp objects now cannot access a lamp's copy of its lampIsOn variable (it's private), but they can always ask the lamp to execute its turnOn() and turnOff methods (they're public):


import Lamp;

class FiddleWithLamps
   {
   /*
   Create some lamps and fiddle with them.
   */

   public static void main(String[] parameters)
      {
      Lamp kandinsky;
      kandinsky = new Lamp();
      kandinsky.turnOn();
      kandinsky.turnOff();
      //kandinsky.lampIsOn = true; //this is now illegal
      }
   }


 
Understanding Privacy in Java

It's tempting to think that making a prop private asks the stage manager to make each copy of the prop private to its own specific lamp. This is not so. It's true that the stage manager won't let any non-Lamp actor alter the prop, but it will still let every Lamp actor alter the prop. So, in terms of privacy, the best we can do is ask the stage manager to not let actors following other scripts alter a particular prop that we define in this script.

So the statement:


   private boolean lampIsOn;

declares that only Lamp objects can access the boolean variable lampIsOn.

We can also make methods private, and with the same effect. Normally, though, it's better to ask the stage manager to let any actor at all request a lamp to execute a particular method, whether the actor obeys the Lamp script or not. We do that using the word public, like so:


   public void turnOn()

It's good style to make all variables private and all methods public. Later, though, we'll discover some small modifications to that rule.
 
Reporting State

Now that we've made every copy of lampIsOn private, no non-Lamp object can find out the internal state of any lamp. The effect is to complete seal every lamp. Except for another lamp, the only object that can know whether a particular lamp is on or off is the same object that turned the lamp on or off in the first place.

If other objects need to know whether a particular lamp is on or off we need some way for a lamp to report its internal state. Let's add a new public method to class Lamp so that any object can query a lamp to find out whether it's currently on or off:


class Lamp
   {
   /*
   Define a lamp that can turn on and off
   and report its state.
   */

   //this lamp is either on or off
   private boolean lampIsOn;

   public void turnOn()
      {
      /*
      Turn this lamp on.
      */

      lampIsOn = true;
      }

   public void turnOff()
      {
      /*
      Turn this lamp off.
      */

      lampIsOn = false;
      }

   public boolean isOn()
      {
      /*
      Report whether this lamp is on or off.
      */

      return lampIsOn;
      }
   }

The new method, isOn(), returns a boolean value, so we must declare its return type as boolean, just as we had to declare the return type of divideApplesAmongPeople() as double. The other two methods don't return values, so we must declare them void, just as we declare main().
 
Encapsulating Lamps

It may seem silly to make the lampIsOn variable private then turn around and add a method to report its state, but doing it this way gives the lamp more control over who it reports its state to. When its variables aren't private, any object at all can come along and alter what should be its internal state.

By protecting its variables and providing well-defined access points to them only through methods, each object takes more control over its own state, and so makes it less likely that another object could incorrectly alter its state.

This encapsulation idea isn't that important when the program is very small, but it becomes more and more important as the program grows larger and more complicated. The more complex a program becomes, the more likely it is that someone, somewhere, will goof. The more protection we can put in to prevent that goof, the more likely it is that the goof won't harm the program's users.
 
Using boolean Expressions

So far, we've seen two if statements in FredsScript:


   if (numberOfPeople <= 0)
      return 0;

and

   if (fred.finishedProcessing == false)
      return;

The general form:


   if (booleanExpression)
      [some statement]

is all one statement, possibly spread over several lines, and with no separate semicolon to terminate it. It asks the Java interpreter to evaluate the booleanExpression, and if it is true then to execute the enclosed statement (the statement immediately following the if), otherwise to jump past the whole if statement and continue execution with the subsequent statement, if any.

We can also select which of two statements we execute next using a statement of the following form:


   if (booleanExpression)
      [some statement]
   else
      [some other statement]

This says that if the booleanExpression is true we execute the first enclosed statement next, otherwise we execute the second enclosed statement next.

Just as with the first kind of if statement, an if-else statement is all one statement even though it's continued over several lines and doesn't have its own separate semicolon to terminate it. As with class and method definitions, the Java interpreter can figure out where it ends for itself since it's all in one fixed format.

We can use the if-else statement to ask objects of the new version of class Lamp if they're on or not:


import Lamp;

class FiddleWithLamps
   {
   /*
   Create some lamps and fiddle with them.
   */

   public static void main(String[] parameters)
      {
      Lamp kandinsky;
      kandinsky = new Lamp();
      kandinsky.turnOn();
      kandinsky.turnOff();

      if (kandinsky.isOn() == true)
         System.out.println("kandinsky is on.");
      else
         System.out.println("kandinsky is off.");
      }
   }


 
Reporting State Changes

Our lamps now respond when other objects ask them their current state, but they don't tell anybody when they change state. One day, though, we might decide to display our lamps on the screen. If so, we'll need some way for them to signal the object displaying them that they are changing state. Let's add that capability next:


class Lamp
   {
   /*
   Define a lamp that can turn on and off,
   report its state, and report its state changes.
   */

   //this lamp is either on or off
   private boolean lampIsOn;

   public void turnOn()
      {
      /*
      Turn this lamp on.
      */

      lampIsOn = true;
      reportState();
      }

   public void turnOff()
      {
      /*
      Turn this lamp off.
      */

      lampIsOn = false;
      reportState();
      }

   public boolean isOn()
      {
      /*
      Report whether this lamp is on or off.
      */

      return lampIsOn;
      }

   public void reportState()
      {
      /*
      Report this lamp's status.
      */

      if (lampIsOn == true)
         System.out.println("I am on.");
      else
         System.out.println("I am off.");
      }
   }


 
Requesting Methods Within Methods

Having lamps report their state changes brings up a puzzle. After altering the lampIsOn variable, both of Lamp's new turnOn() and turnOff() methods now request execution of the reportState() method. As we've seen, however, we can only execute a method by asking an object of the class we define the method in to execute the method. So when an object is executing its turnOn() method, what object is it asking to execute the reportState() method?

The answer is the same object that is currently executing the turnOn() method. That object is asking itself to execute its own reportState() method.

The stage manager makes sure that each of our method execution requests is a request to some particular object to execute the method. If we don't specify an object to execute the method, the stage manager secretly puts a self-reference in front of the method execution request. That self-reference is the word "this". It's the same as saying that whoever is presently executing the method should also be the one to execute the new method request. Think of this as short for theObjectExecutingThisStatement.

There is no such thing as an actorless action in Java. An actor executes its scenes only when asked to by another actor (or by itself, or by the stage manager). So what looks like a bare request to execute some generic reportState() method is a normal method execution request to an object, just as we're used to so far. In other words, the statement:


   reportState();

is exactly the same as the statement:

   this.reportState();

An actor executing a scene containing this statement is asking itself to execute its reportState() scene next. The same is true for accessing variables.

Thus, the following two methods are exactly the same:

   public void turnOn()
      {
      /*
      Turn this lamp on.
      */

      lampIsOn = true;
      reportState();
      }


   public void turnOn()
      {
      /*
      Turn this lamp on.
      */

      this.lampIsOn = true;
      this.reportState();
      }

If kandinsky executes the turnOn() method, kandinsky sets its copy of the lampIsOn variable to true then it asks itself to execute its reportState() method. Similarly, if bauhaus executes the method, everything is in terms of bauhaus' variables and methods. In each case, this lets an object refer to itself.
 
Style Crime

Earlier we had two different ways to turn on a lamp---by asking the lamp to execute its turnOn() method or by going in an monkeying with the lamp's lampIsOn variable directly. Having two different ways to do anything is usually a crime against good style because, for one thing, it can lead to indeterminacy, as we saw earlier.

The reportState() method is another example of the same style crime. It tests the value of the lampIsOn variable to find out whether the lamp is on or not, while the isOn() method already returns the value of the lampIsOn variable.

Instead of having the object that's executing reportState() monkey with lampIsOn, we should have it execute isOn() to see if the lamp is on. That makes isOn() the sole access point for any object, including the lamp itself, to find out if it's on or not. If we then decide, say, to change the name of lampIsOn to lampOn, we don't have to change it in reportState(), we only have to change it in isOn(). Forcing unique access points increase each lamp's encapsulation, which decreases the chance that changing class Lamp will break it.

Here is the modified code:


   public void reportState()
      {
      /*
      Report this lamp's status.
      */

      if (this.isOn() == true)
         System.out.println("I am on.");
      else
         System.out.println("I am off.");
      }

We could, of course, use the form:


   if (isOn() == true)

instead of:

   if (this.isOn() == true)

but the second version is more stylish; it's clearer just which lamp we're asking is on or not, namely: we're asking this lamp---the lamp currently executing the reportState() method.
 
Initializing Variables

Now that we've added methods for a lamp to report its state, another problem pops up. Currently, when we create a lamp it's not automatically off. It's not automatically on, either. It's neither, because it doesn't set its lampIsOn variable to true or false until someone asks it to execute one of its turnOn() or turnOff() methods. Oops.

We can ensure that when we create a lamp it will be off by declaring the lampIsOn variable as follows:


   private boolean lampIsOn = false;

This says that, initially at least, every lamp will be off.

The Java interpreter lets us specify an initial value for any variable when we declare its name and type. As soon as the variable exists it will have its initial value. If we don't specify a value, the variable will still have a value, but nothing that we can depend on. In that case, the stage manager won't let us use the variable until we give it a definite value.
 
Adding a Constructor

Initializing lampIsOn when declaring it is a simple solution to our last problem, but it may not be the best one. Suppose we had several variables to initialize and some of them depended on values in other objects for their correct initial state. Really, what we want is some arbitrary amount of code that executes as soon as we create an object.

Here's a more general solution to the initialization problem:


class Lamp
   {
   /*
   Define a lamp that can turn on and off,
   report its state, report its state changes,
   and make sure it's off on creation.
   */

   //this lamp is either on or off
   private boolean lampIsOn;

   public Lamp()
      {
      /*
      Set this lamp's initial state as being off.
      */

      lampIsOn = false;
      }

   //remaining code goes here...
   }

The new code looks like a method, but it isn't. It's a constructor. We can ask an object to execute its methods whenever we want, but we can't ask it to execute its constructor at all. Nor can we specify return types for constructors, as we must for methods. Further, we can't explicitly return from a constructor. Finally, we must name a constructor the same as the class it constructs objects for.

If we name a method the same as the class we define it in (which is legal, but a really bad style crime) the Java interpreter tells whether it's supposed to be a constructor or not by whether it has a return type or not.

Despite its name, a constructor doesn't construct objects; it initializes them. Constructors set the initial state of the object to be something reasonable for a newly created object of its class. When we write a "new" statement, we are asking the stage manager to create a new object and then to execute its constructor (if any). That's why we must follow the script name with parentheses in every new object creation request.
 
Adding Multiple Constructors

A class can have any number of constructors, each specialized for a different kind of initialization of its objects. Suppose, for example, that we wanted to give our lamps a wattage. A lamp's wattage would be another part of its state, just as whether it's presently on or off is.

First, we'd have to add a new variable to class Lamp to remember each lamp's wattage. Since we usually measure wattage in whole units, it's reasonable to make this an int, and call it, say, wattage.

Then, we'd need a way to set the wattage. Having a setWattage() method, say, would be a style crime because a lamp's wattage won't ever change. Once a lamp exists, it has a fixed wattage. If we were to give class Lamp a method to alter a particular lamp's wattage, some object, somewhere, might ask a lamp to set its wattage incorrectly.

The best thing would be to set each lamp's wattage once and for all when we create it. So we should put that code in the constructor. But the constructor we have now isn't specific to lamps with wattages. We could, of course, change the constructor. Instead, let's add another constructor; one that takes a wattage as a parameter.


class Lamp
   {
   /*
   Define a lamp that can turn on and off,
   report its state, report its state changes,
   have a wattage, and make sure it's off on creation.
   */

   //this lamp is either on or off
   private boolean lampIsOn;

   //this lamp has a wattage;
   private int wattage;

   public Lamp()
      {
      /*
      Set this lamp's initial state
      as being a 60-watt lamp in the off position.
      */

      lampIsOn = false;
      wattage = 60;
      }

   public Lamp(int wattage)
      {
      /*
      Set this lamp's initial state
      as being in the off position.
      Set this lamp's wattage to the given wattage.
      */

      lampIsOn = false;
      this.wattage = wattage;
      }

   //remaining code goes here...
   }

The reference variable this clarifies which wattage is which in the statement:


   this.wattage = wattage;

The left-hand side specifies the wattage the lamp keeps, and the right-hand side specifies the wattage that was passed into the constructor. The statement thus says to copy the value of the parameter passed into the constructor into the lamp's wattage variable.
 
More Style Crimes

We could have named the second constructor's parameter something else, say, lampWattage. That would avoid us having to use this since we could then write:


   wattage = lampWattage;

inside the constructor, instead of

   this.wattage = wattage;

But going to such lengths just to avoid using this is a style crime. A variable's name is an important thing; we should chose it with great care to remind ourselves exactly what the variable is for. To then have to make up another name for essentially the same thing is foolish. Further, other programmers reading our code would then have to remember, and disambiguate, two names for essentially the same thing. That is a style crime. We will see more of them as we continue coding.
 
Choosing Among Constructors

Class Lamp now has two constructors. To figure out which one to execute, the Java interpreter examines the parameters, if any, that we supply in the lamp's new object creation request.

If we say:


   Lamp someLamp;
   someLamp = new Lamp();

we'll get a standard 60-watt lamp since inside the constructor that takes no parameters we set the lamp's default wattage to 60 watts.

If, however, we say:


   Lamp someOtherLamp;
   someOtherLamp = new Lamp(100);

we'll get a 100-watt lamp since inside the constructor that takes one int parameter we set the lamp's wattage to the value of the parameter.

Here's a class to test that:


import Lamp;

class FiddleWithLamps
   {
   /*
   Create some lamps and fiddle with them.
   */

   public static void main(String[] parameters)
      {
      //create a (normal) 60-watt lamp
      Lamp kandinsky;
      kandinsky = new Lamp();
      kandinsky.turnOn();
      kandinsky.turnOff();

      //create a 100-watt lamp
      Lamp bauhaus;
      bauhaus = new Lamp(100);
      bauhaus.turnOn();
      bauhaus.turnOff();
      }
   }

The signature of a method or constructor is the sequence, number, and types of parameters it takes. Having two constructors with different signatures is the same as having an if test embedded in the object creation request that chooses among the constructors based on their signatures. Two or more methods can have the same names as long as they have different signatures since that's what the Java interpreter uses to tell them apart. The same goes for constructors.
 
Declaring Constants

Suppose that we want to place a limit on the highest wattage a lamp can have. From what we've seen so far, we could create a new variable, say, MAXIMUM_WATTAGE, and set its value during its declaration, as we once did for lampIsOn. Then, inside the second constructor, we can test the value of the wattage parameter against MAXIMUM_WATTAGE.

This will work. However, somewhere else we might accidentally change the value of MAXIMUM_WATTAGE and not realize it. We need some way to make sure that once we set its value in its declaration, we cannot ever reset it.

We can use the word final to ask the stage manager to disallow changes to the value inside a variable. So the following statement asks the stage manager to produce a private box containing the int value 500, and it also asks the stage manager to make sure the value in the box never changes:


   private final int MAXIMUM_WATTAGE = 500;

If we declare a variable without modifying it with the word final then we can change its value any time we want.

Here is the beginning of the latest version of the class:


class Lamp
   {
   /*
   Define a lamp that can turn on and off,
   report its state, report its state changes,
   have a wattage, and make sure it's off on creation.
   */

   //this lamp is either on or off
   private boolean lampIsOn;

   //this lamp has a wattage
   private int wattage;

   //this lamp has a maximum possible wattage
   private final int MAXIMUM_WATTAGE = 500;

   public Lamp()
      {
      /*
      Set this lamp's initial state
      as being a 60-watt lamp in the off position.
      */

      lampIsOn = false;
      wattage = 60;
      }

   public Lamp(int wattage)
      {
      /*
      Set this lamp's initial state
      as being in the off position.
      Set this lamp's wattage to the given wattage.
      Do not let any lamp have a wattage higher than
      MAXIMUM_WATTAGE.
      */

      lampIsOn = false;

      if (wattage > MAXIMUM_WATTAGE)
         this.wattage = MAXIMUM_WATTAGE;
      else
         this.wattage = wattage;
      }

   //remaining code goes here...
   }


 
Naming Constants

Unfortunately, the Java interpreter lacks a special word to signify that a box can only hold a constant value, thereby forcing us to keep both final and non-final values in variables, even though one can't vary and the other can.

To emphasize the difference to ourselves at least, it's customary to name final variables (variables-that-are-constant) differently than non-final variables (variables-that-can-vary) (sigh). The convention is to name constants with all-uppercase letters, and to separate the words in the name with underscores. So MAXIMUM_WATTAGE will always have a constant value, while wattage can have a varying value. Hopefully, a future language will make this distinction explicit.
 
Adding Class Variables

While the latest version of the program will work, dragging in the idea of a maximum wattage has brought with it yet another problem.

Recall that every Lamp object we ever create must have a copy of all the variables we declare in that class. Well, that's fine for the lamp's lampIsOn and wattage variables, since both are part of the lamp's state (they can vary from lamp to lamp), but it isn't so sensible for a lamp's MAXIMUM_WATTAGE variable. That isn't a "variable" at all---it's a constant. And every lamp we ever create will have its own copy.

That is a style crime because MAXIMUM_WATTAGE shouldn't belong to any particular lamp, it's a property of the idea of a lamp---it should belong to the class, not any particular object of the class. We'd like some way to have only one copy of it that no particular lamp contains, but that all lamps can access. Declaring it as static does that for us.

Here is the beginning of the latest version of the class:


class Lamp
   {
   /*
   Define a lamp that can turn on and off,
   report its state, report its state changes,
   have a wattage, and make sure it's off on creation.

   The class itself remembers the maximum possible wattage
   of any lamp.
   */

   //this lamp is either on or off
   private boolean lampIsOn;

   //this lamp has a wattage
   private int wattage;

   //all lamps have a maximum possible wattage
   private final static int MAXIMUM_WATTAGE = 500;

   public Lamp()
      {
      /*
      Set this lamp's initial state
      as being a 60-watt lamp in the off position.
      */

      lampIsOn = false;
      wattage = 60;
      }

   public Lamp(int wattage)
      {
      /*
      Set this lamp's initial state
      as being in the off position.
      Set this lamp's wattage to the given wattage.
      Do not let any lamp have a wattage higher than
      MAXIMUM_WATTAGE.
      */

      lampIsOn = false;

      if (wattage > Lamp.MAXIMUM_WATTAGE)
         this.wattage = Lamp.MAXIMUM_WATTAGE;
      else
         this.wattage = wattage;
      }

   //remaining code goes here...
   }

We access static variables with the name of the class they belong to, not any particular object of that class. They belong to the class as a whole, not any particular object of that class. All lamps can learn the maximum possible wattage any lamp can have, but they don't each separately have copies of that value; there is only ever one copy. Since we access such variables through their class, and not any specific object, they're class variables.
 
Adding Class Methods

Since non-Lamp objects can create lamps, they need to know what any lamp's maximum possible wattage is. One simple way to allow this is to make MAXIMUM_WATTAGE public. This is the only reasonable exception to making all variables private. Because the variable can't be changed at all, it's quite safe to make it public. No one can change it since it is unchangeable.

Suppose though that we wanted to do it through a method, say, getMaximumWattage(). This isn't a good idea, just as it wasn't a good idea to make MAXIMUM_WATTAGE a normal variable. No single object of class Lamp should have this method. Further, since objects need to know the maximum wattage before they create any lamps, it's something that must be accessible even when there are no lamps at all. So the method should belong to the class as a whole. It should be a class method, just as MAXIMUM_WATTAGE is a class variable. Once again, we signify that with the word static.

Here's the new code:


class Lamp
   {
   /*
   Define a lamp that can turn on and off,
   report its state, report its state changes,
   have a wattage, and make sure it's off on creation.

   The class itself remembers the maximum possible wattage
   of any lamp and provides a method to report it to objects.
   */

   //some code goes here...

   public static int getMaximumWattage()
      {
      /*
      Report the maximum wattage allowed for any lamp.
      */

      return Lamp.MAXIMUM_WATTAGE;
      }

   //remaining code goes here...
   }

Just as we access class variable MAXIMUM_WATTAGE through its class, we also request execution of class method getMaximumWattage() through its class, like so:


   Lamp.getMaximumWattage();


 
Unravelling the Last Puzzle

Using class variables and methods solves our previous problems, but it, as usual, brings up a puzzler of its own. What does it mean for a variable or method to be a class variable or method?

Since we access class variables through their class, and since they don't exist in any particular object of that class, they must exist before any particular object of that class exists. So where do they exist?

So far, all we've ever seen are objects; objects own variables and objects execute methods. Nothing happens without an object there to mediate it. So what object executes class methods and stores class variables?

The trick is this: Every time we define a class, the stage manager also secretly creates an unnamed object for that class. This class object is what we can ask to access class variables or to execute class methods. Since these class variables and methods don't really belong to any particular object, we access them by using the class name instead of any particular object's name. So, if we were to say, for instance:


   Lamp.getMaximumWattage();

the object associated with class Lamp would execute the method for us.

We've already seen several class methods---all the main() methods we've seen so far are static, or class, methods.

We've also already seen lots of class variables. Every variable declared inside any of the main() methods we've seen thus far has been a class variable. They must be because when the stage manager is executing main() there aren't yet any objects to have variables.

So, kandisky, for example, is a class variable. It belongs to the class as a whole, not to any particular object of the class. Recall that the reference variable kandinsky merely stores the name of an object; although kandinsky is a class variable, the object it names is not a class object.

Unlike MAXIMUM_WATTAGE, however, all those class variables have had only local scope (they only exist inside a main() method) not global scope.
 
One Last Mistake To Avoid

It's very tempting to think that the class name is the reference variable for the secret object. This is not so. The secret object has no---and can have no---reference variable at all. We never create it, and we certainly don't name it, so we can't ever access a reference variable holding its name.

With kandinsky, say, we at least created and named the object that kandinsky refers to. So even though we don't know, and never will know, its real name, we at least know where to find its real name. With the secret object though its name isn't stored anywhere at all. We can do absolutely nothing at all with it. All we can do is ask the Java interpreter to please ask it to do class-related stuff for us. That's all.
 
Starting the Play

Knowing about the secret object gives us a much deeper understanding of what happens when the play starts. What happens is this: the stage manager reads the script and immediately creates a secret object associated with that script. We haven't yet asked it to create any objects, so it hasn't yet created any of our objects.

When we ask the stage manager to begin the play it sends the message:


   [secret object's name].main(parameters);

and the secret object then executes our main() method. This is why we must declare the main() method as a class method using the word static. Secret class objects can only execute class methods. We cannot ask them to execute normal methods because we have no way to name the secret object so that we can send it a message asking it to execute any method.

This gives us a pretty good understanding of the mystery declaration:


   public static void main(String[] parameters)

This statement declares a method named main(). The method accepts one parameter whose type is String[] (which is still a puzzle). It returns no value since its return type is void.

Further, it is public, which makes it publically available to be executed by any object, which is necessary if the stage manager is to see it at the start of the play. Finally, it is a static, or class, method, so that it can be executed without having to ask an object of the class to execute it, which is also necessary at the start of the play since only the secret object exists then.

To execute the main() method, the stage manager sends a message to the secret class object asking it to do so; and that is what starts the play.
 
The Whole Program


class Lamp
   {
   /*
   Define a lamp that can turn on and off,
   report its state, report its state changes,
   have a wattage, and make sure it's off on creation.

   The class itself remembers the maximum possible wattage
   of any lamp and provides a method to report it to objects.
   */

   //this lamp is either on or off
   private boolean lampIsOn;

   //this lamp has a wattage
   private int wattage;

   //all lamps have a maximum possible wattage
   private final static int MAXIMUM_WATTAGE = 500;

   public Lamp()
      {
      /*
      Set this lamp's initial state
      as being a 60-watt lamp in the off position.
      */

      lampIsOn = false;
      wattage = 60;
      }

   public Lamp(int wattage)
      {
      /*
      Set this lamp's initial state
      as being in the off position.
      Set this lamp's wattage to the given wattage.
      Do not let any lamp have a wattage higher than
      MAXIMUM_WATTAGE.
      */

      lampIsOn = false;

      if (wattage > Lamp.MAXIMUM_WATTAGE)
         this.wattage = Lamp.MAXIMUM_WATTAGE;
      else
         this.wattage = wattage;
      }

   public static int getMaximumWattage()
      {
      /*
      Report the maximum wattage allowed for any lamp.
      */

      return Lamp.MAXIMUM_WATTAGE;
      }

   public void turnOn()
      {
      /*
      Turn this lamp on.
      */

      lampIsOn = true;
      reportState();
      }

   public void turnOff()
      {
      /*
      Turn this lamp off.
      */

      lampIsOn = false;
      reportState();
      }

   public boolean isOn()
      {
      /*
      Report whether this lamp is on or off.
      */

      return lampIsOn;
      }

   public void reportState()
      {
      /*
      Report this lamp's status.
      */

      if (this.isOn() == true)
         System.out.println("I am on at wattage: " + wattage);
      else
         System.out.println("I am off.");
      }
   }


import Lamp;

class FiddleWithLamps
   {
   /*
   Create some lamps and fiddle with them.
   */

   public static void main(String[] parameters)
      {
      //create a (normal) 60-watt lamp
      Lamp kandinsky;
      kandinsky = new Lamp();
      kandinsky.turnOn();
      kandinsky.turnOff();

      //create a 100-watt lamp
      Lamp bauhaus;
      bauhaus = new Lamp(100);
      bauhaus.turnOn();
      bauhaus.turnOff();

      if (kandinsky.isOn() == bauhaus.isOn())
         System.out.println("The two lamp states are equal.");
      else
         System.out.println("The two lamp states are unequal.");

      System.out.println("Maximum wattage of any lamp = " +
         Lamp.getMaximumWattage());
      }
   }


last | | contents | | next