The words you are searching are inside this book. To get more targeted content, please make full-text search by clicking here.

Oracle Certifi ed Associate Java SE 8 Programmer I Study Guide

Discover the best professional documents and content resources in AnyFlip Document Base.
Search
Published by Luigy Rod, 2019-07-16 14:30:34

Oracle Certifi ed Associate Java SE 8 Programmer I Study Guide

Oracle Certifi ed Associate Java SE 8 Programmer I Study Guide

Encapsulating Data 205

Encapsulating Data

In Chapter 1, we had an example of a class with a field that wasn’t private:

public class Swan {

int numberEggs; // instance variable

}
Why do we care? Since there is default (package private) access, that means any class

in the package can set numberEggs. We no longer have control of what gets set in our own
class. A caller could even write this:

mother.numberEggs = -1;

This is clearly no good. We do not want the mother Swan to have a negative number of
eggs!

Encapsulation to the rescue. Encapsulation means we set up the class so only methods
in the class with the variables can refer to the instance variables. Callers are required to use
these methods. Let’s take a look at our newly encapsulated Swan class:

1: public class Swan {

2: private int numberEggs; // private

3: public int getNumberEggs() { // getter

4: return numberEggs;

5: }

6: public void setNumberEggs(int numberEggs) { // setter

7: if (numberEggs >= 0) // guard condition

8: this.numberEggs = numberEggs;

9: } }

Note numberEggs is now private on line 2. This means only code within the class can
read or write the value of numberEggs. Since we wrote the class, we know better than to
set a negative number of eggs. We added a method on lines 3–5 to read the value, which is
called an accessor method or a getter. We also added a method on lines 6–9 to update the
value, which is called a mutator method or a setter. The setter has an if statement in this
example to prevent setting the instance variable to an invalid value. This guard condition
protects the instance variable.

On line 8, we used the this keyword that we saw in constructors to differentiate between
the method parameter numberEggs and the instance variable numberEggs.

For encapsulation, remember that data (an instance variable) is private and getters/setters
are public. Java defines a naming convention that is used in JavaBeans. JavaBeans are reusable
software components. JavaBeans call an instance variable a property. The only thing you need
to know about JavaBeans for the exam is the naming conventions listed in Table 4.5.

206 Chapter 4 ■ Methods and Encapsulation

TA B L E 4 . 5 Rules for JavaBeans naming conventions

Rule Example

Properties are private. private int numEggs;

Getter methods begin with is if the public boolean isHappy() {
property is a boolean. return happy;

}

Getter methods begin with get if the public int getNumEggs() {
property is not a boolean. return numEggs;

}

Setter methods begin with set. public void setHappy(boolean happy) {
this.happy = happy;

}

The method name must have a prefix public void setNumEggs(int num) {
of set/get/is, followed by the first numEggs = num;
letter of the property in uppercase, fol-
lowed by the rest of the property name. }

From the last example in Table 4.5, you noticed that you can name the method param-
eter to set anything you want. Only the method name and property name have naming
conventions here.

It’s time for some practice. See if you can figure out which lines follow JavaBeans
naming conventions:

12: private boolean playing;

13: private String name;

14: public boolean getPlaying() { return playing; }

15: public boolean isPlaying() { return playing; }

16: public String name() { return name; }

17: public void updateName(String n) { name = n; }

18: public void setname(String n) { name = n; }

Lines 12 and 13 are good. They are private instance variables. Line 14 doesn't follow the
JavaBeans naming conventions. Since playing is a boolean, the getter must begin with is.
Line 15 is a correct getter for playing. Line 16 doesn't follow the JavaBeans naming con-
ventions because it should be called getName. Lines 17 and 18 do not follow the JavaBeans
naming conventions because they should be named setName. Remember that Java is case
sensitive, so setname is not adequate to meet the naming convention.

Encapsulating Data 207

Creating Immutable Classes

Encapsulating data is helpful because it prevents callers from making uncontrolled changes
to your class. Another common technique is making classes immutable so they cannot be
changed at all.

Immutable classes are helpful because you know they will always be the same. You can
pass them around your application with a guarantee that the caller didn’t change anything.
This helps make programs easier to maintain. It also helps with performance by limiting
the number of copies, as you saw with String in Chapter 3, “Core Java APIs.”

One step in making a class immutable is to omit the setters. But wait: we still want the
caller to be able to specify the initial value—we just don’t want it to change after the object
is created. Constructors to the rescue!

public class ImmutableSwan {

private int numberEggs;

public ImmutableSwan(int numberEggs) {

this.numberEggs = numberEggs;

}

public int getNumberEggs() {

return numberEggs;

}}

In this example, we don't have a setter. We do have a constructor that allows a value to
be set. Remember, immutable is only measured after the object is constructed. Immutable
classes are allowed to have values. They just can't change after instantiation.

Return Types in Immutable Classes

When you are writing an immutable class, be careful about the return types. On the
surface, this class appears to be immutable since there is no setter:
public class NotImmutable {

private StringBuilder builder;
public NotImmutable(StringBuilder b) {

builder = b;
}
public StringBuilder getBuilder() {

return builder;
}}
The problem is that it isn’t really. Consider this code snippet:

continues

208 Chapter 4 ■ Methods and Encapsulation

continued
StringBuilder sb = new StringBuilder("initial");
NotImmutable problem = new NotImmutable(sb);
sb.append(" added");
StringBuilder gotBuilder = problem.getBuilder();
gotBuilder.append(" more");
System.out.println(problem.getBuilder());

This outputs "initial added more"—clearly not what we were intending. The problem
is that we are just passing the same StringBuilder all over. The caller has a reference since
it was passed to the constructor. Anyone who calls the getter gets a reference too.
A solution is to make a copy of the mutable object. This is called a defensive copy.

public Mutable(StringBuilder b) {
builder = new StringBuilder(b);

}
public StringBuilder getBuilder() {

return new StringBuilder(builder);
}

Now the caller can make changes to the initial sb object and it is fine. Mutable no longer
cares about that object after the constructor gets run. The same goes for the getter: call-
ers can change their StringBuilder without affecting Mutable.

Another approach for the getter is to return an immutable object:

public String getValue() {
return builder.toString();

}

There’s no rule that says we have to return the same type as we are storing. String is
safe to return because it is immutable in the first place.

To review, encapsulation refers to preventing callers from changing the instance variables
directly. Immutability refers to preventing callers from changing the instance variables at all.

Writing Simple Lambdas

Java is an object-oriented language at heart. You’ve seen plenty of objects by now. In Java
8, the language added the ability to write code using another style. Functional program-
ming is a way of writing code more declaratively. You specify what you want to do rather
than dealing with the state of objects. You focus more on expressions than loops.

Functional programming uses lambda expressions to write code. A lambda expression
is a block of code that gets passed around. You can think of a lambda expression as an

Writing Simple Lambdas 209

anonymous method. It has parameters and a body just like full-fledged methods do, but it
doesn’t have a name like a real method. Lambda expressions are often referred to as lamb-
das for short. You might also know them as closures if Java isn’t your first language. If you
had a bad experience with closures in the past, don’t worry. They are far simpler in Java.

In other words, a lambda expression is like a method that you can pass as if it were a
variable. For example, there are different ways to calculate age. One human year is equiva-
lent to seven dog years. You want to write a method that that takes an age() method as
input. To do this in an object-oriented program, you’d need to define a Human subclass and a
Dog subclass. With lambdas, you can just pass in the relevant expression to calculate age.

Lambdas allow you to write powerful code in Java. Only the simplest lambda expres-
sions are on the OCA exam. The goal is to get you comfortable with the syntax and the
concepts. This means you aren’t truly doing functional programming yet. You’ll see
lambdas again on the OCP exam.

In this section, we’ll cover an example of why lambdas are helpful, the syntax of
lambdas, and how to use predicates.

Lambda Example

Our goal is to print out all the animals in a list according to some criteria. We’ll show you
how to do this without lambdas to illustrate how lambdas are useful. We start out with the
Animal class:

public class Animal {

private String species;

private boolean canHop;

private boolean canSwim;

public Animal(String speciesName, boolean hopper, boolean swimmer) {

species = speciesName;

canHop = hopper;

canSwim = swimmer;

}

public boolean canHop() { return canHop; }

public boolean canSwim() { return canSwim; }

public String toString() { return species; }

}

The Animal class has three instance variables, which are set in the constructor. It has
two methods that get the state of whether the animal can hop or swim. It also has a
toString() method so we can easily identify the Animal in programs.

