0% found this document useful (0 votes)
3 views

Java Resumen (1)

The document provides an overview of Java programming concepts, including class creation, methods, packages, visibility modifiers, loops, arrays, ArrayLists, and the Scanner class. It explains the use of static methods and variables, wrapper classes, and string manipulation, along with the importance of interfaces in Java. Additionally, it covers autoboxing and the distinction between instance and static methods, emphasizing best practices for coding in Java.

Uploaded by

cragnolinitomas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

Java Resumen (1)

The document provides an overview of Java programming concepts, including class creation, methods, packages, visibility modifiers, loops, arrays, ArrayLists, and the Scanner class. It explains the use of static methods and variables, wrapper classes, and string manipulation, along with the importance of interfaces in Java. Additionally, it covers autoboxing and the distinction between instance and static methods, emphasizing best practices for coding in Java.

Uploaded by

cragnolinitomas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 86

creating a class

adding methods

create objects on the class definition


packages

In Java, a package is a namespace used to group a set of classes together. So far, our
application consisted of only one file with one class, but most Java applications consist of
many files and many classes. The package allows us to connect these classes together.
When using a package, the package name appears at the very top of your code inside your
class file. Java naming standards recommend that the package name is written in all lower-
case letters to avoid confusion with any class names. To add a class to a package, use the
keyword package followed by the package name, for example:
package packagename;

As we continue to create more complicated classes, one of the benefits of Java is the vast
amount of code already written and tested that is available for general use. These packages
are included in the Java API (Application Programming Interface). The API is a library of
packages that can be used in our applications. Most of these packages are designed for
handling common programming requirements, such as working with String objects or File
objects. This allows us to concentrate on the programming needed to handle the business
requirements for our program.
To leverage other classes in Java that are not part of your current package, we can use the
import statement. The import statement allows us to ‘import’ other packages that contain
one or more classes. For example, any program that reads information from the console
uses
the Scanner package and must include this statement: import Java.util.Scanner;

visibility modifiers explained


METHOD SIGNATURES

visibility modifier

return type
parameter list

public void setEmployeeLastName(String lastName)

adding getter and setter methods

LOOPS
for loop

a) for(int i = 0; i<5; i++){System.out.println(i);}

b) for(int j = 5; j>=0; j--) {System.out.println(i);}

While loop

The for-each Loop

The last type of loop that is used in Java is called an enhanced for loop, or a for-each loop.
This loop automatically iterates over a collection of items. To demonstrate the for-each loop,
I must introduce a collection of items. In Java, one of the simplest ways to create a collection
of elements is using an array. An array is used to hold a collection of items that are all the
same type. In Java, the array syntax uses square brackets, which creates a variable called
videoGames that holds a collection of String elements.

ARRAYS

It is possible to declare the array in one statement and then assign the size in a second
statement:

default values
access and modify values in an array

This table shows arrays that are initialized with values and the length of each array.

This array represents the number of students in four separate sections of an English class. If
we have a new student who just started, we need to increase the number of students for the
new student’s section. If the student is in the third section, we need to change the value from
10 to 11, in other words, add one to the value at index 2 (remember, the indices start at zero,
so the third value is in index value 2).

numStudents[2] = numStudents[2] + 1;

To access an element in an array, we start with the array name, and then place the index of
the element in square brackets. For this example, I am changing the value in numStudents
at index 2 to be increased by 1.

array traversing

A for loop can also be used to populate an array. For example, here is
code that creates an integer array and populates the array with even numbers from 0 to 100
(not including 100).

Manipulating arrays using the Arrays class

With the popularity of arrays, it is no surprise that there is an Arrays class as part of the
standard Java API in the util package and can be accessed with this import statement:

import java.util.Arrays;

Some examples

of the types of manipulations available through this class include:


• binarySearch
• copyOf
• copyOfRange
• equals
• fill
• sort
• and toString

Creating ArrayLists

The ArrayList holds a collection of objects and it is resizable!

The ArrayList in figure 6.6 contains a list of Car objects. The syntax used to denote the type
of data in the ArrayList is called a diamond operator and was introduced with Java 7. When
using the diamond operator, the compiler only requires the diamond on the left to include a
value. So, we could rewrite this statement as:

ArrayList<Car> usedCars = new ArrayList<>();

The variable, usedCars, is essentially a reference to the start of a list of Car objects.

Commonly used methods for an ArrayList


Scanner class

In this section, I want to demonstrate how to use the Scanner class, which is found in the
java.util package. When reading input from the user, the Scanner class makes it easy.
Before reading data from the command line, the first thing is to create a new Scanner object,
for example:

Scanner in = new Scanner(System.in);

The Scanner class can also be used to read data from a file.
Once the Scanner object is created, there are several methods that can be used to parse the
input into tokens. For example, the Scanner class has a method that can check the next
value to determine the type of data. If you are expecting the value to be an integer, there is a
method that returns true or false depending on the data. There are methods for all the
primitive data types, even a method that checks for any data at all, hasNext(). Each method
listed in Table 8.1 returns a value of true or false.

List of hasNext methods for the Scanner class


Similar methods exist for actually reading the values. It is important to note that the hasNext
method only check for next token in the scanner’s input and does not actually remove the
data from the input stream. For example, we can use the hasNextInt() method to check for
an integer. If the value is an integer, then the result is true, but we must then use nextInt() to
retrieve that value. Listing 8.2 is code that can be used to test that the user entered an
integer value.

Another useful class is the Object class. This class can be used to refer to any Object,
regardless of its original Class type. For an example of using the Object class, refer to figure
8.5 which shows the sample output from executing the code in listing 8.3.
In this example, I used two of the methods from the Object class: toString() and
getClass(). Other helpful methods include clone() and equals(Object o). Since Strings
are stored as objects in Java, we cannot use the == to compare two String objects. So, if we
want to compare these two objects, the code must use the .equals(Object o) method from
the Object class to make the comparison. If we tried to use ==, the program would try to
compare the reference value of each object, which contains the location in memory for each
object. This is only the same when two objects are pointing to the exact same location, in
other words, they are aliases of each other. Also, in this example, I have explicitly
imported the java.lang.Object class, but it is not required. The java.lang package is
imported by default into every Java program.

Wrapper classes

As I stated earlier, arrays can hold lists of primitive data/lists of objects and ArrayLists can
hold lists of objects. There are times when it is helpful to create an ArrayList that contains
primitive data. For example, we might need an ArrayList that contains double values. But,
since the ArrayList only works with classes and objects, we have to put the primitive data
inside a class first, the wrapper classes were designed just for this task.

Wrapper classes for primitive data types

Each primitive data type has a corresponding wrapper class that allows us to create objects
that hold primitive data. Here is a chart of all the primitive data types and their corresponding
wrapper classes:
Table 8.2 lists the constructors for each of the wrapper classes. It is easy to create objects
from either the corresponding primitive data type or even a String. For example:

Integer a = new Integer(5);

Integer b = new Integer("5");

Both statements create Integer objects containing the value 5.

Here is an example, many educational institutions use Learning Management Systems


