Certification Objective Constructors and Instantiation (Exam Objectives 1.6 and 5.4)


Certification Objective —Constructors and Instantiation (Exam Objectives 1.6 and 5.4)

1.6 Given a set of classes and superclasses, develop constructors for one or more of the classes. Given a class declaration, determine if a default constructor will be created, and if so, determine the behavior of that constructor. Given a nested or non-nested class listing, write code to instantiate the class.

5.4 Given a scenario, develop code that declares and/or invokes overridden or overloaded methods and code that declares and/or invokes superclass, overridden, or overloaded constructors.

Objects are constructed. You can't make a new object without invoking a constructor. In fact, you can't make a new object without invoking not just the constructor of the object's actual class type, but also the constructor of each of its superclasses! Constructors are the code that runs whenever you use the keyword new. OK, to be a bit more accurate, there can also be initialization blocks that run when you say new, but we're going to cover them (init blocks), and their static initialization counterparts, in the next chapter. We've got plenty to talk about here—we'll look at how constructors are coded, who codes them, and how they work at runtime. So grab your hardhat and a hammer, and let's do some object building.

Constructor Basics

Every class, including abstract classes, MUST have a constructor. Burn that into your brain. But just because a class must have one, doesn't mean the programmer has to type it. A constructor looks like this:

 class Foo {    Foo() { } // The constructor for the Foo class } 

Notice what's missing? There's no return type! Two key points to remember about constructors are that they have no return type and their names must exactly match the class name. Typically, constructors are used to initialize instance variable state, as follows:

 class Foo {    int size;    String name;    Foo(String name, int size) {       this.name = name;       this.size = size;    } } 

In the preceding code example, the Foo class does not have a no-arg constructor. That means the following will fail to compile:

 Foo f = new Foo();  // Won't compile, no matching constructor 

but the following will compile:

 Foo f = new Foo("Fred", 43);  // No problem. Arguments match                               // the Foo constructor. 

So it's very common (and desirable) for a class to have a no-arg constructor, regardless of how many other overloaded constructors are in the class (yes, constructors can be overloaded). You can't always make that work for your classes; occasionally you have a class where it makes no sense to create an instance without supplying information to the constructor. A java.awt.Color object, for example, can't be created by calling a no-arg constructor, because that would be like saying to the JVM, "Make me a new Color object, and I really don't care what color it isyou pick." Do you seriously want the JVM making your style decisions?

Constructor Chaining

We know that constructors are invoked at runtime when you say new on some class type as follows:

 Horse h = new Horse(); 

But what really happens when you say new Horse () ?
(Assume Horse extends Animal and Animal extends Object.)

  1. Horse constructor is invoked. Every constructor invokes the constructor of its superclass with an (implicit) call to super(), unless the constructor invokes an overloaded constructor of the same class (more on that in a minute).

  2. Animal constructor is invoked (Animal is the superclass of Horse).

  3. Object constructor is invoked (Object is the ultimate superclass of all classes, so class Animal extends Object even though you don't actually type "extends Object" into the Animal class declaration. It's implicit.) At this point we're on the top of the stack.

  4. Object instance variables are given their explicit values. By explicit values, we mean values that are assigned at the time the variables are declared, like "int x = 27", where "27" is the explicit value (as opposed to the default value) of the instance variable.

  5. Object constructor completes.

  6. Animal instance variables are given their explicit values (if any).

  7. Animal constructor completes.

  8. Horse instance variables are given their explicit values (if any).

  9. Horse constructor completes.

Figure 2-6 shows how constructors work on the call stack.

4.

Object()

3.

Animal() calls super()

2.

Horse() calls super()

1.

main() calls new Horse()


Figure 2-6: Constructors on the call stack

Rules for Constructors

The following list summarizes the rules you'll need to know for the exam (and to understand the rest of this section). You MUST remember these, so be sure to study them more than once.

  • Constructors can use any access modifier, including private. (A private constructor means only code within the class itself can instantiate an object of that type, so if the private constructor class wants to allow an instance of the class to be used, the class must provide a static method or variable that allows access to an instance created from within the class.)

  • The constructor name must match the name of the class.

  • Constructors must not have a return type.

  • It's legal (but stupid) to have a method with the same name as the class, but that doesn't make it a constructor. If you see a return type, it's a method rather than a constructor. In fact, you could have both a method and a constructor with the same name—the name of the class—in the same class, and that's not a problem for Java. Be careful not to mistake a method for a constructor—be sure to look for a return type.

  • If you don't type a constructor into your class code, a default constructor will be automatically generated by the compiler.

  • The default constructor is ALWAYS a no-arg constructor.

  • If you want a no-arg constructor and you've typed any other constructor(s) into your class code, the compiler won't provide the no-arg constructor (or any other constructor) for you. In other words, if you've typed in a constructor with arguments, you won't have a no-arg constructor unless you type it in yourself!

  • Every constructor has, as its first statement, either a call to an overloaded constructor (this()) or a call to the superclass constructor (super()), although remember that this call can be inserted by the compiler.

  • If you do type in a constructor (as opposed to relying on the compiler-generated default constructor), and you do not type in the call to super() or a call to this(), the compiler will insert a no-arg call to super() for you, as the very first statement in the constructor.

  • A call to super() can be either a no-arg call or can include arguments passed to the super constructor.

  • A no-arg constructor is not necessarily the default (i.e., compiler-supplied) constructor, although the default constructor is always a no-arg constructor. The default constructor is the one the compiler provides! While the default constructor is always a no-arg constructor, you're free to put in your own noarg constructor.

  • You cannot make a call to an instance method, or access an instance variable, until after the super constructor runs.

  • Only static variables and methods can be accessed as part of the call to super() or this(). (Example: super (Animal.NAME) is OK, because NAME is declared as a static variable.)

  • Abstract classes have constructors, and those constructors are always called when a concrete subclass is instantiated.

  • Interfaces do not have constructors. Interfaces are not part of an object's inheritance tree.

  • The only way a constructor can be invoked is from within another constructor. In other words, you can't write code that actually calls a constructor as follows:

        class Horse {      Horse() { } // constructor   void doStuff() {     Horse();  // calling the constructor - illegal!   } } 

Determine Whether a Default Constructor will be Created

The following example shows a Horse class with two constructors:

 class Horse {    Horse() { }    Horse (String name) { }  } 

Will the compiler put in a default constructor for the class above? No! How about for the following variation of the class?

 class Horse {    Horse(String name) { } } 

Now will the compiler insert a default constructor? No! What about this class?

 class Horse { } 

Now we're talking. The compiler will generate a default constructor for the preceding class, because the class doesn't have any constructors defined. OK, what about this class?

 class Horse {    void Horse() { } } 

It might look like the compiler won't create one, since there already is a constructor in the Horse class. Or is there? Take another look at the preceding Horse class.

What's wrong with the Horse() constructor? It isn't a constructor at all! It's simply a method that happens to have the same name as the class. Remember, the return type is a dead giveaway that we're looking at a method, and not a constructor.

How do You Know for Sure Whether a Default Constructor will be Created?

Because you didn't write any constructors in your class.

How do you Know What the Default Constructor will Look Like?

Because

  • The default constructor has the same access modifier as the class.

  • The default constructor has no arguments.

  • The default constructor includes a no-arg call to the super constructor (super()).

Table 2-4 shows what the compiler will (or won't) generate for your class.

Table 2-4: Compiler-Generated Constructor Code

Class Code (WhatYouType)

Compiler Generated Constructor Code (in Bold)

 class Foo { } 

 class Foo {   Foo() {     super ( ) ;   } } 

 class Foo {   Foo() { } } 

 class Foo {   Foo() {     super() ;   } } 

 public class Foo { } 

 class Foo {   public Foo() {     super();   } } 

 class Foo {   Foo(String s) { } } 

 class Foo {   Foo (String s) {     super();   } } 

 class Foo {   Foo(String s) {     super();   } } 

 Nothing, compiler doesn't need to insert anything. 

 class Foo {   void Foo() { } } 

 class Foo {   void Foo() { }   Foo() {     super();   } } (void Foo() is a method, not a constructor.) 

What Happens if the Super Constructor has Arguments?

Constructors can have arguments just as methods can, and if you try to invoke a method that takes, say, an int, but you don't pass anything to the method, the compiler will complain as follows:

 class Bar {     void takeInt (int x) { } } class UseBar {    public static void main (String [] args) {      Bar b = new Bar();      b.takeInt(); // Try to invoke a no-arg takeInt ( ) method    } } 

The compiler will complain that you can't invoke takeInt( ) without passing an int. Of course, the compiler enjoys the occasional riddle, so the message it spits out on some versions of the JVM (your mileage may vary) is less than obvious:

 UseBar.java:7: takeInt(int) in Bar cannot be applied to()      b.takeInt();       ^ 

But you get the idea. The bottom line is that there must be a match for the method. And by match, we mean that the argument types must be able to accept the values or variables you're passing, and in the order you're passing them. Which brings us back to constructors (and here you were thinking we'd never get there), which work exactly the same way.

So if your super constructor (that is, the constructor of your immediate superclass/parent) has arguments, you must type in the call to super( ), supplying the appropriate arguments. Crucial point: if your superclass does not have a no-arg constructor, you must type a constructor in your class (the subclass) because you need a place to put in the call to super with the appropriate arguments.

The following is an example of the problem:

 class Animal {    Animal (String name) { } } class Horse extends Animal {    Horse() {       super();  // Problem!    } } 

And once again the compiler treats us with the stunningly lucid:

 Horse.java:7: cannot resolve symbol symbol  : constructor Animal () location: class Animal       super();  // Problem!       ^ 

If you're lucky (and it's a full moon), your compiler might be a little more explicit. But again, the problem is that there just isn't a match for what we're trying to invoke with super()—an Animal constructor with no arguments.

Another way to put this is that if your superclass does not have a no-arg constructor, then in your subclass you will not be able to use the default constructor supplied by the compiler. It's that simple. Because the compiler can only put in a call to a no-arg super(), you won't even be able to compile something like this:

 class Clothing {    Clothing(String s) { } } class TShirt extends Clothing { } 

Trying to compile this code gives us exactly the same error we got when we put a constructor in the subclass with a call to the no-arg version of super():

 Clothing.java:4: cannot resolve symbol symbol  : constructor Clothing () location: class Clothing class TShirt extends Clothing { } ^ 

In fact, the preceding Clothing and TShirt code is implicitly the same as the following code, where we've supplied a constructor for TShirt that's identical to the default constructor supplied by the compiler:

 class Clothing {    Clothing (String s) { } } class TShirt extends Clothing {                   // Constructor identical to compiler-supplied                   // default constructor    TShirt() {       super(); // Won't work!    }           // Invokes a no-arg Clothing() constructor, }              // but there isn't one! 

One last point on the whole default constructor thing (and it's probably very obvious, but we have to say it or we'll feel guilty for years), constructors are never inherited. They aren't methods. They can't be overridden (because they aren't methods and only instance methods can be overridden). So the type of constructor(s) your superclass has in no way determines the type of default constructor you'll get. Some folks mistakenly believe that the default constructor somehow matches the super constructor, either by the arguments the default constructor will have (remember, the default constructor is always a no-arg), or by the arguments used in the compiler-supplied call to super().

So, although constructors can't be overridden, you've already seen that they can be overloaded, and typically are.

Overloaded Constructors

Overloading a constructor means typing in multiple versions of the constructor, each having a different argument list, like the following examples:

 class Foo {    Foo() { }    Foo (String s) { } } 

The preceding Foo class has two overloaded constructors, one that takes a string, and one with no arguments. Because there's no code in the no-arg version, it's actually identical to the default constructor the compiler supplies, but remember—since there's already a constructor in this class (the one that takes a string), the compiler won't supply a default constructor. If you want a no-arg constructor to overload the with-args version you already have, you're going to have to type it yourself, just as in the Foo example.

Overloading a constructor is typically used to provide alternate ways for clients to instantiate objects of your class. For example, if a client knows the animal name, they can pass that to an Animal constructor that takes a string. But if they don't know the name, the client can call the no-arg constructor and that constructor can supply a default name. Here's what it looks like:

  1. public class Animal {  2.   String name;  3.   Animal(String name) {  4.     this.name = name;  5.  }  6   7.  Animal() {  8.   this(makeRandomName());  9.  } 10. 11.  static String makeRandomName() { 12.     int x = (int) (Math.random() * 5); 13.     String name = new String[] {"Fluffy", "Fido",                                      "Rover", "Spike",                                      "Gigi"}[x]; 14.     return name; 15.  } 16. 17.  public static void main (String [] args) { 18.    Animal a = new Animal(); 19.    System.out.println(a.name); 20.    Animal b = new Animal("Zeus"); 21.    System.out.println(b.name); 22.  } 23. } 

Running the code four times produces this output:

 % java Animal Gigi Zeus % java Animal Fluffy Zeus % java Animal Rover Zeus % java Animal Fluffy Zeus 

There's a lot going on in the preceding code. Figure 2-7 shows the call stack for constructor invocations when a constructor is overloaded. Take a look at the call stack, and then let's walk through the code straight from the top.

4.Object

3.Animal(Strings s) calls Super()

2.Animal() calls this(randomlyChosenNameString)

1.main() calls new Animal()


Figure 2-7: Overloaded constructors on the call stack

  • Line 2 Declare a String instance variable name.

  • Lines 3-5 Constructor that takes a String, and assigns it to instance variable name.

  • Line 7 Here's where it gets fun. Assume every animal needs a name, but the client (calling code) might not always know what the name should be, so you'll assign a random name. The no-arg constructor generates a name by invoking the makeRandomName() method.

  • Line 8 The no-arg constructor invokes its own overloaded constructor that takes a String, in effect calling it the same way it would be called if client code were doing a new to instantiate an object, passing it a String for the name. The overloaded invocation uses the keyword this, but uses it as though it were a method name, this(). So line 8 is simply calling the constructor on line 3, passing it a randomly selected String rather than a clientcode chosen name.

  • Line 11 Notice that the makeRandomName() method is marked static! That's because you cannot invoke an instance (in other words, nonstatic) method (or access an instance variable) until after the super constructor has run. And since the super constructor will be invoked from the constructor on line 3, rather than from the one on line 7, line 8 can use only a static method to generate the name. If we wanted all animals not specifically named by the caller to have the same default name, say, "Fred," then line 8 could have read this ("Fred") ; rather than calling a method that returns a string with the randomly chosen name.

  • Line 12 This doesn't have anything to do with constructors, but since we're all here to learnit generates a random integer between 0 and 4.

  • Line 13 Weird syntax, we know. We're creating a new String object (just a single String instance), but we want the string to be selected randomly from a list. Except we don't have the list, so we need to make it. So in that one line of code we

    1. Declare a String variable, name.

    2. Create a String array (anonymously—we don't assign the array itself to anything).

    3. Retrieve the string at index [x] (x being the random number generated on line 12) of the newly created String array.

    4. Assign the string retrieved from the array to the declared instance variable name. We could have made it much easier to read if we'd just written

       String[] nameList = {"Fluffy", "Fido", "Rover", "Spike",                      "Gigi"}; String name = nameList [x] ; 

    But where's the fun in that? Throwing in unusual syntax (especially for code wholly unrelated to the real question) is in the spirit of the exam. Don't be startled! (OK, be startled, but then just say to yourself, "Whoa" and get on with it.)

  • Line 18 We're invoking the no-arg version of the constructor (causing a random name from the list to be passed to the other constructor).

  • Line 20 We're invoking the overloaded constructor that takes a string representing the name.

The key point to get from this code example is in line 8. Rather than calling super(), we're calling this(), and this() always means a call to another constructor in the same class. OK, fine, but what happens after the call to this() ? Sooner or later the super() constructor gets called, right? Yes indeed. A call to this() just means you're delaying the inevitable. Some constructor, somewhere, must make the call to super().

Key Rule: The first line in a constructor Must be a call to super() or a call to this().

No exceptions. If you have neither of those calls in your constructor, the compiler will insert the no-arg call to super(). In other words, if constructor A() has a call to this(), the compiler knows that constructor A() will not be the one to invoke super().

The preceding rule means a constructor can never have both a call to super() and a call to this(). Because each of those calls must be the first statement in a constructor, you can't legally use both in the same constructor. That also means the compiler will not put a call to super() in any constructor that has a call to this().

Thought question: What do you think will happen if you try to compile the following code?

 class A {    A() {      this("foo");    }    A(String s) {       this();    } } 

Your compiler may not actually catch the problem (it varies depending on your compiler, but most won't catch the problem). It assumes you know what you're doing. Can you spot the flaw? Given that a super constructor must always be called, where would the call to super() go? Remember, the compiler won't put in a default constructor if you've already got one or more constructors in your class. And when the compiler doesn't put in a default constructor, it still inserts a call to super() in any constructor that doesn't explicitly have a call to the super constructor—unless, that is, the constructor already has a call to this(). So in the preceding code, where can super() go? The only two constructors in the class both have calls to this(), and in fact you'll get exactly what you'd get if you typed the following method code:

 public void go() {    doStuff() ; } public void doStuff() {    go(); } 

Now can you see the problem? Of course you can. The stack explodes! It gets higher and higher and higher until it just bursts open and method code goes spilling out, oozing out of the JVM right onto the floor. Two overloaded constructors both calling this() are two constructors calling each other. Over and over and over, resulting in

 % java A Exception in thread "main" java.lang.StackOverflowError 

The benefit of having overloaded constructors is that you offer flexible ways to instantiate objects from your class. The benefit of having one constructor invoke another overloaded constructor is to avoid code duplication. In the Animal example, there wasn't any code other than setting the name, but imagine if after line 4 there was still more work to be done in the constructor. By putting all the other constructor work in just one constructor, and then having the other constructors invoke it, you don't have to write and maintain multiple versions of that other important constructor code. Basically, each of the other not-the-real-one overloaded constructors will call another overloaded constructor, passing it whatever data it needs (data the client code didn't supply).

Constructors and instantiation become even more exciting (just when you thought it was safe), when you get to inner classes, but we know you can stand to have only so much fun in one chapter, so we're holding the rest of the discussion on instantiating inner classes until Chapter 8.




SCJP Sun Certified Programmer for Java 5 Study Guide Exam 310-055
SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press)
ISBN: 0072253606
EAN: 2147483647
Year: 2006
Pages: 131

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