We plan to write a lot of different checks, so we want an interface. You’ll learn more
about interfaces in the next chapter. For now, it is enough to remember that an interface
specifies the methods that our class needs to implement:

public interface CheckTrait {

boolean test(Animal a);

}

210 Chapter 4 ■ Methods and Encapsulation

The first thing we want to check is whether the Animal can hop. We provide
a class that can check this:

public class CheckIfHopper implements CheckTrait {
public boolean test(Animal a) {
return a.canHop();
}

}

This class may seem simple—and it is. This is actually part of the problem that lambdas
solve. Just bear with us for a bit. Now we have everything that we need to write our code to
find the Animals that hop:

1: public class TraditionalSearch {

2: public static void main(String[] args) {

3: List<Animal> animals = new ArrayList<Animal>(); // list of animals

4: animals.add(new Animal("fish", false, true));

5: animals.add(new Animal("kangaroo", true, false));

6: animals.add(new Animal("rabbit", true, false));

7: animals.add(new Animal("turtle", false, true));

8:

9: print(animals, new CheckIfHopper()); // pass class that does check

10: }

11: private static void print(List<Animal> animals, CheckTrait checker) {

12: for (Animal animal : animals) {

13: if (checker.test(animal)) // the general check

14: System.out.print(animal + " ");

15: }

16: System.out.println();

17: }

18: }

The print() method on line 11 method is very general—it can check for any trait. This
is good design. It shouldn’t need to know what specifically we are searching for in order to
print a list of animals.

Now what happens if we want to print the Animals that swim? Sigh. We need to write
another class CheckIfSwims. Granted, it is only a few lines. Then we need to add a new line
under line 9 that instantiates that class. That’s two things just to do another check.

Why can’t we just specify the logic we care about right here? Turns out that we can with
lambda expressions. We could repeat that whole class here and make you find the one line
that changed. Instead, we’ll just show you. We could replace line 9 with the following, which
uses a lambda:

9: print(animals, a -> a.canHop());

Writing Simple Lambdas 211

Don’t worry that the syntax looks a little funky. You’ll get used to it and we’ll describe it
in the next section. We’ll also explain the bits that look like magic. For now, just focus on
how easy it is to read. We are telling Java that we only care about Animals that can hop.

It doesn’t take much imagination to figure how we would add logic to get the Animals
that can swim. We only have to add one line of code—no need for an extra class to do
something simple. Here’s that other line:

print(animals, a -> a.canSwim());

How about Animals that cannot swim?

print(animals, a -> ! a.canSwim());

The point here is that it is really easy to write code that uses lambdas once you get the
basics in place. This code uses a concept called deferred execution. Deferred execution
means that code is specified now but will run later. In this case, later is when the print()
method calls it.

Lambda Syntax

One of the simplest lambda expressions you can write is the one you just saw:

a -> a.canHop();

This means that Java should call a method with an Animal parameter that returns a
boolean value that’s the result of a.canHop(). We know all this because we wrote the code.
But how does Java know?

Java replies on context when figuring out what lambda expressions mean. We are pass-
ing this lambda as the second parameter of the print() method. That method expects a
CheckTrait as the second parameter. Since we are passing a lambda instead, Java tries to
map our lambda to that interface:

boolean test(Animal a);

Since that interface’s method takes an Animal, that means the lambda parameter has to
be an Animal. And since that interface’s method returns a boolean, we know the lambda
returns a boolean.

The syntax of lambdas is tricky because many parts are optional. These two lines do the
exact same thing:

a -> a.canHop()

(Animal a) -> { return a.canHop(); }

Let’s look at what is going on here. The first example, shown in Figure 4.5, has
three parts:
■ Specify a single parameter with the name a
■ The arrow operator to separate the parameter and body
■ A body that calls a single method and returns the result of that method

212 Chapter 4 ■ Methods and Encapsulation

F I G U R E 4 . 5 Lambda syntax omitting optional parts

parameter name body

a -> a.canHop()

arrow

The second example also has three parts; it’s just more verbose (see
Figure 4.6):
■ Specify a single parameter with the name a and stating the type is Animal

■ The arrow operator to separate the parameter and body

■ A body that has one or more lines of code, including a semicolon and a return statement

F I G U R E 4 . 6 Lambda syntax, including optional parts

parameter name body

(Animal a) -> { return a.canHop(); }

optional parameter type required because in block
arrow

The parentheses can only be omitted if there is a single parameter and its type is not
explicitly stated. Java does this because developers commonly use lambda expressions this
way and they can do as little typing as possible.

It shouldn’t be news to you that we can omit braces when we only have a single state-
ment. We did this with if statements and loops already. What is different here is that the
rules change when you omit the braces. Java doesn’t require you to type return or use
a semicolon when no braces are used. This special shortcut doesn’t work when we have
two or more statements. At least this is consistent with using {} to create blocks of code
elsewhere.

Let’s look at some examples of valid lambdas. Pretend that there are valid interfaces that
can consume a lambda with zero, one, or two String parameters.

3: print(() -> true); // 0 parameters
4: print(a -> a.startsWith("test")); // 1 parameter
5: print((String a) -> a.startsWith("test")); // 1 parameter
6: print((a, b) -> a.startsWith("test")); // 2 parameters
7: print((String a, String b) -> a.startsWith("test")); // 2 parameters

Writing Simple Lambdas 213

Notice that all of these examples have parentheses around the parameter list except the
one that takes only one parameter and doesn’t specify the type. Line 3 takes 0 parameters
and always returns the Boolean true. Line 4 takes one parameter and calls a method on
it, returning the result. Line 5 does the same except that it explicitly defines the type of the
variable. Lines 6 and 7 take two parameters and ignore one of them—there isn’t a rule that
says you must use all defined parameters.

Now let’s make sure you can identify invalid syntax. Do you see what’s wrong with each
of these?

print(a, b -> a.startsWith("test")); // DOES NOT COMPILE
print(a -> { a.startsWith("test"); }); // DOES NOT COMPILE
print(a -> { return a.startsWith("test") }); // DOES NOT COMPILE

The first line needs parentheses around the parameter list. Remember that the parenthe-
ses are only optional when there is one parameter and it doesn’t have a type declared. The
second line is missing the return keyword. The last line is missing the semicolon.

You might have noticed all of our lambdas return a boolean. That is because the scope
for the OCA exam limits what you need to learn.

What Variables Can My Lambda Access?

Lambdas are allowed to access variables. This topic isn’t on the OCA exam, but you may
come across it when practicing. Lambdas are allowed to access variables. Here’s an
example:

boolean wantWhetherCanHop = true;
print(animals, a -> a.canHop() == wantWhetherCanHop);

The trick is that they cannot access all variables. Instance and static variables are okay.
Method parameters and local variables are fine if they are not assigned new values.

There is one more issue you might see with lambdas. We’ve been defining an argument
list in our lambda expressions. Since Java doesn’t allow us to redeclare a local variable, the
following is an issue:

(a, b) -> { int a = 0; return 5;} // DOES NOT COMPILE

We tried to redeclare a, which is not allowed. By contrast, the following line is okay
because it uses a different variable name:

(a, b) -> { int c = 0; return 5;}

214 Chapter 4 ■ Methods and Encapsulation

Predicates

In our earlier example, we created an interface with one method:

boolean test(Animal a);

Lambdas work with interfaces that have only one method. These are called functional
interfaces—interfaces that can be used with functional programming. (It’s actually more
complicated than this, but for the OCA exam this definition is fine.)

You can imagine that we’d have to create lots of interfaces like this to use lambdas. We
want to test Animals and Strings and Plants and anything else that we come across.

Luckily, Java recognizes that this is a common problem and provides such an interface
for us. It’s in the package java.util.function and the gist of it is as follows:

public interface Predicate<T> {
boolean test(T t);

}

That looks a lot like our method. The only difference is that it uses this type T instead of
Animal. That’s the syntax for generics. It’s like when we created an ArrayList and got to
specify any type that goes in it.

This means we don’t need our own interface anymore and can put everything
related to our search in one class:

1: import java.util.*;
2: import java.util.function.*;
3: public class PredicateSearch {
4: public static void main(String[] args) {
5: List<Animal> animals = new ArrayList<Animal>();
6: animals.add(new Animal("fish", false, true));
7:
8: print(animals, a -> a.canHop());
9: }
10: private static void print(List<Animal> animals, Predicate<Animal>↵

checker) {
11: for (Animal animal : animals) {
12: if (checker.test(animal))
13: System.out.print(animal + " ");
14: }
15: System.out.println();
16: }
17: }