(LMS) to help track students in a class, along with their grades during a semester. When
designing the LMS, the developer needs to allow for a course that has a small number of
students, but it also has to handle courses with very large numbers of students.
To keep track of the grades for a particular assignment, it makes sense to use an ArrayList
so it can grow and shrink as needed. Once the ArrayList is declared, a loop can ask the user
to enter grades using -1 as the sentinel value to indicate there are no more grades to be
entered.

Autoboxing is the term used to describe the conversion from a primitive data type to its
corresponding wrapper class. This occurs automatically by the compiler when a primitive
data type is added to an ArrayList. The compiler also unboxes automatically when the values
are taken from the ArrayList and put into a primitive data type. Listing 8.4 shows the code for
finding the average of a series of grades.

Wrapper class methods

Each wrapper class has similar methods available from the API. These methods provide the
ability to compare wrapper objects, convert from a primitive data type to an object, parse a
String to a numeric object and much more.
STRINGS

It is easy to create a variable that holds a string:

String groceryItem = "apples";

It is often helpful to find the length of a string object. The String class has a method,
.length(), which returns an integer value representing the number of characters in a string,
There is also a method that can be used to concatenate (or put together) two strings, it is
.concat(). Here are some examples of using this method:

String s1 = "apples";
String s2 = "oranges";
String s3 = s1.concat(" and ").concat(s2);
System.out.println(s3);

In Lesson 8 we learned about the wrapper classes that exist for all of the primitive data
types.
Each numeric wrapper class has a valueOf() method that retrieves the numeric value from a
string.
This is helpful when parsing string data to find the numeric values in the String. Let’s
look at a program that starts by asking the user to enter a list of numbers separated by
commas. Using that list, the program needs to find the average and then print all the values
as integers.
In order to separate the numbers from the commas, I start with the String class method
.split(). This method uses a delimiter to split the string, in this case it will split it every time
it encounters a comma followed by a space. The split method removes the specified value
and returns an array of string values. Once the array is created, I can use the wrapper class
method, valueOf(), to convert the string to a number. Then, it is easy to process the Integer
ArrayList and find the average.
static methods and variables

This lesson introduces the topic of the static keyword in Java. In Java, using the keyword
static indicates that the method is a class method or the variable is a class variable as
opposed to an instance method or instance variable. Every Java application uses the static
keyword at least once for the main method:

public static void main(String[] args) {...}

every Java application has at least one static method,


the main method. The main method is declared as static since there can only be one
instance
of the main method for each application. The Java virtual machine looks for the specific
method signature: public static void main(String[] args) to find the starting point of
any application. Java applications can have one or many separate files that work together to
form the application. But there can only be one main method, so by declaring it static, it
ensures there is only one. If you try to execute code that does not include a static main
method, you get an error message: “Error: Could not find or load main class ...”.

There are rules about whether a static method can call another static method, or can a static
method call an instance method. Table 10.1 shows the rules for static methods and
variables:
Listing 10.1 is a code snippet that shows an example of a class that contains three methods
and six variables. Two of the methods are static and the third method is an instance method
(any method without the keyword static is considered an instance method). The static
methods are: assignNextNumber and main. The print method is an instance method since it
does not include the keyword static.

#A Static variable nextItemNumber is used to keep track of the next item number
#B This is a static method used to assign the next item number, then increase it by one
#C This is an instance method that prints out the shirt item number
#D Create a new tshirt object
#E Provide values for the instance data
#F Use the static method to get the next available item number
#G Print the tshirt item number

In this example, line 13 causes a compile time error, this occurs because we cannot call a
non-static method from a static method. In this case, I’m trying to call the non-static method
print from the static main method. To fix this error, it is possible to use the TShirt object
created on line 9 to call the instance method and print the item number for this t-shirt:

tee1.print();

Since print is an instance method, we must access it through an object that is created using
the class TShirt.
Figure 10.1 shows the variables and methods that are static on the left. The right is a list of
instance variables and methods. There can only be one variable for nextItemNumber and
one main method. All TShirt objects are given copies of the instance data and the price is
assigned to 14.99 by default. This can be changed once an object is created. But if most of
the t-shirts cost 14.99, this saves time by automatically assigning this value.

To decide if a method should be static, consider the question, does the program need to call
this method independent of any objects? If the answer is yes, then it should be static. If the
method only makes sense when attached to an object, then it should be an instance method.

static variables

Static variables are useful when the information is the same for all instances of a class. For
example, if we had an Employee class and all employees worked for Linkedin, we could
create a static variable for the company name in the Employee class that allocates space for
that variable once, even if we have 1,000 employees. A static variable is shared across all
instances of a class. Here is an example:

Final Static Variables


Often a static variable is also defined as final. This makes sense since there is only one copy
of a static variable. Final is another keyword that is used to indicate the variable does not
change. The keyword final can be used with or without the keyword static. A non-static final
variable is used for constant values, for example, the definition of double final PI =
3.14159; This definition can be used for any constants that need to be defined in our code.

When using the keywords static final with a primitive data field, the value cannot be
changed. But, it is different is the variable represents an object such as an ArrayList. Listing
10.3 shows an example of defining an ArrayList as static final and then adding values to
the ArrayList, even though it is defined as final. Here is the output that is produced from
executing the code in Listing 10.3:

#A Create an empty ArrayList and define it as static final


#B Add planet names to the ArrayList even though it was declared as final
#C From the main method, call the static method to add the planets to the ArrayList
#D Print the ArrayList after all planets have been added

In this example, the program can add the values to the planets ArrayList even though it has
the keywords static final. That values can be added because that action does not attempt to
update the reference value of the original planets ArrayList. In line 19, the code is trying to
assign the planets ArrayList to the new ArrayList of dwarf planets, but this attempts to
change
the reference value and the compiler produces an error “cannot assign a value to final
variable planets”.

It is important to understand the difference between updating the reference value of an


object, which is used as the pointer to the location in memory, and the actual values
contained
in each object such as the list of planets in the example code.
A static variable that includes the attribute final, must be initialized before use. This is
different from other static variables which are automatically assigned default values. Table
10.2 shows the values that are automatically assigned to a static variable if it not otherwise
initialized.

In Java, an interface is defined similar to a class but it uses the keyword interface instead of
class. It is used to establish a type of contract that describes how the software must interact
with any classes that implement this interface. An interface is automatically identified as
abstract by the compiler because it does not provide definitions for the methods declared in
the interface. In addition, each method must be defined by the implementing class. Each
method included in the interface is considered abstract by default. An abstract method is a
method that is declared but does not include an implementation. When an abstract method
is included in an interface, it only provides the method signature.

Let’s start with an example, if we created a video game and we wanted to allow the game to
be played on multiple gaming systems, we could create an interface that identifies the
specific
methods required for the controller to play the game. The gaming systems might include
Sony PlayStation, Microsoft Xbox, Nintendo Switch, or even the PC. Each of these
manufacturers has a specific type of controller interface that needs to perform the actions
identified in the interface, but they each use different buttons, keys, toggles, and so on.
Diagram 11.1 shows how the ControllerInterface is implemented by each type of controller.
defining a new interface

Remember, each method included in the interface is considered public and abstract by
default.
Notice that the methods only contain the signature, they do not have any code in the body,
and they end with a semicolon. Now, any program that implements our vehicle interface
must provide valid definitions for each abstract method. The class that implements the
interface must maintain the same return type and parameters (including data type and the
number of parameters) as the method signature from the interface.
implementing an interface

