In the previous lesson we tidied up the Animal constructors.
Packages, the Object class, and interfaces
Packages allow you to partition your classes into sets of logical groups, and enables you to protect parts of your application from other parts. The Object class provides the base from which all classes in Java, including your own, derive their functionality through inheritance. Interfaces provide a means of specifying types having specific capabilities.
In this section you will learn:
- How to use packages.
- The Object class.
- Using the this and super keywords.
- Using interfaces.
Packages and imports
A package is a named area used to group classes (or other files) that are logically related in some way. They are typically implemented by associating each with a unique folder or sub-folder (i.e., directory or subdirectory). While it is possible to create classes that are not in any package it is strongly recommended that you use packages for all your classes. All Java supplied classes are in packages, and all of the classes you have developed so far have been placed in one of the user-defined packages called virtualzoo or
.core.
You have also previously learned that for all classes that are in a package there must be a package declaration statement as the first non-comment line, for example:
package virtualzoo.core;
- To follow naming conventions, package names should be in all lower case, with compound words run together.
- You can define packages within other packages, using dots to separate them, for example
virtualzoo.corewould mean that within thevirtualzoopackage there exists a sub-package calledcore. This is useful for partitioning your classes into logically related areas, and later in the course when you develop graphical user interface classes you will make use of packagevirtualzoo.uifor these classes.
If you look at the Java APIs you will be able to see all of the supplied packages and their contents. If you select String from the bottom left-hand frame you will see in the main section that the String class is within package java.lang.
Other supplied packages are java.io (for input/output classes), java.math (for mathematical classes), java.util (for utility classes), and many more.
In order to use a Java supplied class you need to import its package into your source file, except for classes which exist within java.lang which contains those classes so frequently used that it is automatically imported. An example of such a class is String: you didn't need to specify an import since it is defined within java.lang.
Suppose the zoo wishes to record the date each animal is admitted to the zoo, which for simplicity you can assume will always be the date the animal's object is instantiated. There is a Date class supplied within the java.util package that will serve to store this date, so the first step is to import it into the Animal class. Insert the import line below:
package virtualzoo.core;
import java.util.Date;
public abstract class Animal {
... rest of class...
Note that the import statement must be placed after the package statement but before the class declaration statement. Now that you have specified the import your class can make use of the Date class. Date is just one of a number of classes within the java.util package. If you need to use several classes from the same package it is possible to specify a wild card import in the following format:
import java.util.*; // makes any class within java.util available
The asterisk indicates that you might want to use any class within the java.util package. Java only imports the classes you use, however, so there is no penalty associated with using the wild-card technique.
At this stage, it would be useful to modify the import statement that appears in class VirtualZoo to use the wild-card technique, as follows:
import virtualzoo.core.*;
The above change will allow VirtualZoo to access any class in virtualzoo.core rather than having to import Animal, Lion, Penguin and Monkey individually. You can therefore delete the following statements from within VirtualZoo:
import virtualzoo.core.Animal; import virtualzoo.core.Lion; import virtualzoo.core.Monkey; import virtualzoo.core.Penguin;
And replace them with this one statement:
import virtualzoo.core.*;
- Returning to the Animal class, now that you have specified the
importstatement you can define a new instance variable to store the admission date:
// Declare instance variables to hold an animal's attributes
private String name; // the animal's name
private String gender; // the animal's gender ("m" or "f")
private int age; // the animal's age in years
private Date dateAdmitted; // date animal was admitted to the zoo
Because the admission date is always assumed to be the date the animal object is created there is no need to pass an additional argument into the constructor. However, the constructor does need to initialise the new instance variable, so it holds today's date:
public Animal(String myName, String myGender, int myAge) {
name = myName;
gender = myGender;
age = myAge;
dateAdmitted = new Date(); // today's date is assumed
}
The statement new Date() invokes the zero-argument constructor of the Date class which creates a Date object containing today's date. This object is then assigned to the instance variable dateAdmitted.
You can look at the API documentation to see detailed information about the Date class. You will note that Java is supplied with two classes named Date, the one in java.util you have just used and a different one in package java.sql. This demonstrates another useful aspect of packages: you can distinguish classes of the same name by referring to their package name.
- If you look at the API for
java.util.Dateyou will see that it has a number of constructors, although several are deprecated. This means that they have been superseded by better approaches, but this course does not require the use of them.
Because the Animal class takes the view that the dateAdmitted instance variable uses the day the object was constructed as the admission date, you want to prevent client objects from modifying this date. Therefore, you will not provide a "set" method to change its value. However, the variable is correctly private, so for client objects to access it you need to provide a "get" method for it.
Here is a first attempt at a getDateAdmitted() method:
// Return the date the animal was admitted to the zoo
public Date getDateAdmitted() {
return dateAdmitted;
}
At first sight this seems reasonable – it follows the same pattern as the other getter methods defined within Animal. There is, however, a potential problem. Enter the following statements into VirtualZoo (you will need to import java.util.*):
Animal leo = new Lion("Leo", "m", 4);
// Output Leo's admission date
Date date = leo.getDateAdmitted();
System.out.println(date);
// Change Leo's admission date!
date.setYear(150); // gets added to 1900 - see Date class API
date = leo.getDateAdmitted();
System.out.println(date);
TIP:
NetBeans contains a useful short-cut entry to generate a System.out.println() statement: Type sout and press the tab key on your keyboard.
The first time you output the date it will show today's date, but then the client object invokes the setYear() method upon the returned Date object and modifies the admission year value to be 2050. This modifies the dateAdmitted instance variable since its reference points to the same object which has just been modified.
In the above code the getDateAdmitted() method is invoked a second time. This is not actually necessary, but done here to demonstrate that the value inside the Animal object was indeed modified.
You will remember, though, that the intention was not to allow this date to change – it should be set to today's date inside the constructor and remain unchanged thereafter. The instance variable is defined as private and there is no setter method for it. The problem has arisen because the Date class is mutable, that is, it has methods which modify its own state. So when your getDateAdmitted() method returned a reference to it you were also returning the capability of modifying it.
This problem does not occur with the previous getter methods defined within Animal because:
- The
nameandgenderinstance variables are of typeString, andStringobjects are immutable, that is, there are no methods that can modify its value after it has been constructed. - The
ageinstance variable is of the primitiveinttype. When you return a primitive type, the caller gets its own copy of its contents so again the variable inside theAnimalobject cannot be modified.
The solution is for the getDateAdmitted() method to return a defensive copy of the dateAdmitted object in order to prevent a privacy leak. This requires constructing and returning a completely new object, although it will be set to have the same value as dateAdmitted.
If you look at the Date API, you will see two things:
- There is a constructor for the
Dateclass which accepts alongprimitive argument type representing the date in milliseconds. - There is a method called
getTime()which returns alongprimitive value containing the date value represented in milliseconds.
Therefore, if you get the date in milliseconds from the dateAdmitted instance variable you can use that value to create a separate object set to that value. Modify the getDateAdmitted() method as follows:
public Date getDateAdmitted() {
// Return defensive copy
long time = dateAdmitted.getTime();
return new Date(time);
}
It is possible to combine the two statements into one and remove the need to allocate a named variable:
public Date getDateAdmitted() {
// Return defensive copy
return new Date(dateAdmitted.getTime());
}
If you run VirtualZoo again you should find that the same date is output both times. The setYear() method is operating on a separate copy and therefore has no impact upon the state of your Animal object.
In the next lesson you will learn about the Object class.
Comments