This time, line 10 is the only one that changed. We expect to have a
Predicate passed in that uses type Animal. Pretty cool. We can just use it with-
out having to write extra code.

Summary 215

Java 8 even integrated the Predicate interface into some existing classes. There is only
one you need to know for the exam. ArrayList declares a removeIf() method that takes a
Predicate. Imagine we have a list of names for pet bunnies. We decide we want to remove
all of the bunny names that don’t begin with the letter h because our little cousin really
wants us to choose an H name. We could solve this problem by writing a loop. Or we could
solve it in one line:

3: List<String> bunnies = new ArrayList<>();

4: bunnies.add("long ear");

5: bunnies.add("floppy");

6: bunnies.add("hoppy");

7: System.out.println(bunnies); // [long ear, floppy, hoppy]

8: bunnies.removeIf(s -> s.charAt(0) != 'h');

9: System.out.println(bunnies); // [hoppy]

Line 8 takes care of everything for us. It defines a predicate that takes a String and
returns a boolean. The removeIf() method does the rest.

For the OCA exam, you only need to know how to implement lambda expressions that
use the Predicate interface. Remember the one method in the interface called test()? It
takes any one reference type parameter and returns a boolean. Functional programming
is a large topic and just the basics are covered. On the OCP exam, you’ll learn how to get
rid of the loop entirely for more than just removeIf(). You’ll also learn the rules for imple-
menting your own functional interfaces as we did with CheckTrait.

Summary

As you learned in this chapter, Java methods start with an access modifier of public,
private, protected or blank (default access). This is followed by an optional specifier such
as static, final, or abstract. Next comes the return type, which is void or a Java type.
The method name follows, using standard Java identifier rules. Zero or more parameters go
in parentheses as the parameter list. Next come any optional exception types. Finally, zero
or more statements go in braces to make up the method body.

Using the private keyword means the code is only available from within the same class.
Default (package private) access means the code is only available from within the same
package. Using the protected keyword means the code is available from the same package
or subclasses. Using the public keyword means the code is available from anywhere. Static
methods and static variables are shared by the class. When referenced from outside the
class, they are called using the classname—for example, StaticClass.method(). Instance
members are allowed to call static members, but static members are not allowed to call
instance members. Static imports are used to import static members.

Java uses pass-by-value, which means that calls to methods create a copy of the parameters.
Assigning new values to those parameters in the method doesn’t affect the caller’s variables.

216 Chapter 4 ■ Methods and Encapsulation

Calling methods on objects that are method parameters changes the state of those objects and
is reflected in the caller.

Overloaded methods are methods with the same name but a different parameter list.
Java calls the most specific method it can find. Exact matches are preferred, followed by
wider primitives. After that comes autoboxing and finally varargs.

Constructors are used to instantiate new objects. The default no-argument constructor
is called when no constructor is coded. Multiple constructors are allowed and can call each
other by writing this(). If this() is present, it must be the first statement in the constructor.
Constructors can refer to instance variables by writing this before a variable name to indi-
cate they want the instance variable and not the method parameter with that name. The order
of initialization is the superclass (which we will cover in Chapter 5); static variables and static
initializers in the order they appear; instance variables and instance initializers in the order
they appear; and finally the constructor.

Encapsulation refers to preventing callers from changing the instance variables directly.
This is done by making instance variables private and getters/setters public. Immutability
refers to preventing callers from changing the instance variables at all. This uses several
techniques, including removing setters. JavaBeans use methods beginning with is and get
for boolean and non-boolean property types, respectively. Methods beginning with set are
used for setters.