overloading methods

Three ways to overload a method


There are three ways to overload a method (see matching examples in figure 13.2):
1. Varying the number of parameters in the method signature
2. Changing the data type of one or more parameters in the method signature
3. Changing the order of the parameters in the method signature

Overloading Constructors
Methods that are used to instantiate an object are called constructors. These methods are
frequently overloaded to provide various options for instantiating an object. Every class
contains at least one constructor, a default constructor, which is automatically created when
the programmer does not explicitly provide one. But, programmers often find it useful to
declare multiple versions of a class constructor.
Here is an example, if we have a class called Computer, we might want to create a computer
object for a laptop and another object for a desktop computer. The laptop object might only
have the brand name, but the desktop computer might include the brand name and
information about the monitor, keyboard and mouse. Listing 13.4 shows the code for a
Computer class with two overloaded constructors, one for a laptop and one for a desktop
computer.

Now let’s take a look at how we can use a class that has overloaded constructors. For this
example, I will refer to the Computer class from listing 13.4. The same process applies to
overloaded constructors, the calling method decides which constructor to call based on the
argument list. In my example, there are two overloaded constructors, one for creating a
laptop object and one for creating a desktop object. We can add a third constructor that does
not include any arguments, this constructor is called when creating an object that does not
include any initial arguments to be passed to the constructor. The no-parameter constructor
can be used to create an object based on common values for the instance data. For this
example, most of our customers order surface laptops made by Microsoft. So, the no
parameter constructor creates an object with the brand value set to Microsoft. Figure 13.6
shows a screenshot of sample output for executing the code in listing 13.5.

Listing 13.5 shows the code with the additional overloaded constructor and a main method
that creates three computer instances.
inheritance

This lesson introduces the topic of overriding methods in Java. In Java, a class can extend
another class (referred to as the parent class) if we want the classes to share some or all of
the data and methods in the parent class. This is called inheritance. Figure 14.1 is a diagram
showing inheritance where class B is the parent class to class A. The extended class is
considered a subclass or a child class of the parent class.
A method in a child class overrides a similar method in the parent class when it has the
exact same method signature. This allows the implementation of the child method to be
different than the parent method.
Remember, the method signature includes the return type, the method name, and a
parameter list.

Write a Class that Extends Another Class

Write an overridden method in a subclass


Using the keyword super

In the previous example, each of the subclasses of Book uses the keyword super. When a
class extends another class, it automatically inherits access to all public and protected
instance data and methods of the super class, including the class constructor. The
constructor is the method used to create an object and provide values for the instance data.
Remember, a class that is extended by other classes is considered the super class. So, in
our subclass, we can refer to the parent constructor using the keyword super. This keyword
refers to a method in the parent class and it is used so we don’t have to write the same
method twice, once in the parent class and again in the child class. In the Book example,
each subclass refers to the constructor in the Book class using the keyword super followed
by a list of argument values that are required for the Book constructor.
Figure 14-5 shows how the Cookbook class uses the Book constructor to initialize the values
for the book title, publisher, author(s), and the number of pages. Notice that the instance
data is only defined in the Book class and it is defined as public. This allows the subclasses
to
access all of these data fields directly.

Using the keyword instanceof

The keyword instanceof can be used to test whether the object is an instance of a class or
subclass.
Understand the Definition of Polymorphism

Polymorphism is an important and powerful concept in object-oriented programming.


Polymorphism can occur at compile time and at runtime. When a single class contains
overloaded methods (which are discussed in Lesson 13), then the same method name is
used
but the parameter list must be different. In this situation, the same method name performs
differently depending on the argument list that is passed to the method. This is considered
compile time polymorphism since the compiler identifies which version of the method to call
based on the argument list.

The second type is called runtime polymorphism. This type of polymorphism is used to
bind a method call at runtime instead of compile time. This polymorphism occurs using
overridden methods which are introduced in Lesson 14. In this type, the Java Virtual
Machine,
or JVM, determines which version of the method to call at runtime. The exact same method
signature is used in multiple classes where one class must extend the other class using
inheritance. The subclass overrides the method in the superclass and provides different
implementations of the same method. Multiple objects which are instances of subclasses of
the same superclass can each perform different operations when the method is called. The
JVM then decides whether to use the method in the subclass or the method in the
superclass
at runtime. This lesson demonstrates how the JVM can decide which method to call at
runtime.

Let’s start by looking at an example. If we have these three classes: Television,


Computer, and GamingSystem, we know that each class is a type of Electronic device and
therefore can be described as a subclass of the superclass Electronic.
The three subclasses inherit all the public/protected data and methods from the
superclass. Each of the subclass devices contain additional information specific to that
device.
So, when we want to print the information about each electronic device we need to use the
overridden print method in the subclass to ensure we are getting all the specific information
for that device. In this case, there is a print method in the Electronic class, but each subclass
has the same print method signature, but it behaves differently. If we created three
Electronic objects and instantiated them as a Television object, a GamingSystem object, and
a Computer object and then invoked the print method for each reference, the output might
look like this:
Benefits of Using Polymorphism

You might be wondering why we would use this approach in our applications. Here are some
of the benefits of using polymorphism:
• A single reference variable can be used to reference objects of multiple data types
• Polymorphism allows a subclass to use the more general definitions provided by a
superclass and override methods to provide specialized method implementations
specific to the subclass
• Since runtime polymorphism requires subclasses that override the superclass methods
with alternative implementations, this enables code reuse of existing classes without
the need to recompile
• Polymorphism enables an object behavior to be determined at runtime
• Compile time polymorphism occurs when a class has method overloading, this is often
used for constructors which allows different ways to instantiate an object

The Difference Between Compile Time and Runtime Polymorphism

There are two types of polymorphism, method overloading and method overriding. This
section discusses the difference between the two types. To start, method overloading
(Lesson
13) is an example of static polymorphism and it occurs at compile time. Method overriding
(Lesson 14) is considered dynamic binding and it occurs at run-time.
From Lesson 13, we know that there are times when we need to write a method that
performs the same logic but might have different input. A simple example is a method that
finds the average of three numbers. We might want to allow the three numbers to be
integers, but there are times when they might all be doubles. The logic inside the method is
going to add the numbers, divide by three, and return the result. So, the method signature is
very similar except for the type of parameters in the parameter list. In this code snippet,
there are two overloaded methods for findAverage, and the main method calls the
appropriate
method based on the arguments in the method call (A and B)
These methods are considered overloaded since they have the same name but different
parameter lists. The compiler checks to make sure the method signature is not the same.
Since the list of parameters is different, the compiler can check any statement that calls the
findAverage method to determine if the argument list matches the first method parameter list
(double, double, double) or the second parameter list (int, int, int). As long as the method
call matches one of these two methods, it will not generate an error and the correct method
will be invoked. This is a type of polymorphism (where two methods with the same name
behave differently) that is checked at compile time. If the parameters were not different, then
this would cause a compile time error.
Now let’s talk about method overriding, which is checked at runtime. In general terms,
when an object is created using a superclass, it can have a reference to a subclass object
which has overridden a superclass method. So, the call to the overridden method is
determined at runtime since the compiler does not necessarily know whether the variable
has
a reference type of the superclass or subclass. This process in which a call to the overridden
method is resolved at runtime is known as dynamic binding or runtime polymorphism.
Figure 15.1 is a diagram with a superclass called Electronic which has three subclasses.
According to this diagram, a Television is an Electronic, a Computer is an Electronic and a
GamingSystem is an Electronic. Each class has a print method. Since the name and
parameter list (which is empty) are the same, we know that each subclass is overriding the
superclass print method.
Based on this diagram, we can use the following statement to define an Electronic reference
variable that is refencing a Television object:

