Constructors are a special sort of method that initialise freshly minted
objects. They are like static methods in that you don't need an existing object
to use them, just new. They are like instance
methods in that they operate on the current object. They are a bit like static
factory methods in that they are used in creating new objects, however, they don't
create objects, just initialise them.
Constructors are not Ordinary Methods
They have a number of magic properties.
-
They have the same name as the class.
-
Constructors are not inherited. This means you have to write out reams of dummy
constructors for every subclass that just invoke the corresponding super(xxx).
-
If you don't define the any constructors, Java will "write" a
default parameterless one for you. You won't actually see the code unless you
examine the class file. If you later add some other constructor, it won't
generate this invisible default one.
-
If you don't call super(xxx) as the first line of
code in a constructor, Java will automatically insert a call to the default
superclass constructor super() for you. If the
superclass has no default constructor, you will have to insert super(xxx)
manually. Your call to super(xxx) must be the very
first code in the constructor. You can't even sandwich it in a try
block.
-
Constructors logically return an object, yet you are not allowed to declare that
fact. You can't write void either.
-
It is possible to have ordinary static or instance methods also with the same
name as the class. The compiler distinguishes these from true constructors
because they have an explicit return type. Be careful!
-
You don't specify new inside your constructor. The
space for the object has already been allocated before your constructor started
to execute. All you are doing is initialising fields.
-
You don't return this. The constructor does not
return a value. A ordinary static method that created objects would do so.
-
If you wanted to handle your own memory management, e.g. recycle used objects,
you would do this with an ordinary static method, not a constructor.
-
Be very careful calling any methods inside your constructors. If subclasses
override them, you will be invoking the subclass's version, which may be
attempting to use fields that have not been initialised yet. You won't get a
warning message! The problem usually shows up as puzzling NullPointerExceptions.
The way out is to move code out of the constructor, often to the addNotify
method. However, addNotify can get in analogous
problem to the constructor since it too is overridden, and it may use overridden
methods. One way to avoid the problem is to only use private
methods in constructor initialisation.
-
You can call a constructor only once in an object's lifetime. Thus the
constructor may initialise fields marked final
where ordinary methods such as addNotify cannot,
even if they are only called once in practice.
Object Initialisation
Objects are initialised in a complex series of steps. However, the process works
pretty much as you expect it would have to. Constructors are just the icing on
the cake in the whole scheme of object initialisation. It works like this:
-
new allocates space for an object.
-
All the fields are zeroed.
-
Fields that were initialised inline (with an = on the declare) are set.
-
Fields that were initialised inside { } initialiser blocks are set. There are
actually interleaved in textual order with the inline initialisations above.
-
The derived constructor is started. The very first thing it does in call the
base constructor which initialises fields only in the base class.
-
f The derived constructor continues, initialising fields in the base or derived
class.
You have to be aware of this order. Java is not smart enough to resolve
initialisation dependencies. I got it trouble with this when I ran a code
beautifier than put all my fields in alphabetical order. The dependencies no
longer worked and the fields were initialised to the wrong values, without any
warning messages.
addNotify
Don't put GUI-type code in your constructor. Put it it in an addNotify
method that looks like this:
/**
* Build the GUI
*/
public void addNotify()
{
super.addNotify();
setTitle( "Whazmotron Inc." );
...
}
Don't forget to call super.addNotify
(); first, or the peer object won't get
created and your Component won't be hooked into the
actual underlying native GUI and you will be staring at a blank screen.
Putting GUI code here means the GUI is ready for it. The peer exists as soon at
the code returns from the call to super.addNotify(),
not before. the peer does not yet exist in the constructor. You need a peer for getImage
type activities. Further, if you call methods in your constructor that
subclasses override, it means you are less likely to be be referencing variables
that don't exist yet if you divide your initialisation logically between the
constructor and addNotify.
This is also a good place to move constructor code that is failing because it
uses methods overridden my subclasses that reference fields not yet initialised
by the subclass's constructor which has not yet run yet.