Lambda expressions, or lambdas, allow passing around blocks of code. The full syntax
looks like (String a, String b) -> { return a.equals(b); }. The parameter types can
be omitted. When only one parameter is specified without a type, the parentheses can also
be omitted. The braces and return statement can be omitted for a single statement, making
the short form (a -> a.equals(b). Lambdas are passed to a method expecting an inter-
face with one method. Predicate is a common interface. It has one method named test
that returns a boolean and takes any type. The removeIf() method on ArrayList takes a
Predicate.

Exam Essentials

Be able to identify correct and incorrect method declarations. A sample method signature
is public static void method(String... args) throws Exception {}.

Identify when a method or field is accessible. Recognize when a method or field is
accessed when the access modifier (private, protected, public, or default access) does not
allow it.

Recognize valid and invalid uses of static imports. Static imports import static members.
They are written as import static, not static import. Make sure they are importing static
methods or variables rather than classnames.

State the output of code involving methods. Identify when to call static rather than
instance methods based on whether the classname or object comes before the method.

Exam Essentials 217

Recognize the correct overloaded method. Exact matches are used first, followed by wider
primitives, followed by autoboxing, followed by varargs. Assigning new values to method
parameters does not change the caller, but calling methods on them does.

Evaluate code involving constructors. Constructors can call other constructors by call-
ing this() as the first line of the constructor. Recognize when the default constructor is
provided. Remember the order of initialization is the superclass, static variables/initializers,
instance variables/initializers, and the constructor.

Be able to recognize when a class is properly encapsulated. Look for private instance
variables and public getters and setters when identifying encapsulation.

Write simple lambda expressions. Look for the presence or absence of optional elements
in lambda code. Parameter types are optional. Braces and the return keyword are optional
when the body is a single statement. Parentheses are optional when only one parameter is
specified and the type is implicit. The Predicate interface is commonly used with lambdas
because it declares a single method called test(), which takes one parameter.

218 Chapter 4 ■ Methods and Encapsulation

Review Questions

1. Which of the following can fill in the blank in this code to make it compile? (Choose all
that apply)
public class Ant {
_____ void method() { }
}
A. default
B. final
C. private
D. Public
E. String
F. zzz:

2. Which of the following compile? (Choose all that apply)
A. final static void method4() { }
B. public final int void method() { }
C. private void int method() { }
D. static final void method3() { }
E. void final method() {}
F. void public method() { }

3. Which of the following methods compile? (Choose all that apply)
A. public void methodA() { return;}
B. public void methodB() { return null;}
C. public void methodD() {}
D. public int methodD() { return 9;}
E. public int methodE() { return 9.0;}
F. public int methodF() { return;}
G. public int methodG() { return null;}

4. Which of the following compile? (Choose all that apply)
A. public void moreA(int... nums) {}
B. public void moreB(String values, int... nums) {}
C. public void moreC(int... nums, String values) {}
D. public void moreD(String... values, int... nums) {}
E. public void moreE(String[] values, ...int nums) {}
F. public void moreF(String... values, int[] nums) {}
G. public void moreG(String[] values, int[] nums) {}

Review Questions 219

5. Given the following method, which of the method calls return 2? (Choose all that apply)
public int howMany(boolean b, boolean... b2) {
return b2.length;
}
A. howMany();
B. howMany(true);
C. howMany(true, true);
D. howMany(true, true, true);
E. howMany(true, {true});
F. howMany(true, {true, true});
G. howMany(true, new boolean[2]);

6. Which of the following are true? (Choose all that apply)
A. Package private access is more lenient than protected access.
B. A public class that has private fields and package private methods is not visible to
classes outside the package.
C. You can use access modifiers so only some of the classes in a package see a particular
package private class.
D. You can use access modifiers to allow read access to all methods, but not any instance
variables.
E. You can use access modifiers to restrict read access to all classes that begin with the
word Test.

7. Given the following my.school.ClassRoom and my.city.School class definitions, which
line numbers in main() generate a compiler error? (Choose all that apply)
1: package my.school;
2: public class Classroom {
3: private int roomNumber;
4: protected String teacherName;
5: static int globalKey = 54321;
6: public int floor = 3;
7: Classroom(int r, String t) {
8: roomNumber = r;
9: teacherName = t; } }

1: package my.city;
2: import my.school.*;
3: public class School {
4: public static void main(String[] args) {
5: System.out.println(Classroom.globalKey);
6: Classroom room = new Classroom(101, ""Mrs. Anderson");

220 Chapter 4 ■ Methods and Encapsulation

7: System.out.println(room.roomNumber);
8: System.out.println(room.floor);
9: System.out.println(room.teacherName); } }
A. None, the code compiles fine.
B. Line 5
C. Line 6
D. Line 7
E. Line 8
F. Line 9

8. Which of the following are true? (Choose all that apply)
A. Encapsulation uses package private instance variables.
B. Encapsulation uses private instance variables.
C. Encapsulation allows setters.
D. Immutability uses package private instance variables.
E. Immutability uses private instance variables.
F. Immutability allows setters.

9. Which are methods using JavaBeans naming conventions for accessors and mutators?
(Choose all that apply)
A. public boolean getCanSwim() { return canSwim;}
B. public boolean canSwim() { return numberWings;}
C. public int getNumWings() { return numberWings;}
D. public int numWings() { return numberWings;}
E. public void setCanSwim(boolean b) { canSwim = b;}

10. What is the output of the following code?
1: package rope;
2: public class Rope {
3: public static int LENGTH = 5;
4: static {
5: LENGTH = 10;
6: }

Review Questions 221

7: public static void swing() {
8: System.out.print("swing ");
9: }
10: }

1: import rope.*;
2: import static rope.Rope.*;
3: public class Chimp {
4: public static void main(String[] args) {
5: Rope.swing();
6: new Rope().swing();
7: System.out.println(LENGTH);
8: }
9: }

A. swing swing 5
B. swing swing 10
C. Compiler error on line 2 of Chimp.
D. Compiler error on line 5 of Chimp.
E. Compiler error on line 6 of Chimp.
F. Compiler error on line 7 of Chimp.

11. Which are true of the following code? (Choose all that apply)
1: public class Rope {
2: public static void swing() {
3: System.out.print("swing ");
4: }
5: public void climb() {
6: System.out.println("climb ");
7: }
8: public static void play() {
9: swing();
10: climb();
11: }
12: public static void main(String[] args) {
13: Rope rope = new Rope();
14: rope.play();
15: Rope rope2 = null;
16: rope2.play();
17: }
18: }

222 Chapter 4 ■ Methods and Encapsulation

A. The code compiles as is.
B. There is exactly one compiler error in the code.
C. There are exactly two compiler errors in the code.
D. If the lines with compiler errors are removed, the output is climb climb.
E. If the lines with compiler errors are removed, the output is swing swing.
F. If the lines with compile errors are removed, the code throws a NullPointerException.

12. What is the output of the following code?
import rope.*;
import static rope.Rope.*;
public class RopeSwing {
private static Rope rope1 = new Rope();
private static Rope rope2 = new Rope();
{
System.out.println(rope1.length);
}
public static void main(String[] args) {
rope1.length = 2;
rope2.length = 8;
System.out.println(rope1.length);
}
}

package rope;
public class Rope {

public static int length = 0;
}
A. 02
B. 08
C. 2
D. 8
E. The code does not compile.
F. An exception is thrown.

13. How many compiler errors are in the following code?
1: public class RopeSwing {
2: private static final String leftRope;
3: private static final String rightRope;
4: private static final String bench;
5: private static final String name = "name";

Review Questions 223

6: static {
7: leftRope = "left";
8: rightRope = "right";
9: }
10: static {
11: name = "name";
12: rightRope = "right";
13: }
14: public static void main(String[] args) {
15: bench = "bench";
16: }
17: }
A. 0
B. 1
C. 2
D. 3
E. 4
F. 5

14. Which of the following can replace line 2 to make this code compile? (Choose
all that apply)
1: import java.util.*;
2: // INSERT CODE HERE
3: public class Imports {
4: public void method(ArrayList<String> list) {
5: sort(list);
6: }
7: }
A. import static java.util.Collections;
B. import static java.util.Collections.*;
C. import static java.util.Collections.sort(ArrayList<String>);
D. static import java.util.Collections;
E. static import java.util.Collections.*;
F. static import java.util.Collections.sort(ArrayList<String>);

15. What is the result of the following statements?
1: public class Test {
2: public void print(byte x) {
3: System.out.print("byte");
4: }
5: public void print(int x) {
6: System.out.print("int");

224 Chapter 4 ■ Methods and Encapsulation

7: }
8: public void print(float x) {
9: System.out.print("float");
10: }
11: public void print(Object x) {
12: System.out.print("Object");
13: }
14: public static void main(String[] args) {
15: Test t = new Test();
16: short s = 123;
17: t.print(s);
18: t.print(true);
19: t.print(6.789);
20: }
21: }
A. bytefloatObject
B. intfloatObject
C. byteObjectfloat
D. intObjectfloat
E. intObjectObject
F. byteObjectObject

16. What is the result of the following program?
1: public class Squares {
2: public static long square(int x) {
3: long y = x * (long) x;
4: x = -1;
5: return y;
6: }
7: public static void main(String[] args) {
8: int value = 9;
9: long result = square(value);
10: System.out.println(value);
11: } }
A. -1
B. 9
C. 81
D. Compiler error on line 9.
E. Compiler error on a different line.

Review Questions 225

17. Which of the following are output by the following code? (Choose all that apply)
public class StringBuilders {
public static StringBuilder work(StringBuilder a,
StringBuilder b) {
a = new StringBuilder("a");
b.append("b");
return a;
}
public static void main(String[] args) {
StringBuilder s1 = new StringBuilder("s1");
StringBuilder s2 = new StringBuilder("s2");
StringBuilder s3 = work(s1, s2);
System.out.println("s1 = " + s1);
System.out.println("s2 = " + s2);
System.out.println("s3 = " + s3);
}
}
A. s1 = a
B. s1 = s1
C. s2 = s2
D. s2 = s2b
E. s3 = a
F. s3 = null
G. The code does not compile.

18. Which of the following are true? (Choose 2)
A. this() can be called from anywhere in a constructor.
B. this() can be called from any instance method in the class.
C. this.variableName can be called from any instance method in the class.
D. this.variableName can be called from any static method in the class.
E. You must include a default constructor in the code if the compiler does not include one.
F. You can call the default constructor written by the compiler using this().
G. You can access a private constructor with the main() method.

19. Which of these classes compile and use a default constructor? (Choose all that apply)
A. public class Bird { }
B. public class Bird { public bird() {} }
C. public class Bird { public bird(String name) {} }
D. public class Bird { public Bird() {} }

226 Chapter 4 ■ Methods and Encapsulation

E. public class Bird { Bird(String name) {} }
F. public class Bird { private Bird(int age) {} }
G. public class Bird { void Bird() { }

20. Which code can be inserted to have the code print 2?
public class BirdSeed {
private int numberBags;
boolean call;

public BirdSeed() {
// LINE 1
call = false;
// LINE 2

}
public BirdSeed(int numberBags) {

this.numberBags = numberBags;
}
public static void main(String[] args) {

BirdSeed seed = new BirdSeed();
System.out.println(seed.numberBags);
}}
A. Replace line 1 with BirdSeed(2);
B. Replace line 2 with BirdSeed(2);
C. Replace line 1 with new BirdSeed(2);
D. Replace line 2 with new BirdSeed(2);
E. Replace line 1 with this(2);
F. Replace line 2 with this(2);

21. Which of the following complete the constructor so that this code prints out 50? (Choose
all that apply)
public class Cheetah {
int numSpots;
public Cheetah(int numSpots) {
// INSERT CODE HERE
}
public static void main(String[] args) {
System.out.println(new Cheetah(50).numSpots);
}
}

Review Questions 227

A. numSpots = numSpots;
B. numSpots = this.numSpots;
C. this.numSpots = numSpots;
D. numSpots = super.numSpots;
E. super.numSpots = numSpots;
F. None of the above.

22. What is the result of the following?
1: public class Order {
2: static String result = "";
3: { result += "c"; }
4: static
5: { result += "u"; }
6: { result += "r"; }
7: }

1: public class OrderDriver {
2: public static void main(String[] args) {
3: System.out.print(Order.result + " ");
4: System.out.print(Order.result + " ");
5: new Order();
6: new Order();
7: System.out.print(Order.result + " ");
8: }
9: }
A. curur
B. ucrcr
C. u ucrcr
D. u u curcur
E. u u ucrcr
F. ur ur urc
G. The code does not compile.

23. What is the result of the following?
1: public class Order {
2: String value = "t";
3: { value += "a"; }
4: { value += "c"; }
5: public Order() {

228 Chapter 4 ■ Methods and Encapsulation

6: value += "b";
7: }
8: public Order(String s) {
9: value += s;
10: }
11: public static void main(String[] args) {
12: Order order = new Order("f");
13: order = new Order();
14: System.out.println(order.value);
15: } }
A. tacb
B. tacf
C. tacbf
D. tacfb
E. tacftacb
F. The code does not compile.
G. An exception is thrown.

24. Which of the following will compile when inserted in the following code? (Choose
all that apply)
public class Order3 {
final String value1 = "1";
static String value2 = "2";
String value3 = "3";
{
// CODE SNIPPET 1
}
static {
// CODE SNIPPET 2
}
}
A. value1 = "d"; instead of // CODE SNIPPET 1
B. value2 = "e"; instead of // CODE SNIPPET 1
C. value3 = "f"; instead of // CODE SNIPPET 1
D. value1 = "g"; instead of // CODE SNIPPET 2
E. value2 = "h"; instead of // CODE SNIPPET 2
F. value3 = "i"; instead of // CODE SNIPPET 2

Review Questions 229

25. Which of the following are true about the following code? (Choose all that apply)
public class Create {
Create() {
System.out.print("1 ");
}
Create(int num) {
System.out.print("2 ");
}
Create(Integer num) {
System.out.print("3 ");
}
Create(Object num) {
System.out.print("4 ");
}
Create(int... nums) {
System.out.print("5 ");
}
public static void main(String[] args) {
new Create(100);
new Create(1000L);
}
}
A. The code prints out 2 4.
B. The code prints out 3 4.
C. The code prints out 4 2.
D. The code prints out 4 4.
E. The code prints 3 4 if you remove the constructor Create(int num).
F. The code prints 4 4 if you remove the constructor Create(int num).
G. The code prints 5 4 if you remove the constructor Create(int num).

26. What is the result of the following class?
1: import java.util.function.*;
2:
3: public class Panda {
4: int age;
5: public static void main(String[] args) {
6: Panda p1 = new Panda();
7: p1.age = 1;
8: check(p1, p -> p.age < 5);

230 Chapter 4 ■ Methods and Encapsulation

9: }
10: private static void check(Panda panda, Predicate<Panda> pred) {
11: String result = pred.test(panda) ? "match" : "not match";
12: System.out.print(result);
13: } }
A. match
B. not match
C. Compiler error on line 8.
D. Compiler error on line 10.
E. Compiler error on line 11.
F. A runtime exception is thrown.

27. What is the result of the following code?
1: interface Climb {
2: boolean isTooHigh(int height, int limit);
3: }
4:
5: public class Climber {
6: public static void main(String[] args) {
7: check((h, l) -> h.append(l).isEmpty(), 5);
8: }
9: private static void check(Climb climb, int height) {
10: if (climb.isTooHigh(height, 10))
11: System.out.println("too high");
12: else
13: System.out.println("ok");
14: }
15: }
A. ok
B. too high
C. Compiler error on line 7.
D. Compiler error on line 10.
E. Compiler error on a different line.
F. A runtime exception is thrown.

28. Which of the following lambda expressions can fill in the blank? (Choose all that apply)
List<String> list = new ArrayList<>();
list.removeIf(___________________);

Review Questions 231

A. s -> s.isEmpty()
B. s -> {s.isEmpty()}
C. s -> {s.isEmpty();}
D. s -> {return s.isEmpty();}
E. String s -> s.isEmpty()
F. (String s) -> s.isEmpty()

29. Which lambda can replace the MySecret class to return the same value? (Choose
all that apply)
interface Secret {
String magic(double d);
}

class MySecret implements Secret {
public String magic(double d) {
return "Poof";
}

}
A. caller((e) -> "Poof");
B. caller((e) -> {"Poof"});
C. caller((e) -> { String e = ""; "Poof" });
D. caller((e) -> { String e = ""; return "Poof"; });
E. caller((e) -> { String e = ""; return "Poof" });
F. caller((e) -> { String f = ""; return "Poof"; });



Chapter Class Design

5 OCA EXAM OBJECTIVES COVERED IN THIS
CHAPTER:

✓ Working with Inheritance
■ Describe inheritance and its benefits
■ Develop code that demonstrates the use of polymorphism;
including overriding and object type versus reference type
■ Determine when casting is necessary
■ Use super and this to access objects and constructors
■ Use abstract classes and interfaces

In Chapter 1, “Java Building Blocks,” we introduced the
basic definition for a class in Java. In Chapter 4, “Methods
and Encapsulation,” we delved into constructors, methods,
and modifiers, and showed how you can use them to build more structured classes. In this
chapter, we’ll take things one step further and show how class structure is one of the most
powerful features in the Java language.
At its core, proper Java class design is about code reusability, increased functionality,
and standardization. For example, by creating a new class that extends an existing class,
you may gain access to a slew of inherited primitives, objects, and methods. Alternatively,
by designing a standard interface for your application, you ensure that any class that imple-
ments the interface has certain required methods defined. Finally, by creating abstract class
definitions, you’re defining a platform that other developers can extend and build on top of.

Introducing Class Inheritance

When creating a new class in Java, you can define the class to inherit from an existing class.
Inheritance is the process by which the new child subclass automatically includes any
public or protected primitives, objects, or methods defined in the parent class.

For illustrative purposes, we refer to any class that inherits from another class as a child
class, or a descendent of that class. Alternatively, we refer to the class that the child inherits
from as the parent class, or an ancestor of the class. If child X inherits from class Y, which
in turn inherits from class Z, then X would be considered an indirect child, or descendent,
of class Z.

Java supports single inheritance, by which a class may inherit from only one direct par-
ent class. Java also supports multiple levels of inheritance, by which one class may extend
another class, which in turn extends another class. You can extend a class any number of
times, allowing each descendent to gain access to its ancestor’s members.

To truly understand single inheritance, it may helpful to contrast it with multiple inheri-
tance, by which a class may have multiple direct parents. By design, Java doesn’t support
multiple inheritance in the language because studies have shown that multiple inheritance
can lead to complex, often difficult-to-maintain code. Java does allow one exception to the
single inheritance rule: classes may implement multiple interfaces, as you’ll see later in this
chapter.

Figure 5.1 illustrates the various types of inheritance models. The items on the left are
considered single inheritance because each child has exactly one parent. You may notice
that single inheritance doesn’t preclude parents from having multiple children. The right

Introducing Class Inheritance 235

side shows items that have multiple inheritance. For example, a dog object has multiple par-
ent designations. Part of what makes multiple inheritance complicated is determining which
parent to inherit values from in case of a conflict. For example, if you have an object or
method defined in all of the parents, which one does the child inherit? There is no natural
ordering for parents in this example, which is why Java avoids these issues by disallowing
multiple inheritance altogether.

F I G U R E 5 .1 Types of inheritance

Animal Animal Pet Friendly

Mammal Bird Dog

Bat Tiger Parrot Eagle Husky Poodle

Single Inheritance Multiple Inheritance

It is possible in Java to prevent a class from being extended by marking the class with
the final modifier. If you try to define a class that inherits from a final class, the compiler
will throw an error and not compile. Unless otherwise specified, throughout this chapter
you can assume the classes we work with are not marked as final.

Extending a Class

In Java, you can extend a class by adding the parent class name in the definition using the
extends keyword. The syntax of defining and extending a class is shown in Figure 5.2.

F I G U R E 5 . 2 Defining and extending a class

public or default access modifier class name

abstract or final keyword (optional) extends parent class (optional)
class keyword (required)

public abstract class ElephantSeal extends Seal {

// Methods and Variables defined here

}

We’ll discuss what it means for a class to be abstract and final, as well as the class
access modifiers, later in this chapter.

236 Chapter 5 ■ Class Design

Because Java allows only one public class per file, we can create two files, Animal.java
and Lion.java, in which the Lion class extends the Animal class. Assuming they are in the
same package, an import statement is not required in Lion.java to access the Animal class.

Here are the contents of Animal.java:

public class Animal {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}

}

And here are the contents of Lion.java:

public class Lion extends Animal {
private void roar() {
System.out.println("The "+getAge()+" year old lion says: Roar!");
}

}

Notice the use of the extends keyword in Lion.java to indicate that the Lion class
extends from the Animal class. In this example, we see that getAge() and setAge() are
accessible by subclass Lion, because they are marked as public in the parent class. The
primitive age is marked as private and therefore not accessible from the subclass Lion, as
the following would not compile:

public class Lion extends Animal {
private void roar() {
System.out.println("The "+age+" year old lion says: Roar!");
// DOES NOT COMPILE
}

}

Despite the fact that age is inaccessible by the child class, if we have an instance of a
Lion object, there is still an age value that exists within the instance. The age value just
cannot be directly referenced by the child class nor any instance of the class. In this man-
ner, the Lion object is actually “bigger” than the Animal object in the sense that it includes
all the properties of the Animal object (although not all of those properties may be directly
accessible) along with its own set of Lion attributes.

Introducing Class Inheritance 237

Applying Class Access Modifiers

As discussed in Chapter 4, you can apply access modifiers (public, private, protected,
default) to both class methods and variables. It probably comes as no surprise that you can
also apply access modifiers to class definitions, since we have been adding the public access
modifier to nearly every class up to now.

For the OCA exam, you should only be familiar with public and default
package-level class access modifiers, because these are the only ones that
can be applied to top-level classes within a Java file. The protected and
private modifiers can only be applied to inner classes, which are classes
that are defined within other classes, but this is well out of scope for the
OCA exam.

The public access modifier applied to a class indicates that it can be referenced and used
in any class. The default package private modifier, which is the lack of any access modifier,
indicates the class can be accessed only by a subclass or class within the same package.

As you know, a Java file can have many classes but at most one public class. In fact, it
may have no public class at all. One feature of using the default package private modifier
is that you can define many classes within the same Java file. For example, the following
definition could appear in a single Java file named Groundhog.java, since it contains only
one public class:

class Rodent {}

public class Groundhog extends Rodent {}

If we were to update the Rodent class with the public access modifier, the Groundhog.java
file would not compile unless the Rodent class was moved to its own Rodent.java file.

The rules for applying class access modifiers are identical for interfaces. There can be at
most one public class or interface in a Java file. Like classes, top-level interfaces can also
be declared with the public or default modifiers. We’ll discuss interfaces in detail later in
this chapter.

For simplicity, any time you see multiple public classes or interfaces
defined in the same code block in this chapter, assume each class is
defined in its own Java file.

Creating Java Objects

Throughout our discussion of Java in this book, we have thrown around the word object
numerous times—and with good reason. In Java, all classes inherit from a single class,

238 Chapter 5 ■ Class Design

java.lang.Object. Furthermore, java.lang.Object is the only class that doesn’t have any
parent classes.

You might be wondering, “None of the classes I’ve written so far extend java.lang
.Object, so how do all classes inherit from it?” The answer is that the compiler has been
automatically inserting code into any class you write that doesn’t extend a specific class.
For example, consider the following two equivalent class definitions:
public class Zoo {
}

public class Zoo extends java.lang.Object {
}

The key is that when Java sees you define a class that doesn’t extend another class, it
immediately adds the syntax extends java.lang.Object to the class definition.

If you define a new class that extends an existing class, Java doesn’t add this syntax,
although the new class still inherits from java.lang.Object. Since all classes inherit from
java.lang.Object, extending an existing class means the child automatically inherits from
java.lang.Object by construction. This means that if you look at the inheritance structure
of any class, it will always end with java.lang.Object on the top of the tree, as shown in
Figure 5.3.

F I G U R E 5 . 3 Java object inheritance

java.lang.Object



Mammal

Ox

All objects inherit java.lang.Object

Defining Constructors

As you may recall from Chapter 4, every class has at least one constructor. In the case that
no constructor is declared, the compiler will automatically insert a default no-argument
constructor. In the case of extending a class, though, things are a bit more interesting.

In Java, the first statement of every constructor is either a call to another constructor
within the class, using this(), or a call to a constructor in the direct parent class, using

Introducing Class Inheritance 239

super(). If a parent constructor takes arguments, the super constructor would also take
arguments. For simplicity in this section, we refer to the super() command as any par-
ent constructor, even those that take an argument. Notice the user of both super() and
super(age) in the following example:

public class Animal {
private int age;
public Animal(int age) {
super();
this.age = age;
}

}

public class Zebra extends Animal {
public Zebra(int age) {
super(age);
}
public Zebra() {
this(4);
}

}

In the first class, Animal, the first statement of the constructor is a call to the parent
constructor defined in java.lang.Object, which takes no arguments. In the second class,
Zebra, the first statement of the first constructor is a call to Animal’s constructor, which
takes a single argument. The class Zebra also includes a second no-argument construc-
tor that doesn’t call super() but instead calls the other constructor within the Zebra class
using this(4).

Like the this() command that you saw in Chapter 4, the super() command may only
be used as the first statement of the constructor. For example, the following two class defi-
nitions will not compile:

public class Zoo {
public Zoo() {
System.out.println("Zoo created");
super(); // DOES NOT COMPILE
}

}

public class Zoo {
public Zoo() {
super();
System.out.println("Zoo created");

240 Chapter 5 ■ Class Design

super(); // DOES NOT COMPILE
}
}

The first class will not compile because the call to the parent constructor must be the
first statement of the constructor, not the second statement. In the second code snippet,
super() is the first statement of the constructor, but it also used as the third statement.
Since super() can only be used as the first statement of the constructor, the code will like-
wise not compile.

If the parent class has more than one constructor, the child class may use any valid
parent constructor in its definition, as shown in the following example:

public class Animal {
private int age;
private String name;
public Animal(int age, String name) {
super();
this.age = age;
this.name = name;
}
public Animal(int age) {
super();
this.age = age;
this.name = null;
}

}

public class Gorilla extends Animal {
public Gorilla(int age) {
super(age,"Gorilla");
}
public Gorilla() {
super(5);
}

}

In this example, the first child constructor takes one argument, age, and calls the par-
ent constructor, which takes two arguments, age and name. The second child constructor
takes no arguments, and it calls the parent constructor, which takes one argument, age.
In this example, notice that the child constructors are not required to call matching parent
constructors. Any valid parent constructor is acceptable as long as the appropriate input
parameters to the parent constructor are provided.

Introducing Class Inheritance 241

Understanding Compiler Enhancements

Up to now, we defined numerous classes that did not explicitly call the parent construc-
tor via the super() keyword, so why did the code compile? The answer is that the Java
compiler automatically inserts a call to the no-argument constructor super() if the first
statement is not a call to the parent constructor. For example, the following three class
and constructor definitions are equivalent, because the compiler will automatically convert
them all to the last example:

public class Donkey {
}

public class Donkey {
public Donkey() {
}

}

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

}

Make sure you understand the differences between these three Donkey class definitions
and why Java will automatically convert them all to the last definition. Keep the process the
Java compile performs in mind as we discuss the next few examples.

What happens if the parent class doesn’t have a no-argument constructor? Recall that
the no-argument constructor is not required and only inserted if there is no constructor
defined in the class. In this case, the Java compiler will not help and you must create at least
one constructor in your child class that explicitly calls a parent constructor via the super()
command. For example, the following code will not compile:

public class Mammal {
public Mammal(int age) {
}

}

public class Elephant extends Mammal { // DOES NOT COMPILE
}

In this example no constructor is defined within the Elephant class, so the compiler tries
to insert a default no-argument constructor with a super() call, as it did in the Donkey
example. The compiler stops, though, when it realizes there is no parent constructor that
takes no arguments. In this example, we must explicitly define at least one constructor, as
in the following code:

242 Chapter 5 ■ Class Design

public class Mammal {
public Mammal(int age) {
}

}

public class Elephant extends Mammal {
public Elephant() { // DOES NOT COMPILE
}

}

This code still doesn’t compile, though, because the compiler tries to insert the no-
argument super() as the first statement of the constructor in the Elephant class, and there
is no such constructor in the parent class. We can fix this, though, by adding a call to a par-
ent constructor that takes a fixed argument:

public class Mammal {
public Mammal(int age) {
}

}

public class Elephant extends Mammal {
public Elephant() {
super(10);
}

}

This code will compile because we have added a constructor with an explicit call to a
parent constructor. Note that the class Elephant now has a no-argument constructor even
though its parent class Mammal doesn’t. Subclasses may define no-argument constructors
even if their parent classes do not, provided the constructor of the child maps to a parent
constructor via an explicit call of the super() command.

You should be wary of any exam question in which the parent class defines a constructor
that takes arguments and doesn’t define a no-argument constructor. Be sure to check that
the code compiles before answering a question about it.

Reviewing Constructor Rules

Let’s review the rules we covered in this section.

Constructor Definition Rules:

1. The first statement of every constructor is a call to another constructor within the class
using this(), or a call to a constructor in the direct parent class using super().

2. The super() call may not be used after the first statement of the constructor.

Introducing Class Inheritance 243

3. If no super() call is declared in a constructor, Java will insert a no-argument super()
as the first statement of the constructor.

4. If the parent doesn’t have a no-argument constructor and the child doesn’t define any
constructors, the compiler will throw an error and try to insert a default no-argument
constructor into the child class.

5. If the parent doesn’t have a no-argument constructor, the compiler requires an explicit
call to a parent constructor in each child constructor.

Make sure you understand these rules; the exam will often provide code that breaks one
or many of these rules and therefore doesn’t compile.

Calling Constructors

Now that we have covered how to define a valid constructor, we’ll show you how Java calls
the constructors. In Java, the parent constructor is always executed before the child con-
structor. For example, try to determine what the following code outputs:

class Primate {
public Primate() {
System.out.println("Primate");
}

}

class Ape extends Primate {
public Ape() {
System.out.println("Ape");
}

}

public class Chimpanzee extends Ape {
public static void main(String[] args) {
new Chimpanzee();
}

}

The compiler first inserts the super() command as the first statement of both the
Primate and Ape constructors. Next, the compiler inserts a default no-argument construc-
tor in the Chimpanzee class with super() as the first statement of the constructor. The code
will execute with the parent constructors called first and yields the following output:

Primate
Ape

244 Chapter 5 ■ Class Design

The exam creators are fond of questions similar to the previous one that try to get
you to determine the output of statements involving constructors. Just remember to
“think like the compiler” as much as possible and insert the missing constructors or
statements as needed.

Calling Inherited Class Members

Java classes may use any public or protected member of the parent class, including meth-
ods, primitives, or object references. If the parent class and child class are part of the same
package, the child class may also use any default members defined in the parent class.
Finally, a child class may never access a private member of the parent class, at least not
through any direct reference. As you saw in the first example in this chapter, a private
member age may be accessed indirectly via a public or protected method.

To reference a member in a parent class, you can just call it directly, as in the following
example with the output function displaySharkDetails():
class Fish {

protected int size;
private int age;

public Fish(int age) {
this.age = age;

}

public int getAge() {
return age;

}
}

public class Shark extends Fish {

private int numberOfFins = 8;

public Shark(int age) {
super(age);
this.size = 4;

}

public void displaySharkDetails() {
System.out.print("Shark with age: "+getAge());

Introducing Class Inheritance 245

System.out.print(" and "+size+" meters long");
System.out.print(" with "+numberOfFins+" fins");
}
}

In the child class, we use the public method getAge() and protected member size to
access values in the parent class.

As you may remember from Chapter 4, you can use the keyword this to access a mem-
ber of the class. You may also use this to access members of the parent class that are acces-
sible from the child class, since a child class inherits all of its parent members. Consider
the following alternative definition to the displaySharkDetails() method in the previous
example:

public void displaySharkDetails() {
System.out.print("Shark with age: "+this.getAge());
System.out.print(" and "+this.size+" meters long");
System.out.print(" with "+this.numberOfFins+" fins");

}

In Java, you can explicitly reference a member of the parent class by using the super key-
word, as in the following alternative definition of displaySharkDetails():

public void displaySharkDetails() {
System.out.print("Shark with age: "+super.getAge());
System.out.print(" and "+super.size+" meters long");
System.out.print(" with "+this.numberOfFins+" fins");

}

In the previous example, we could use this or super to access a member of the parent
class, but is the same true for a member of the child class? Consider this example:

public void displaySharkDetails() {
System.out.print("Shark with age: "+super.getAge());
System.out.print(" and "+super.size+" meters long");
System.out.print(" with "+super.numberOfFins+" fins"); // DOES NOT COMPILE

}

This code will not compile because numberOfFins is only a member of the current class,
not the parent class. In other words, we see that this and super may both be used for
methods or variables defined in the parent class, but only this may be used for members
defined in the current class.

As you’ll see in the next section, if the child class overrides a member of the parent class,
this and super could have very different effects when applied to a class member.

246 Chapter 5 ■ Class Design

super() vs. super

As discussed in Chapter 4, this() and this are unrelated in Java. Likewise, super() and
super are quite different but may be used in the same methods on the exam. The first,
super(), is a statement that explicitly calls a parent constructor and may only be used in
the first line of a constructor of a child class. The second, super, is a keyword used to ref-
erence a member defined in a parent class and may be used throughout the child class.

The exam may try to trick you by using both super() and super in a constructor. For
example, consider the following code:

public Rabbit(int age) {
super();
super.setAge(10);

}

The first statement of the constructor calls the parent’s constructor, whereas the second
statement calls a function defined in the parent class. Contrast this with the following
code, which doesn’t compile:

public Rabbit(int age) {
super; // DOES NOT COMPILE
super().setAge(10); // DOES NOT COMPILE

}

This code looks similar to the previous example, but neither line of the constructor will
compile since they are using the keywords incorrectly. When you see super() or super
on the exam, be sure to check that they are being used correctly.

Inheriting Methods

Inheriting a class grants us access to the public and protected members of the parent
class, but also sets the stage for collisions between methods defined in both the parent class
and the subclass. In this section, we’ll review the rules for method inheritance and how
Java handles such scenarios.

Overriding a Method

What if there is a method defined in both the parent and child class? For example, you may
want to define a new version of an existing method in a child class that makes use of the
definition in the parent class. In this case, you can override a method a method by declar-
ing a new method with the signature and return type as the method in the parent class. As
you may recall from Chapter 4, the method signature includes the name and list of input
parameters.

Introducing Class Inheritance 247

When you override a method, you may reference the parent version of the method
using the super keyword. In this manner, the keywords this and super allow you to select
between the current and parent version of a method, respectively. We illustrate this with the
following example:

public class Canine {
public double getAverageWeight() {
return 50;
}

}

public class Wolf extends Canine {
public double getAverageWeight() {
return super.getAverageWeight()+20;
}
public static void main(String[] args) {
System.out.println(new Canine().getAverageWeight());
System.out.println(new Wolf().getAverageWeight());
}

}

In this example, in which the child class Wolf overrides the parent class Canine, the
method getAverageWeight() runs without issue and outputs the following:

50.00
70.00

You might be wondering, was the use of super in the child’s method required? For
example, what would the following code output if we removed the super keyword in the
getAverageWeight() method of the Wolf class?

public double getAverageWeight() {
return getAverageWeight()+20; // INFINITE LOOP

}

In this example, the compiler would not call the parent Canine method; it would call the
current Wolf method since it would think you were executing a recursive call. A recursive
function is one that calls itself as part of execution, and it is common in programming. A
recursive function must have a termination condition. In this example, there is no termina-
tion condition; therefore, the application will attempt to call itself infinitely and produce a
stack overflow error at runtime.

Overriding a method is not without limitations, though. The compiler performs the fol-
lowing checks when you override a nonprivate method:
1. The method in the child class must have the same signature as the method in the parent

class.

248 Chapter 5 ■ Class Design

2. The method in the child class must be at least as accessible or more accessible than the
method in the parent class.

3. The method in the child class may not throw a checked exception that is new or
broader than the class of any exception thrown in the parent class method.

4. If the method returns a value, it must be the same or a subclass of the method in the
parent class, known as covariant return types.

The first rule of overriding a method is somewhat self-explanatory. If two methods have
the same name but different signatures, the methods are overloaded, not overridden. As you
may recall from our discussion of overloaded methods in Chapter 4, the methods are unre-
lated to each other and do not share any properties.

Overloading vs. Overriding

Overloading a method and overriding a method are similar in that they both involve
redefining a method using the same name. They differ in that an overloaded method will
use a different signature than an overridden method. This distinction allows overloaded
methods a great deal more freedom in syntax than an overridden method would have.
For example, take a look at the following code sample:

public class Bird {
public void fly() {
System.out.println("Bird is flying");
}
public void eat(int food) {
System.out.println("Bird is eating "+food+" units of food");
}

}

public class Eagle extends Bird {
public int fly(int height) {
System.out.println("Bird is flying at "+height+" meters");
return height;
}
public int eat(int food) { // DOES NOT COMPILE
System.out.println("Bird is eating "+food+" units of food");
return food;
}

}

Introducing Class Inheritance 249

The first method, fly(), is overloaded in the subclass Eagle, since the signature changes
from a no-argument constructor to a constructor with one int argument. Because the
method is being overloaded and not overridden, the return type can be changed from
void to int without issue.

The second method, eat(), is overridden in the subclass Eagle, since the signature is the
same as it is in the parent class Bird—they both take a single argument int. Because the
method is being overridden, the return type of the method in Eagle must be a subclass of
the return type of the method in Bird. In this example, the return type void is not a sub-
class of int; therefore, the compiler will throw an exception on this method definition.

Any time you see a method on the exam with the same name as a method in the parent
class, determine whether the method is being overloaded or overridden first; doing so
will help you with questions about whether the code will compile.

Let’s review some examples of the last three rules of overriding methods so you can
learn to spot the issues when they arise:

public class Camel {
protected String getNumberOfHumps() {
return "Undefined";
}

}

public class BactrianCamel extends Camel {
private int getNumberOfHumps() { // DOES NOT COMPILE
return 2;
}

}

In this example, the method in the child class doesn’t compile for two reasons. First, it
violates the second rule of overriding methods: the child method must be at least as acces-
sible as the parent. In the example, the parent method uses the protected modifier, but the
child method uses the private modifier, making it less accessible in the child method than
in the parent method. It also violates the fourth rule of overriding methods: the return
type of the parent method and child method must be covariant. In this example, the
return type of the parent method is String, whereas the return type of the child method
is int, neither of which is covariant with each other.

Any time you see a method that appears to be overridden on the example, first check to
make sure it is truly being overridden and not overloaded. Once you have confirmed it is
being overridden, check that the access modifiers, return types, and any exceptions defined
in the method are compatible with one another. Let’s take a look at some example methods
that use exceptions:

250 Chapter 5 ■ Class Design

public class InsufficientDataException extends Exception {}

public class Reptile {
protected boolean hasLegs() throws InsufficientDataException {
throw new InsufficientDataException();
}
protected double getWeight() throws Exception {
return 2;
}

}

public class Snake extends Reptile {
protected boolean hasLegs() {
return false;
}
protected double getWeight() throws InsufficientDataException{
return 2;
}

}

In this example, both parent and child classes define two methods, hasLegs() and
getWeight(). The first method, hasLegs(), throws an exception InsufficientDataException
in the parent class but doesn’t throw an exception in the child class. This does not violate the
third rule of overriding methods, though, as no new exception is defined. In other words, a
child method may hide or eliminate a parent method’s exception without issue.

The second method, getWeight(), throws Exception in the parent class
and InsufficientDataException in the child class. This is also permitted, as
InsufficientDataException is a subclass of Exception by construction.

Neither of the methods in the previous example violates the third rule of overriding
methods, so the code compiles and runs without issue. Let’s review some examples that do
violate the third rule of overriding methods:

public class InsufficientDataException extends Exception {}

public class Reptile {
protected double getHeight() throws InsufficientDataException {
return 2;
}
protected int getLength() {
return 10;
}

}

Introducing Class Inheritance 251

public class Snake extends Reptile {

protected double getHeight() throws Exception { // DOES NOT COMPILE

return 2;

}

protected int getLength() throws InsufficientDataException { // DOES NOT COMPILE

return 10;

}

}

Unlike the earlier example, neither of the methods in the child class of this code will com-
pile. The getHeight() method in the parent class throws an InsufficientDataException,
whereas the method in the child class throws an Exception. Since Exception is not
a subclass of InsufficientDataException, the third rule of overriding methods is
violated and the code will not compile. Coincidentally, Exception is a superclass of
InsufficientDataException.

Next, the getLength() method doesn’t throw an exception in the parent class, but it
does throw an exception, InsufficientDataException, in the child class. In this manner,
the child class defines a new exception that the parent class did not, which is a violation of
the third rule of overriding methods.

The last three rules of overriding a method may seem arbitrary or confusing at first, but
as you’ll see later in this chapter when we discuss polymorphism, they are needed for con-
sistency of the language. Without these rules in place, it is possible to create contradictions
within the Java language.

Redeclaring private Methods

The previous section defined the behavior if you override a public or protected method in
the class. Now let’s expand our discussion to private methods. In Java, it is not possible to
override a private method in a parent class since the parent method is not accessible from
the child class. Just because a child class doesn’t have access to the parent method, doesn’t
mean the child class can’t define its own version of the method. It just means, strictly speak-
ing, that the new method is not an overridden version of the parent class’s method.

Java permits you to redeclare a new method in the child class with the same or modi-
fied signature as the method in the parent class. This method in the child class is a separate
and independent method, unrelated to the parent version’s method, so none of the rules for
overriding methods are invoked. For example, let’s return to the Camel example we used in
the previous section and show two related classes that define the same method:

public class Camel {

private String getNumberOfHumps() {

return "Undefined";

}

}

252 Chapter 5 ■ Class Design

public class BactrianCamel extends Camel {
private int getNumberOfHumps() {
return 2;
}

}

This code compiles without issue. Notice that the return type differs in the child method
from String to int. In this example, the method getNumberOfHumps() in the parent class
is hidden, so the method in the child class is a new method and not an override of the
method in the parent class. As you saw in the previous section, if the method in the parent
class were public or protected, the method in the child class would not compile because it
would violate two rules of overriding methods. The parent method in this example is
private, so there are no such issues.

Hiding Static Methods

A hidden method occurs when a child class defines a static method with the same name
and signature as a static method defined in a parent class. Method hiding is similar but
not exactly the same as method overriding. First, the four previous rules for overriding a
method must be followed when a method is hidden. In addition, a new rule is added for
hiding a method, namely that the usage of the static keyword must be the same between
parent and child classes. The following list summarizes the five rules for hiding a method:

1. The method in the child class must have the same signature as the method in the parent
class.

2. The method in the child class must be at least as accessible or more accessible than the
method in the parent class.

3. The method in the child class may not throw a checked exception that is new or
broader than the class of any exception thrown in the parent class method.

4. If the method returns a value, it must be the same or a subclass of the method in the
parent class, known as covariant return types.

5. The method defined in the child class must be marked as static if it is marked as
static in the parent class (method hiding). Likewise, the method must not be marked
as static in the child class if it is not marked as static in the parent class (method
overriding).

Note that the first four are the same as the rules for overriding a method.
Let’s review some examples of the new rule:

public class Bear {
public static void eat() {
System.out.println("Bear is eating");
}

}

Introducing Class Inheritance 253

public class Panda extends Bear {
public static void eat() {
System.out.println("Panda bear is chewing");
}
public static void main(String[] args) {
Panda.eat();
}

}

In this example, the code compiles and runs without issue. The eat() method in the
child class hides the eat() method in the parent class. Because they are both marked as
static, this is not considered an overridden method. Let’s contrast this with examples that
violate the fifth rule:

public class Bear {
public static void sneeze() {
System.out.println("Bear is sneezing");
}
public void hibernate() {
System.out.println("Bear is hibernating");
}

}

public class Panda extends Bear {
public void sneeze() { // DOES NOT COMPILE
System.out.println("Panda bear sneezes quietly");
}
public static void hibernate() { // DOES NOT COMPILE
System.out.println("Panda bear is going to sleep");
}

}

In this example, sneeze() is marked as static in the parent class but not in the child
class. The compiler detects that you’re trying to override a method that should be hidden
and generates a compiler error. In the second method, hibernate() is an instance mem-
ber in the parent class but a static method in the child class. In this scenario, the compiler
thinks that you’re trying to hide a method that should be overridden and also generates a
compiler error.

254 Chapter 5 ■ Class Design

As you saw in the previous example, hiding static methods is fraught
with pitfalls and potential problems and as a practice should be avoided.
Though you might see questions on the exam that contain hidden static
methods that are syntactically correct, avoid hiding static methods in your
own work, since it tends to lead to confusing and difficult-to-read code.
You should not reuse the name of a static method in your class if it is
already used in the parent class.

Overriding vs. Hiding Methods

In our description of hiding of static methods, we indicated there was a distinction between
overriding and hiding methods. Unlike overriding a method, in which a child method
replaces the parent method in calls defined in both the parent and child, hidden methods
only replace parent methods in the calls defined in the child class.

At runtime the child version of an overridden method is always executed for an instance
regardless of whether the method call is defined in a parent or child class method. In this
manner, the parent method is never used unless an explicit call to the parent method is
referenced, using the syntax ParentClassName.method(). Alternatively, at runtime the par-
ent version of a hidden method is always executed if the call to the method is defined in the
parent class. Let’s take a look at an example:

public class Marsupial {
public static boolean isBiped() {
return false;
}
public void getMarsupialDescription() {
System.out.println("Marsupial walks on two legs: "+isBiped());
}

}

public class Kangaroo extends Marsupial {
public static boolean isBiped() {
return true;
}
public void getKangarooDescription() {
System.out.println("Kangaroo hops on two legs: "+isBiped());
}
public static void main(String[] args) {
Kangaroo joey = new Kangaroo();
joey.getMarsupialDescription();
joey.getKangarooDescription();
}

}


Click to View FlipBook Version