Electronic tv = new Television("Samsung", "Smart", "60 inches");

Now, if we want to print the television information, we use this statement: tv.print(); The
compiler checks to make sure that Television is a subclass of Electronic and if the subclass
has a print method. In this case, it does have a print method. At runtime, the JVM invokes
the print method from the Television class and not the method from the Electronic class. This
allows the same method name to perform a different action which is the definition of
polymorphism. This action is resolved at runtime and not compile time.

Polymorphism In Action

Setup Classes Using Inheritance to Demonstrate Polymorphism


My neighbor, who used to be the field hockey coach, is now the athletic director for our local
high school. In this role, she is in charge of all the sporting events for our school. She also
needs to have a current roster of all students that are athletes. This is used to periodically
check their GPA to make sure they are still eligible to play sports.
If she asked me to create an application to help her manage all the sports teams and the
athletes on these teams, I would start by examining the relationship of these entities. For this
example, I am using classes to represent a SportsTeam, Student, Athlete, FootballPlayer,
TennisPlayer, FieldHockeyPlayer, and SoccerPlayer. The SportsTeam class will be used to
create a team object with a list of all the athletes on that team. The Student class is a
superclass since all athletes are students. And each subcategory of Athlete extends the
Athlete
class, so they are all subclasses of the Athlete class. Figure 16.1 shows the hierarchy of
these
classes.
Now that we have our diagram of the classes and their relationships, the next step is to
identify the attributes and behaviors for each class. For this example, here are the
requirements for each class:

Define Objects Using Polymorphism

Using the information from section 16.1, I have started an application that can be used to
keep track of all the student athletes. Listings 16.1-16.5 show the code for the
Lesson16_Example (which contains the main method), SportsTeam, Student, Athlete, and
the
FieldHockeyPlayer classes. To save space, I have omitted the FootballPlayer, TennisPlayer,
and SoccerPlayer classes in this section, but the complete code is available at the end of
this
lesson. For this example, I have decided to place each class in a separate file. The package
at
the top of each code file is the same for each class, so the compiler knows these classes are
all part of one application. Before diving into the code, let’s look at figure 16.2. This figure
shows the hierarchy of our classes and provides a subset of a UML diagram for each class
so
you can see the methods in the superclass and the subclasses.
The diagram in figure 16.2 shows each class and the methods defined in each class. It is
especially important to note that each class has its own version of a toString method. Since
the Athlete class extends the Student class, this is an example of overriding a method. The
same is true for each of the subclasses under Athlete, they each override the toString
method in the Athlete class. Examples of how this works is included in the next section. First,
let’s see the code for each of these classes, then I will point out how polymorphism is used
in this example.

In Listing 16.1, we start to see a glimpse of polymorphism. Notice that on lines 16-19, I am
adding my athletes to the students array. Since each type of athlete (FootballPlayer,
SoccerPlayer, etc.) extends Athlete and the Athlete class extends Student, we can assign
each element in the students array to a reference of a Student, or Athlete, or
FieldHockeyPlayer,
etc. This is possible since each athlete type is a subclass of the Athlete class and the Athlete
class is a subclass of the Student class.
In other words, an object of a subclass can be used anywhere an object of the superclass
is used. The JVM decides which method to call at runtime based on the object type, this is
considered dynamic binding. So, in the example above, variable fh1 is created as a Student
reference type but then it is pointing to a FieldHockeyPlayer object. So, when invoking a
method on the object fh1, the JVM first looks to the FieldHockeyPlayer class first, if it is not
found, it goes to the superclass, in this case the Athlete cl ass. If the Athlete class does not
have a corresponding method, it goes to the superclass of the Athlete, which is the Student
class.
The next listing is for the SportsTeam class.

The SportsTeam class is designed to create a roster of eligible athletes for each sports
team.
This class accepts a list of potential athletes in the method starting on line 10. To avoid
encountering a null pointer exception, line 12 checks to make sure each value in the
athletes’
array has been instantiated. Then it checks to see if each athlete is active and eligible, if the
athlete is eligible, then they are added to the list of team members. There is also a toString
method that prints the team name and a list of athletes on the team. In this example it might
seem strange to have the override statement on line 21, but even though the SportsTeam
class does not explicitly extend any other classes, remember, all classes extend the Object
class by default. So, the toString method in the SportsTeam class overrides the toString
method in the Object class. The next listing is for the Student class.

The Student class is designed to represent all students. It contains information about the
student’s name, age, grade, and GPA. This class is the superclass to the Athlete class. This
makes sense since every Athlete must be a Student. Note that this class has two public
methods:

public double getGPA()

public String toString()

Since these methods are public, they become accessible to any subclass that extends the
Student class.
Here is our first subclass of the Student class. The athlete class is not specific to a certain
sport, it is representative of all students who play sports. It includes access to all the public
and protected characteristics of the Student class, and then adds any additional
characteristics specific to all athletes. In this example, I have added two boolean variables
that are used to indicate if the athlete is active/inactive and eligible/ineligible. In the code
listing 16.4 on line 12, you can see that the program is referencing the getGPA method in the
Student class.
Since that method is defined as a public method, it is available to all the subclasses. This is
an example of how inheritance can be used in Java.
The method starting on line 21 is an overridden version of the toString method. So, when
an object reference is for an Athlete object, if the code calls the toString method, it will
execute this method. Inside this method I am using the keyword super to reference the
toString method in the Student class. This enables my program to use the code already
written for part of the String that gets returned from a call to this method.

Note that this class has the following public methods:


Since these methods are public, they become accessible to any subclass that extends the
Athlete class in addition to the two public methods from the Student class.

In the FieldHockeyPlayer class, it has access to all of the following methods from the Athlete
class and the Student class:

Since the toString method is actually overridden in the FieldHockeyPlayer class, it gets
invoked whenever a variable references a FieldHockeyPlayer object. This is the power of
polymorphism, here is an example:
Now that we have all the classes defined, let’s review how the virtual machine determines
which method to invoke. Remember, if a class extends another class, it can access any
public
or protected attributes and methods of the superclass. For example, in code listing 16.6, I
start by defining a variable that references a Student object. Next, I am going to update the
reference value of this variable to a FieldHockeyPlayer data type. At this point, the variable
has access to the public met

Using the diagram in Figure 16.1, I’ve created a table with sample code that instantiates
several types of objects. Most of the statements are valid, but there is one statement that
causes a compile time error: Incompatible types: Student cannot be converted to
Athlete. Review the table and notice the use of the superclass and subclass references.
When working with inheritance, it is sometimes difficult to understand what is allowable when
using a superclass or subclass to create various objects. A general rule is that the variable
declaration on the left-hand-side must be the same or a superclass of the instantiated object
on the right-hand-side. The benefit of creating a variable using the parent class is that the
variable name can be reassigned to any of the subclasses. But, don’t be fooled, since the
variable is considered a reference to a parent object, it only has access to public data and
methods in the parent class and access to any methods that are overridden in the child
class.
The next section discusses how we can work with upcasting and downcasting to
get around this limitation.

