44. The rules for overriding a method are as follows:
■ The argument list must exactly match that of the overridden method. If they
don't match, you can end up with an overloaded method you didn't intend.
■ The return type must be the same as, or a subtype of, the return type declared
in the original overridden method in the superclass. (More on this in a few
pages when we discuss covariant returns.)
■ The access level can't be more restrictive than the overridden method's.
■ The access level CAN be less restrictive than that of the overridden method.
■ Instance methods can be overridden only if they are inherited by the subclass.
A subclass within the same package as the instance's superclass can override
any superclass method that is not marked private or final. A subclass in a
different package can override only those non-final methods marked public
or protected (since protected methods are inherited by the subclass).
■ The overriding method CAN throw any unchecked (runtime) exception,
regardless of whether the overridden method declares the exception. (More
in Chapter 5.)
■ The overriding method must NOT throw checked exceptions that are new
or broader than those declared by the overridden method. For example, a
method that declares a FileNotFoundException cannot be overridden by a
method that declares a SQLException, Exception, or any other non-runtime
exception unless it's a subclass of FileNotFoundException.
■ The overriding method can throw narrower or fewer exceptions. Just because
an overridden method "takes risks" doesn't mean that the overriding subclass'
exception takes the same risks. Bottom line: an overriding method doesn't
have to declare any exceptions that it will never throw, regardless of what the
overridden method declares.
■ You cannot override a method marked final.
■ You cannot override a method marked static. We'll look at an example in a
few pages when we discuss static methods in more detail.
■ If a method can't be inherited, you cannot override it. Remember that
overriding implies that you're reimplementing a method you inherited!
45. public class TestAb extends Test {
static Integer test(int t) {
return null;
}
/*
* ok, becuase it's the differnt type.
*/
static String test(Integer t) {
return null;
}
static String test(double t) {
return null;
}
public static void main(String[] args) {
test(2); // ok.
test(new Double(2)); // ok.
}
}
46. class Animal { }
class Horse extends Animal { }
class UseAnimals {
public void doStuff(Animal a) {
System.out.println("In the Animal version");
}
public void doStuff(Horse h) {
System.out.println("In the Horse version");
}
public static void main (String [] args) {
UseAnimals ua = new UseAnimals();
Animal animalObj = new Animal();
Horse horseObj = new Horse();
ua.doStuff(animalObj);
ua.doStuff(horseObj);
}
}
The output is what you expect:
in the Animal version
in the Horse version
47. Even though the actual object at runtime is a Horse and not an Animal, the
choice of which overloaded method to call (in other words, the signature of the
method) is NOT dynamically decided at runtime. Just remember, the reference
type (not the object type) determines which overloaded method is invoked! To
summarize, which overridden version of the method to call (in other words, from
which class in the inheritance tree) is decided at runtime based on object type, but
which overloaded version of the method to call is based on the reference type of
the argument passed at compile time. If you invoke a method passing it an Animal
reference to a Horse object, the compiler knows only about the Animal, so it
chooses the overloaded version of the method that takes an Animal. It does not
matter that at runtime there's actually a Horse being passed.
48. As we mentioned earlier, a class is not allowed to extend multiple classes in Java.
An interface, however, is free to extend multiple interfaces.
interface Bounceable extends Moveable, Spherical { // ok!}
49. class Yow extends Foo implements Fi { } // OK. Class can do both
// (extends must be 1st)
50. When a subclass wants to change the method implementation of an inherited
method (an override), the subclass must define a method that matches the inherited
version exactly. Or, as of Java 5, you're allowed to change the return type in the
overriding method as long as the new return type is a subtype of the declared return
type of the overridden (superclass) method.
51. 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!
}
}
52. 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.)
a. 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).
b. Animal constructor is invoked (Animal is the superclass of Horse).
c. 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.
d. 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.
e. Object constructor completes.
f. Animal instance variables are given their explicit values (if any).
g. Animal constructor completes.
h. Horse instance variables are given their explicit values (if any).
i. Horse constructor completes.
Figure 2-6 shows how constructors work on the call stack.
Object
Animal() calls super()
Horse() calls super()
main calls new Horse()
53. 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()).
54.
class Foo { } -->class Foo {
Foo() {
super();
}
}
class Foo {
Foo() { }
} -->
class Foo {
Foo() {
super();
}
}
public class Foo { } --> public 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();
}
}
55. 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().
56. Finally, remember that static methods can't be overridden! This doesn't mean they
can't be redefined in a subclass, but redefining and overriding aren't the same thing.
Let's take a look at an example of a redefined (remember, not overridden), static
method:
class Animal {
static void doStuff() {
System.out.print("a ");
}
}
class Dog extends Animal {
static void doStuff() { // it's a redefinition,
// not an override
System.out.print("d ");
}
public static void main(String [] args) {
Animal [] a = {new Animal(), new Dog(), new Animal()};
for(int x = 0; x < a.length; x++)
a[x].doStuff(); // invoke the static method
}
}
Running this code produces the output:
a a a
Remember, the syntax a[x].doStuff() is just a shortcut (the syntax trick)…the
compiler is going to substitute something like Animal.doStuff() instead. Notice
that we didn't use the Java 1.5 enhanced for loop here (covered in Chapter 5), even
though we could have. Expect to see a mix of both Java 1.4 and Java 5 coding styles
and practices on the exam.
■ The argument list must exactly match that of the overridden method. If they
don't match, you can end up with an overloaded method you didn't intend.
■ The return type must be the same as, or a subtype of, the return type declared
in the original overridden method in the superclass. (More on this in a few
pages when we discuss covariant returns.)
■ The access level can't be more restrictive than the overridden method's.
■ The access level CAN be less restrictive than that of the overridden method.
■ Instance methods can be overridden only if they are inherited by the subclass.
A subclass within the same package as the instance's superclass can override
any superclass method that is not marked private or final. A subclass in a
different package can override only those non-final methods marked public
or protected (since protected methods are inherited by the subclass).
■ The overriding method CAN throw any unchecked (runtime) exception,
regardless of whether the overridden method declares the exception. (More
in Chapter 5.)
■ The overriding method must NOT throw checked exceptions that are new
or broader than those declared by the overridden method. For example, a
method that declares a FileNotFoundException cannot be overridden by a
method that declares a SQLException, Exception, or any other non-runtime
exception unless it's a subclass of FileNotFoundException.
■ The overriding method can throw narrower or fewer exceptions. Just because
an overridden method "takes risks" doesn't mean that the overriding subclass'
exception takes the same risks. Bottom line: an overriding method doesn't
have to declare any exceptions that it will never throw, regardless of what the
overridden method declares.
■ You cannot override a method marked final.
■ You cannot override a method marked static. We'll look at an example in a
few pages when we discuss static methods in more detail.
■ If a method can't be inherited, you cannot override it. Remember that
overriding implies that you're reimplementing a method you inherited!
45. public class TestAb extends Test {
static Integer test(int t) {
return null;
}
/*
* ok, becuase it's the differnt type.
*/
static String test(Integer t) {
return null;
}
static String test(double t) {
return null;
}
public static void main(String[] args) {
test(2); // ok.
test(new Double(2)); // ok.
}
}
46. class Animal { }
class Horse extends Animal { }
class UseAnimals {
public void doStuff(Animal a) {
System.out.println("In the Animal version");
}
public void doStuff(Horse h) {
System.out.println("In the Horse version");
}
public static void main (String [] args) {
UseAnimals ua = new UseAnimals();
Animal animalObj = new Animal();
Horse horseObj = new Horse();
ua.doStuff(animalObj);
ua.doStuff(horseObj);
}
}
The output is what you expect:
in the Animal version
in the Horse version
47. Even though the actual object at runtime is a Horse and not an Animal, the
choice of which overloaded method to call (in other words, the signature of the
method) is NOT dynamically decided at runtime. Just remember, the reference
type (not the object type) determines which overloaded method is invoked! To
summarize, which overridden version of the method to call (in other words, from
which class in the inheritance tree) is decided at runtime based on object type, but
which overloaded version of the method to call is based on the reference type of
the argument passed at compile time. If you invoke a method passing it an Animal
reference to a Horse object, the compiler knows only about the Animal, so it
chooses the overloaded version of the method that takes an Animal. It does not
matter that at runtime there's actually a Horse being passed.
48. As we mentioned earlier, a class is not allowed to extend multiple classes in Java.
An interface, however, is free to extend multiple interfaces.
interface Bounceable extends Moveable, Spherical { // ok!}
49. class Yow extends Foo implements Fi { } // OK. Class can do both
// (extends must be 1st)
50. When a subclass wants to change the method implementation of an inherited
method (an override), the subclass must define a method that matches the inherited
version exactly. Or, as of Java 5, you're allowed to change the return type in the
overriding method as long as the new return type is a subtype of the declared return
type of the overridden (superclass) method.
51. 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!
}
}
52. 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.)
a. 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).
b. Animal constructor is invoked (Animal is the superclass of Horse).
c. 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.
d. 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.
e. Object constructor completes.
f. Animal instance variables are given their explicit values (if any).
g. Animal constructor completes.
h. Horse instance variables are given their explicit values (if any).
i. Horse constructor completes.
Figure 2-6 shows how constructors work on the call stack.
Object
Animal() calls super()
Horse() calls super()
main calls new Horse()
53. 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()).
54.
class Foo { } -->class Foo {
Foo() {
super();
}
}
class Foo {
Foo() { }
} -->
class Foo {
Foo() {
super();
}
}
public class Foo { } --> public 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();
}
}
55. 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().
56. Finally, remember that static methods can't be overridden! This doesn't mean they
can't be redefined in a subclass, but redefining and overriding aren't the same thing.
Let's take a look at an example of a redefined (remember, not overridden), static
method:
class Animal {
static void doStuff() {
System.out.print("a ");
}
}
class Dog extends Animal {
static void doStuff() { // it's a redefinition,
// not an override
System.out.print("d ");
}
public static void main(String [] args) {
Animal [] a = {new Animal(), new Dog(), new Animal()};
for(int x = 0; x < a.length; x++)
a[x].doStuff(); // invoke the static method
}
}
Running this code produces the output:
a a a
Remember, the syntax a[x].doStuff() is just a shortcut (the syntax trick)…the
compiler is going to substitute something like Animal.doStuff() instead. Notice
that we didn't use the Java 1.5 enhanced for loop here (covered in Chapter 5), even
though we could have. Expect to see a mix of both Java 1.4 and Java 5 coding styles
and practices on the exam.