What Does it Mean to Upcast and Downcast objects


Upcast and downcast are two terms associated with polymorphism. In Java,
upcasting means casting an object to a superclass type, while downcasting means casting
an object to a subclass type.
A subclass object that is defined using a superclass is considered
upcasting and can be done implicitly. In the code listing 16.8 (which is a reprint of Listing
16.1), lines 16 – 19 are examples of upcasting. This is true since each variable reference on
the right-hand-side is from a subclass of the variable reference on the left-hand-side.
Upcasting is always allowed, but we need to be careful with downcasting. When we use
downcasting on an object, it can cause a ClassCastException. In the code Listing 16.6, I
have
included a check to make sure that the object is a FieldHockeyPlayer before downcasting
the reference object in the array. This is done using the instanceof keyword.
In order to downcast, we must explicitly use the subclass name and cast the object to that
subclass. Line 23 shows an example of how to downcast from a Student reference to a
FieldHockeyPlayer reference. This is necessary for this code since I wanted to add field
hockey athletes to the list of field hockey players. An array is created to hold the list of field
hockey players on line 14. So, every object added to this array must be of type
FieldHockeyPlayer (or a subclass of FieldHockeyPlayer if there was one).

#A This is an example of upcasting, we are casting a FieldHockeyPlayer to a Student object


reference.
#B Since we included the check to make sure this object is a FieldHockeyPlayer, we can use
this to downcast from Student

One important note when using upcasting and downcasting is the order of precedence of the
dot operator. In my example, I am casting the Student object s to a FieldHockeyPlayer object
before adding it to the fhPlayers array. If I needed to access any of the instance data or
methods from the FieldHockeyPlayer class, I can use this same logic. I can cast the Student
to a FieldHockeyPlayer, but I must enclose the casting portion with parentheses so that it is
done first. Table 16.2 shows valid/invalid access to the FieldHockeyPlayer class methods
given the Student reference variable s. These statements would be defined in the main
method from Listing 16.6.
How the JVM Chooses the Correct Method to Execute

In this lesson, we have seen examples of polymorphism in action. Runtime polymorphism


occurs when a subclass overrides the same method signature as a method in the
superclass.
This can actually cause a daisy chain effect, where each time a subclass extends a
superclass
it can override the method. In figure 16.3, the main method creates four new objects using
Class1. Each object variable is referencing either the superclass (Class1) or the subclasses
Class2, Class3, and Class4. Each class has a method1 that takes one integer value. The
superclass has a second method, method2 that also takes one integer value.
At runtime, the JVM starts by looking for method1 in the corresponding class. The last
statement defines an object c4 that references a Class4 object. When the JVM invokes
method2 on this object, it starts by looking for an overridden method in Class4, but there is
not method. So, it checks the superclass, Class3. It keeps going until it finds the correct
method signature in Class1.
Comparing Objects vs. Primitive Data Types

When we create an object variable, the variable


stores a reference value to the location of the object data in memory. When we use the
keyword new to create a new instance of a class, the memory location is stored in the
variable.
Even if these two objects have the exact same values, the reference values will be different.
So, if we compare these two objects to test for equality using the symbol ==, it will return
false. Figures 17.1 and 17.2 show the difference between comparing two primitive data
types and two objects.
Can you think of an instance where p1 might be equal to p2? If we are interested in whether
or not both values have the same reference value, then they are equal. In this case, the
objects are considered aliases of each other. In this example, I can accomplish this by
having p2 point to p1:
Now, p1 and p2 have the same reference value and point to the EXACT same object in
memory. Therefore, when we compare them using the == sign, the statement is considered
true. In the next section, I will show you how to add a method to compare two objects for
equality.

Compare Objects for Equality

As we have seen in the previous section, if we use the == sign to compare two objects, it
only compares their reference values. There are times when we want to check and see if all
the values from one object match the values from another object. To accomplish this, we can
add a new method to our Person class. This new method is added using a generic object as
the argument. If the argument is of type Person, when we can check each field to see if they
match. If it does, then the method returns true, otherwise it returns false. Listing 17.2 is an
example of a Person class with an equals method used to test for equality:
In the new equals method, each field from the Person class is compared. If they are all
equal, the method returns true. If the object data is not the same or if it is not an instance of
the Person class, it returns false. Notice that the equals method is invoked on lines 7 and 8
in Listing 17.2. The method uses the object on the left-hand side (lhs) as the calling object, in
this case p1 is the calling object for both statements. In the parentheses, the program
passes an object as the argument to the method. The first statement uses p2, the second
statement uses p3. In the equals method, the values of the calling object are referred to by
the keyword this. The objects p2 and p3 are passed to the argument variable obj which is
defined as an Object. Remember, every class inherits from the Object class, so we can use
that to define an Object variable and have it reference a Person object. But, in order to
compare the fields from our calling object to the information in our argument object, we must
cast it as a Person object. Finally, notice the use of the instanceof keyword to check if the
argument is a Person object.
Figures 17.4 and 17.5 are diagrams depicting how the objects are passed to the equals
method with annotation to help explain how the variables in the method refer to different
objects.
By adding the equals method to the Person class, the programmer can define the criteria for
comparison based on the business requirements. For example, if the business requirements
want to compare two Person objects and consider them equal if they have the same first
name and last name, regardless of age, the code could be changed to only test those two
characteristics of the objects. Listing 17.3 shows a revised version that only checks for first
and last name to be the same.

Creating Classes

The application must be able to:


1. Create a project with a start date, end date, customer name, address of the project,
description, estimated cost, overhead percentage for the contractor, and a list of
workers
2. Print out the list of projects including the project address location
3. Compare two projects and determine which project must be done first based on the
start date

This is a small company that hires the following types of workers:


• Electrician
• Plumber
• Carpenter
Using this approach to create the classes for this project, I can create a list of workers who
areassociated with a specific project. This design creates a list of workers which can be a
regularWorker, an Electrician, a Carpenter, or a Plumber. Notice that each subclass of the
Workerclass overrides these methods: doWork, calculatePay and toString. Using
polymorphism I can invoke the overridden methods for each type of worker when calculating
their total project cost which is made up of wages and expenses. Polymorphism is also used
when each subclass overrides the toString method and the JVM decides which specific
version of the toString method to invoke.
One of the requirements for this project is to have the ability to compare two projects and
identify which projects should be started first. To provide this comparison, the Project class
implements Comparable and then overrides the compareTo method.

The Project class


The project class in this application will do most of the heavy lifting. For this class, I have
decided to include the following instance data:

• start date
• end date
• customer name
• project description
• address location of the project
• list of workers
The constructor for this class is setup to take in the project name, owner name, address of
the
project, and the start/end dates of the project. Since some projects might not have start and
end dates assigned yet, there are two overloaded constructors for this class. Listing 18.1
shows the first part of the Project class, I have split it into pieces to better describe what is
occurring in each section.

Next, I have included a method to add the workers to the project using an ArrayList so I can
easily add/remove/update the workers assigned to this project. There is also a method to
calculate the cost based on the workers payroll plus any overhead amount for the general
contractor. This general contractor does not earn an hourly wage, instead she assigns an
overhead percentage which is added to the total cost for the project. This covers her time
and expenses. The overhead amount is normally 10%, but the application has the ability to
change that value depending on the degree of difficulty of the project.

Notice in the method to calculate the Project cost, I used an enhanced for loop to obtain the
information about all the workers in the ArrayList. This is helpful if you are not sure how
many workers are assigned to each project, the code loops through the ArrayList and stops
when it reaches the last worker.
In the project class, notice that it contains two overridden methods. The first is the
compareTo method, this is required when you implement the Comparable interface from the
Java API. In this class, I have provided logic to compare the start date of any two projects.
In the compareTo method, the project compares the start date of the project that is invoking
the method and passing the other project as an argument to the compareTo method.
By implementing the compareTo method in the Project class, it allows me to easily
compare two projects. For this example, I am comparing the start dates of each project, but
this method can easily be updated to compare other components of the project. Just
remember that the compareTo method returns and integer value with the following values:
• negative number if the first object comes before the second object
• zero if they are equal
• positive number if the first object comes after the second object
This gives the programmer control on what is compared and what determines the order. We
could change our project to compare two projects and identify which client name comes first
alphabetically for example.
The second overridden method is the toString method. Remember, every class
automatically inherits from the Object class which has a toString method. If I did not provide
an overridden method, any time I called the toString method it would simply return the
reference address of the object. Instead, in the Project class, the overridden version of the
toString method formats and returns all the information about a project for printing.

The Address Class

The Address class is used to create an address object for either the worker or the location of
the project. It is designed to allow for two types of addresses, one with only one street line
address, and a second constructor for addresses with a second street line. The second
street line is often used for identifying an apartment number or even a PO Box.
The Worker class

Now that we have the Project class created, the next class I will add is the Worker class and
it contains all the general information about a worker. This class will act as a superclass to
the electrician, plumber and carpenter classes. Both the doWork() and the toString methods
will be overridden in each subclass, these are examples of polymorphism.
The Carpenter Class
The carpenter class extends the Worker class. One of the differences between the
superclass worker and the carpenter class is that it allows for the additional cost for lumber
materials. It also overrides the doWork() and toString() methods which is an example that
enables polymorphism. This happens when a Worker object is assigned a reference type of
a Carpenter object and then invokes any of the overridden methods. The JVM first attempts
to invoke the method in the Carpenter class, if it is not found, then it looks for the method in
the superclass.
The Electrician Class
The Electrician class also extends the Worker class. For this activity, I’ve included a variable
to allow for additional expenses such as associated wiring costs. Since the Electrician class
extends the Worker class, the constructor takes in the values for name, address, id number,
hours and rate and uses the keyword super to assign these values to the instance data. This
can be helpful especially if we need data validation, for example, if we want to make sure the
hours and rate are not negative. This logic can be included once in the Worker class and
then each subclass benefits from this data validation.
The Plumber Class

The Plumber class also extends the Worker class. There are definitely more differences
between these types of workers, but to keep the code to a minimum I’ve tried to keep it
simple. So, the Plumber class is very similar to the Carpenter and Electrician classes. But
I’ve
added a variable for any additional costs associated with plumbing materials.

A future version of this application might include more information for each type of worker.
There are definitely more differences between each type of worker, but this gives you an
idea of how the project can be started.

Main Application

The main application for this contractor application is used to do the following:

• Create Address objects for two customers


• Using the LocalDate class from the java.time package from the java API to create date
objects for the start/end dates for each project
• Create two projects, one for a new house and one for a small project to add outdoor
motion lighting
• Create Address objects for all the workers, the last worker uses an overloadedconstructor
since this worker has a second address line for the apartment number
• Add three workers, an electrician, plumber, and carpenter that can be used on these
projects
• Add these workers to an ArrayList so we can assign them to a project
• Add all three workers to the house project
• Set the lumber costs to $2000
• Set the general contractor overhead to 18% for this large project
• Print out the project information
• Repeat the process for the small outdoor motion lighting project
• Compare the start dates to determine which project needs to start first and print the
appropriate message

Now that all the classes are created, we turn our attention to the main method. The goal of
the main method in this example includes logic to demonstrate all the topics covered in this
unit. A few important concepts are shown in Listing 18.7 (lines 28-30) where the Worker
class is used to create a reference variable that points to a subclass object. These lines are
enabling polymorphism.
Since all the workers are created using the superclass Worker, line 37 shows how I had to
cast the worker as the specific subclass that was used when it was created to allow access
to methods defined in the subclass that were not overridden methods.
Before reviewing the code for the main method for this application, here is a printout of
some sample output:
Next is the second part of the main method. This is where the workers are added to the
project. The first project uses all three types of workers and extra costs are assigned for
each
worker. The second project only requires an electrician, so the plumber and carpenter are
removed from the list of workers and the wiring costs are updated for the electrician prior to
creating the project. Finally, the projects are printed using the overridden method toString.

Pass by Value vs. Pass by Reference


In figure 19.1, I have identified the key terms that are used when calling a method. Methods
are designed to allow the calling program to pass in values as arguments. In this lesson, I
will review scenarios that pass primitive data types, Strings and other objects. For each
scenario, I will identify if it is a pass by value or pass by reference.
Passing Primitive Types

As I stated in the introduction, method arguments are either considered pass by value or
pass by reference. If the data type of an argument is a primitive data type, then it is
considered a pass by value. For primitive data types, the original argument value is never
changed by the method. This lesson explains what happens to the argument values and
parameter values before, during and after the method executes.
Let’s look at an example. The output in figure 19.2 is from the code snippet in Listing 19.1.

Listing 19.1 shows the code snippet used to create the output in figure 19.2. The variable
value from the main method starts as the value 10.0. When it is passed to the
doubleMethod, the value of the argument variable on line 14 is copied into another double
variable also called value on line 4. The method then uses the parameter variable and
multiplies it by two, then returns the new value to the main method which is then stored in
the variable doubledValue.

In this example, I added print statements to the main method before and after the call to the
doubleMethod. Once the method returns control to the main method, the parameter variable
value is no longer accessible.

Figure 19.3 shows how the memory is updated for each of the labelled statements, #A, #B,
#C and #D. Starting with line 9, two variables are created in memory and initialized with
numeric data: value = 10.0 and doubledValue = 0.0. Next, line 14 passes the value of 10.0
to
the new parameter variable defined for the method on line 4, this appears in the
doubleMethod section of the diagram. This value is multiplied by 2 and returned to the
calling program on line 5. At that point, the value 20.0 is then placed in memory for the
variable doubledValue. Once the return statement is executed, the method variable called
value is no longer accessible and this is indicated in the diagram with a circle and strike
through line.

Every argument value is copied into the corresponding parameter, but if it is a primitive data
type the original argument value is never changed by the method. The method creates a
local copy of the value that can be manipulated inside the method, when control is passed
back to the calling method, this value is no longer available.

This process works the same for all primitive data types. Remember, a method can contain
parameters of multiple primitive data types or even multiple parameters of the same data
type. It can also contain a mix of primitive data types and object references. This section
describes what happens to the variables defined as primitive data types. At the end of the
lesson, I will review an example that has both primitive and object reference data types.
Each time the method is called, the argument values are copied to the corresponding
parameter variables. Once the method returns control to the calling method, the parameter
variables are no longer accessible.
Before moving on to object reference types, let’s look at one more example that has multiple
parameter values. Figure 19.4 shows the sample output from executing the code snippet in
Listing 19.2

In this example, the method name is calculateCost and it has two parameter variables for
quantity and cost. The quantity variable is of type int and the cost variable is a double. Both
of these variables are considered pass by value. The method multiplies the two values and
returns the total cost to the calling program. The method also sets the values to zero after it
calculates the total cost. Since all the values are pass by value, this does not change the
values in the main method.
In the next section, I will explain what happens when the value is an object reference. In this
case, the method copies the reference address and creates an alias to the location of the
information for that object in memory. So, when the information is updated, and the method
returns control to the calling program, the object values are still updated.
Passing Reference Types

Next, let’s review the second type of argument passing, pass by reference. Here is an
example of a code snippet for pass by reference. Figure 19.5 is a screenshot of the output
from the program in listing 19.4.
Pass by reference occurs any time the argument variable contains an object. The term pass
by reference refers to the fact that the reference address of the object is copied from the
argument value into the corresponding parameter value.
In this example, there is an Employee class that contains the first name, last name, and the
hourly rate for each employee. In the main method, an Employee object is created and the
reference address is stored in the variable e on line 21. The reference address to the
Employee object is then passed to the updateRate method along with a new value for the
hourly rate on line 23.
The updateRate method has two parameter variables, one for the reference value to the
employee object called emp and a second parameter variable for the new hourly rate called
newRate. Now that the updateRate method has a copy of the address for the employee
object, it uses that address to find the memory location of the employee object and then it
updates the hourly rate. When the method returns control to the calling program, the
reference address to the employee object is not changed, but the hourly rate value stored at
that reference is now updated to the new rate.
I also added print statements to show the value of the employee object before and after
calling the method to update the hourly rate. Figure 19.5 shows the output from executing
the code in Listing 19.4 and is reprinted here for easy reference.

This printout shows the values of the employee before and after calling the update
hourlyRate method. Notice that the hourly rate is updated in the second print statement
which occurs after the call to the updateRate method.
To help understand how pass by reference works, figure 19.6 shows a diagram of how the
values are stored in memory. In this figure, an Employee object is created with a String for
the first name and last name and a primitive data type for the hourly rate. The reference
address of the Employee object, e is copied to the parameter variable called emp. At this
point in the program execution, e and emp are pointing to the same object, that is, the same
location in memory. Next, the updateRate method changes the hourlyRate for the emp
object to 15.50. When control is returned to the main method, the employee object, e, also
shows the new rate since it refers to the same object in memory as emp.
In this example, an object was created and the reference address to the object was passed
to a method. This is actually copying the address from the calling method argument to the
parameter variable so they both contain the same value, the location of the object in
memory.
In our example, the location value is never changed, but if any of the values at that location
are updated, the updates will persist when the method ends and returns control to the calling
program. This can be confusing, so try to remember that arguments containing primitive data
types cannot be altered by the method, but arguments that contain an object reference
address can have the contents of the object updated the by the method.
Collections in Java

• List – contains an ordered set of objects that can have duplicates (note: ordered does
not necessarily mean sorted), for example: [Jeep, Chevy, Ford, Jeep]

Set – a collection of objects that does NOT contain any duplicates, for example: [Jeep
Chevy, Ford]

• Queue – a collection that uses a specific order for adding and removing elements, such
as first in, first out (FIFO), for example: [customer3, customer2, customer1]

• Map – this type uses a map to associate unique keys to a value in the collection,
therefore it does NOT allow duplicate keys, for example: (zipCode, cityName), (17084,
"Reedsville")

Each interface in the framework has one or more classes that implement the interface.
Figure 21.1 shows a diagram of the Java Collection Framework where you can see the
interfaces and the classes that implement the interfaces. In this diagram, I have only listed a
subset of all the available classes and interfaces, but these are the most widely used. In the
next few lessons, I will review the four major interfaces: List, Set, Queue, and Map.
Understand when to use the List collection

Follow each question by answering yes/no until you reach the appropriate collection type.
For
example, if we have a list of customers that have ordered items online, we can follow the
decision tree to determine which type of collection to use to hold the list of customer orders.
This list does not contain a key/value pair, so next we check to see if duplicates are allowed.
Since a customer may return and purchase more items, duplicates are allowed. Next, do we
need to access elements at a specific index, for this example, we choose the yes path. We
want to fill the orders on a first come, first serve basis, so we continue to the question about
a significant amount of data. Again, we might have a high volume of sales and new
customers are frequently added and removed once their order is filled, so we end up with a
LinkedList.
Here is a sample code snippet using a LinkedList:

List<String> customerList = new LinkedList<String>();

These are not strict rules that have to be followed, but this helps to choose the most efficient
collection type for your application.

ArrayList, LinkedList and Vector

One important thing to remember when working with collections, is that you cannot
instantiate an interface.
For example, the List interface must create a new list as either an ArrayList,
LinkedList, or Vector. When using a list, the user has control over where each element is
added to the list, which element to delete, etc. The user can also access specific elements
using their index value in the list. It even has a method to search for an element in the list.
A few key points about a List in Java:

• The List interface allows for duplicate elements


• Elements can contain null values
• The indexes start at position zero
• List supports Generics, which are discussed in Lesson 24

We can create any of these list types with a variable declared as a List reference type since
it is the super class for each of these subclasses. Here is a code snippet that uses an
ArrayList to hold the color values of the rainbow:
Lesson 6 has more detailed information on how to create and use ArrayLists since it is a
commonly used data structure for storing lists of data. An ArrayList is considered a
Collection since it implements the List interface.

NOTE: Unlike ArrayLists, arrays are not part of the Collections framework.
A LinkedList uses a doubly linked list to store the elements in the list. Let’s start by looking at
an example of a singly linked list that contains test scores for four students on a recent test:

In this example, each node in the list contains a value and a pointer. The pointer contains
the
reference address to the next node in the list. If the value in the next node is NULL, then it is
at the end of the list. As you can see, this type of list only allows you to go in one direction.
You can start at the head of the list or if you have the reference address to an element in the
list, you can access that element and iterate over the remaining elements, but you can’t go
backwards.
The LinkedList data structure in Java works the same way but it allows you to go forward
and backward through a list of elements. That is why it is considered a doubly linked list.
When using a LinkedList, each element stores a reference location for the next and previous
element in the list. The first and last element are identified by looking at the reference value,
the first element has a null pointer for the previous element, and the last element has a null
pointer for the next element. There is also a reference value to the start of the list, referred to
as the head of the list. One important difference about a LinkedList is that it does NOT use
index values to reference objects. Here is a code snippet for creating a LinkedList and a
diagram of the list that gets created:
The next list type is called a Vector. A Vector can grow and shrink as needed, and it can be
referenced using index values. The Vector Class is a carryover from older versions of Java.
A key difference is that vectors are synchronized in Java. This means that they are thread
safe, any method that uses a Vector will not allow access until the method returns control to
the calling. A Vector is similar to an ArrayList, but it is considered thread safe. If your
program is not using threads, this should not be a problem and you can use an ArrayList.

In this code, I start by creating a new Vector of Strings. Next, I added three furniture items
to the vector. Remember, vectors work very similar to an ArrayList, but they are
synchronized. So, each application thread can only access the elements in the vector when
all other threads have released the objects.
Consider a library. You can go to the library and check out a book. While you are reading it,
no one else can check it out. When you are done, you return it to the library and now the
next person can access the book. This is similar to the concept of synchronization in Java.

Common Collection Interface Methods


Since List, Set, and Queue all implement the Collection Interface, I wanted to provide a list of
the methods in the Collection Interface. These methods are automatically inherited by each
of these subcollections. Table 21.2 lists a subset of the methods that are included in the
Collection Interface.
The methods listed in Table 21.2 are from the Collection Interface. In addition to this set of
methods, each of the interfaces that implement Collection also have additional methods. For
example, the List interface allows for adding an element to a certain index value: boolean
add(int index, E element).

For each of the common methods, Table 21.3 shows a sample code snippet for an ArrayList,
LinkedList and Vector. The examples use the code in Listing 21.2. For this example, each
row uses the list from the previous row as a starting point.
The table starts with the ArrayList, LinkedList, and Vector from listing 21.2. To help
demonstrate the methods, I made the ArrayList a list of String objects, the LinkedList
contains Integer values, and the Vector contains Character values. The third column shows
the result of the list after each method statement is executed.

Sets in Java
In this lesson, we are concentrating on the Set interface. Lesson 21 introduces the Java
Collection along with an overview of all the interfaces and classes that extend from the
Collection Interface. Figure 22.1 shows the Java Collection Framework with only the Set
interfaces and classes. As you can see, there are two classes that extend directly from the
Set interface: HashSet and LinkedHashSet. The TreeSet extends the SortedSet interface
which extends the Set interface. The TreeSet is different since it requires the elements to be
sorted, but it still follows the rule that all elements in a Set must be unique, no duplicates.

Understand when to use the Set collection

Choosing the right Collection can make a big difference in your application performance,
readability, and reuse. To help decide which Collection to use, I’ve created a decision tree as
seen in Figure 22.5. This time the highlighted section identifies the criteria for using the Set
interface.
The highlighted portion of the decision tree indicates the questions used to choose a Set
type.
Follow each question by answering yes/no until you reach the appropriate collection type.
For example, in our Consider This section, we start with the Set interface since there can be
no duplicates. Next, is the order important? In our example it is important since we want the
animals to be in alphabetical order. The last question is whether or not it needs to be sorted,
and we answered yes, so we would use a TreeSet.
These are not strict rules that have to be followed, but this helps to choose the most efficient
collection type for your application.

HashSet, LinkedHashSet, and TreeSet

One important thing to remember when working with collections, is that you cannot
instantiate an interface.
Table 22.1 provides information about each of the Set classes: HashSet, LinkedHashSet,
and TreeSet. One common attribute to all Set collections is that they do not allow for
duplicate values.
If you have a list of elements and you only want to identify the unique items in the list, you
can add the list collection to any of the Set types listed above and only one copy of each
unique element will be added. Code Listing 22.1 shows an example of using a Set to create
a unique list of elements.

In this example, I have started by creating a list of four customers, including one duplicate
customer. Then I created a HashSet and added the list of customers to that set. This allows
the code to automatically remove any duplicates. The output from this code snippet shows
the original list and then prints the elements in the HashSet:
[customer1, customer2, customer3, customer1]
[customer2, customer3, customer1]

As you can see, the second line of output only contains one element for customer1. It is also
noticeable that the HashSet does not retain the insertion order of the elements. The next
three sections take a closer look at each type of Set.

HashSet

We can create any of these set types with a variable declared as a Set reference type since
it is the super class for each of these subclasses. Listing 22.2 creates a Set reference
variable, numbers, that is instantiated as a HashSet. Next, the for loop adds the numbers 12
through 102 . The output from this code might look like this: [16, 64, 1, 49, 81, 4, 36, 100, 9,
25].
The HashSet makes searching for an element quick, line 14 searches the collection for a
specific value, in this case 100, which returns true.

LinkedHashSet
The difference between a LinkedHashSet and a HashSet is that it allows us to store the
elements in the order they are added to the set. The LinkedHashSet data structure in Java
works similar to the LinkedList from Lesson 21. It allows you to go forward and backward
through a set of elements. Here is a code snippet that uses a LinkedHashSet to store the
same values from the HashSet example: 12 through 102.

Listing 22.3 creates a Set reference variable, numbers, that is instantiated as a


LinkedHashSet. This time the output is in insertion order producing a different output than
the HashSet: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100].
The linked list shown in figure 22.4 shows how the elements are inserted into the set based
on insertion order. Since the set is a linked set, it follows the same rules as the LinkedList,
creating a double linked set of elements.

TreeSet

Code listing 22.4 uses a TreeSet to hold a list of animals at the zoo:

#A Create a Set reference variable to a TreeSet


#B Add random items to the TreeSet which stores the elements in sorted order

Listing 22.4 creates a Set reference variable, animals, that is instantiated as a TreeSet.

Three

animals are added to the set. Since the TreeSet stores the elements in sorted order, the
output is: [Bear, Lion, Tiger]. The TreeSet collection can be created using the Set interface,
the SortedSet interface and the class name TreeSet. Listing 22.5 is similar to the code listing
from 22.4, but this time there are two differences. On line 9 I have changed the set type from
Set to SortedSet and added an additional statement on line 13.
Code Listing 22.5 sample code to create a set of values in sorted

In Listing 22.5, since the animals set does not allow duplicates, the statement on line 13 to
add Bear to the set a second time is ignored and this code produces the exact same result:
[Bear, Lion, Tiger]. When adding a duplicate element, it does not throw an exception error,
instead it returns true if the element is added and false if the element already exists and was
not added a second time.

Common Collection Interface Methods


Since Set, HashSet, LinkedHashSet, and TreeSet all implement the Collection Interface, I
wanted to provide a reference to the common methods in the Collection Interface. These
methods are automatically inherited by each of these sub-collections. Table 22.2 lists a
subset of the methods that are included in the Collection Interface.
In Table 22.2, I wanted to take a closer look at the retainAll method. This method is similar to
the intersection of two sets. So, only elements that occur in both sets are returned from this
method.
The methods listed in Table 22.2 are from the Collection Interface. In addition to these
methods, each of the interfaces that implement Collection also have additional methods. For
example, the SortedSet interface contains additional methods, for example: subset, headset
and tailSet, first, and last. You can find all the methods for each class in the Oracle
JavaDocs:
https://docs.oracle.com/en/java/javase

To demonstrate how to use these methods, I have created a code example, shown here in
Listing 22.7, that demonstrates how to use these additional methods for a SortedSet.
The output from code listing 22.7 is shown below in figure 22.5. Make sure you look closely
at the output, it might be different than what you expect.

At first glance the output looks incorrect but remember that a TreeSet adds the elements in
sorted order to the set, it does not maintain the insertion order. So, in this example, the first
element is not Red, instead it is the first color alphabetically which is Blue.

You might also like