Objects can be almost anything in Java. Everything in the language is designed to use small, somewhat self-contained pieces of code, or objects. Objects use the fundamentals such as declarations and expressions to do the real work of the language. The idea is to use these pieces of code in appropriate ways so you don't have to keep rewriting them for each use. This is why it's called "object-oriented" programming. (This concept is discussed in Chapter 1, "Introducing Java.")
The smallest logical unit of Java is the object. Everything else is merely a grouping of objects on a larger scale. This chapter describes how to create and use objects. It explores the structure of Java in greater detail than the previous chapter. Chapter 6, "Fundamentals of the Java Language," covers the smallest parts of objects, such as primitive data types and control flow keywords; this chapter shows you how to put the small pieces to work in a structural framework.
To create applications with Java, it is essential to understand how the parts fit together. Here you'll explore the grouping of objects into classes. Classes are given characteristics through inheritance. Class libraries are groups of classes. Packages are groups of class libraries. Methods are functions that perform activities within classes. Methods can have templates managed by interfaces.
These are abstract concepts. There is tremendous power for application flexibility in Java, but it is easy to get lost in terminology. This chapter is full of examples to make the concepts concrete and easy to understand. A conceptual example involving an apartment building is used throughout the chapter to demonstrate the structure of Java.
By the end of the chapter you should have a good idea of the relationships among the parts of Java and how to use them effectively.
Classes are made up of objects. This is easy to understand if you are familiar with object-oriented programming. If you are not, then what is an object? Think of an object as a unit that can be made up of other smaller units. Think of an apartment building, for example. It is made up of apartments. Each apartment probably has doors, windows, a kitchen, a bedroom, and a bathroom, among other things. Each apartment is a unit. Sometimes an apartment building is referred to as a five-unit apartment building.
The apartments do not all have to be exactly alike. Some may be on the first floor, some may be in the basement, some may face south, and some may be recently refurbished. One apartment may have two bedrooms and another, just down the hall, only one. Each apartment has its own mail slot with an individual address. Even though the apartments are not all identical, they are all apartments.
Apartments are the objects in this example. Even though they have smaller parts and are not identical, conceptually each is a unit. The apartment building is the class. It is made up of objects, or units. These objects are not all exactly alike, but they have enough similar characteristics that they can be classed together.
Another term useful in object-oriented programming is instance. Apartment building 3 is an instance, or actual apartment building. It is real. An instance is one specific object within the class of objects.
Each apartment has more interesting information. One might be empty. In this case, the empty status is the apartment's state. A rental-application program could keep track of available apartments on the basis of this state. A method would be associated with testing for the state of this apartment.
Now let's take these ideas to another level of abstraction. Have you ever driven by a huge apartment complex, say, outside a university? The complex is made up of apartment buildings, which are made up of apartment units. The whole complex can be referred to as Countrybrook or something else equally romantic and descriptive. The complex, then, is the conceptual gathering together of the apartment buildings in the area even though each apartment building is slightly different, with different addresses and other characteristics that make them unique.
The apartment complex is an example of one of the classes in a class library. A class library is a set of classes. Class libraries group classes that perform similar functions but are dissimilar enough to warrant their own classes. Recall that the apartment complex is one class. Suppose right down the street is a mall. The mall is made up of smaller units-stores. The mall is another class in the class library. It is a building, but it has quite a different structure and function than the apartment building. It could be set up as its own class.
Java comes with a set of class libraries that handle many tasks, such as input/output, screen painting, and mouse clicks. The class libraries of Java are its heart and strength. In fact, this is such an important subject that five chapters in this book are devoted to it. See Part IV, "The Java Application Programming Interace," for more information on the Java class libraries.
At this point it is important to begin to understand the concept of objects, libraries, class libraries, and packages. These concepts are the structure of Java.
Classes are made up of many different parts, such as methods and variables. Think back to the apartment example. The class is a template for information about a group of things, even though the individual things may be somewhat different. Classes also contain methods for obtaining information about the state of a member. Each real member of a class is referred to as an instance.
Methods are functions that report back a status. Methods can report back with a value such as an integer or a boolean. Methods can be called by other classes outside their own immediate class.
A superclass is the top-level class of every application. In Java, it is automatically the Object class. This means that if a class does not implicitly declare its superclass, or next higher class level, then it is a subclass of Object. (Declarations are discussed in the section "Declaring a Class.") Every instance of a class reimplements the definitions and logic created by the superclass.
Subclasses extend the superclass and create a new variation of the class. They inherit the characteristics of the preceding class.
A class must have a valid identifier name; it can begin with an alphabetic character, the underscore, or the dollar sign. When you think of an appropriate name, use this syntax for declaring a class:
class MySuperClass.MyClass {
// class body
}
The class body is declared within curly braces. Notice that the name of the class has the superclass and . before the class name. (This is also how inheritance, discussed subsequently, is shown.) This is how the compiler knows where to go for information on the class.
The name associated with a class has the same restrictions that identifier names do; that is, they can be named anything as long as they begin with an alphabetic character, a dollar sign, or an underscore. By convention, however, a class name should begin with a capital letter. This makes the class easily distinguishable from methods, variables, and so on. Java itself follows this convention; it is highly recommended.
Java produces two program types: application and applet. Applications are stand-alone and can be run directly from the command line. Applets require an external program to provide an interface to the user. Web browsers and the Java Developer's Kit (JDK) applet viewer are examples of interfaces that support applets.
Applets are also more restricted in Java in what they can do. Because Web browsers are a common method for accessing applets, what the applet can do to the local system is a security concern. Therefore, applets are prevented from reading or writing local files to disk, accessing memory directly, and opening connections to other applets that do not reside on the same server as the current applet.
This provides basic security, such as preventing an applet on an unknown Web page from automatically getting a copy of your password file and sending it to the author of the applet. However, Java allows some of these restrictions to be reduced. Using the applet viewer, you can allow an applet to have read and write access to a particular directory or set of directories. This is potentially dangerous and should be used only with caution.
Note |
The actual mechanism that provides security for applets is in the client application running the applet (for example, your browser or the applet viewer). This mechanism is usually the applet security manager. For more information on the applet security manager, refer to Chapter 5, "Java Tools and the JDK: A Primer." |
Applications do not have these restrictions. Users running Java applications are allowed to access any files, memory locations, or network resources that they normally could.
Java applications are true applications, like those developed by any other language, such as C or C++. The idea of Java applets is popular at this writing, but Java applications have received little fanfare. Sun Microsystems is trying to change that perception. Sun anticipates the Java programming language to replace C++ as the development language of choice, for several reasons.
One reason is that it is a more streamlined language but still offers much of the power of C++. Java was developed by specifications in one organization; it has not just grown in an ad hoc manner like many other programming languages. Therefore, parts of Java fit together nicely. Java has eliminated some of the redundancy found in C++.
Another reason is that Java is platform independent with regard to programming and maintenance. An organization can write a Java application that will run on every platform for which a Java interpreter has been written. This is a huge win for companies that must support multiple platforms.
Think about the way companies run today. They probably support multiple platforms internally. They may want to sell applications to run externally on a client base that supports multiple platforms as well. It is not a rare occurrence to find users with multiple terminals on desks to support and access a variety of applications. Imagine being able to write one application that runs on a company's pcs, Macintoshes, mainframes, UNIX systems, and whatever else is available. You can do this with Java.
So, you ask, what is the drawback? The current problem is speed. Java can run significantly slower than a natively compiled application that converts source code into machine code. Performance relies on many factors. However, it is difficult for an interpreted language to compete with natively compiled code.
Performance issues will probably be overcome in the near future with optimized Java interpreters, or just-in-time compilers, which are slowly becoming available. There is also talk of writing native compilers that will allow code to reside on local systems. (Entrepreneurs, anyone?) The problem with this solution is the loss of the instant-updating features of Java. It's also possible that the performance hit will be acceptable because of the niceties of Java portability. Giving up the performance of machine code for the development ease of a higher-level language like C code was also deemed acceptable, although some purists complained. Now is the time to move to the next higher level of language instead of lamenting the loss of C.
Note |
A software toolkit for Java that includes a just-in-time compiler is Symantec Café. Many other software companies plan to include just-in-time compilers as well. Who knows-maybe the mainstream integration of Java into operating systems will bring with it the speedy just-in-time compilers. |
Java applications are similar in structure to applets. The only major differences are method of declaration and program invocation. We have seen many examples of declarations. It may not have been obvious how the program began execution, though; this is handled by the method main.
Every Java application must define one method named main, which is similar to main in C and C++. When a Java application is invoked, the Java interpreter looks for the method named main and begins execution there. If main is not declared, the interpreter will complain.
main methods must always be declared public. It would be impossible for a program outside Java to even start the Java application if main were not declared public.
The main method itself must be declared in a fixed format:
public static void main (String args[]) {
...body of main...
}
The public declaration enables main to be accessed from external programs. static indicates that this method cannot be modified by subclasses. void means that this method does not return a value of any kind. main is the required name of the method. (String args[]) indicates that main will have command-line arguments consisting of an array of type String. This array contains the command-line arguments specified when this application was started.
Here is the standard Hello World! Java application:
public class HelloWorld {
public static void main (String args[]) {
System.out.println("Hello World!");
}
}
To compile the example, type the following:
javac HelloWorld.java
When the compiler finishes, invoke the Java interpreter as follows:
java HelloWorld
The output from the application should be as follows:
Hello World!
Applets are the most famous aspect of the Java programming language at this time. Java is associated with the World Wide Web; Java applets are powerful in that they can take a fairly static Web page and turn it into a highly interactive, animated multimedia extravaganza.
Applets in Java rely on an external program to interface with the user. They cannot be run by themselves. The Java-enabled Web browser and the applet viewer that comes with the Java Developer's Kit are two common examples of these programs.
Applets are an extension of an existing Java class, java.applet.Applet. An applet is really an example of a subclass. To declare an applet, you again use the class keyword, but the modifier extends is added. This notifies the compiler that an applet is a subclass of another class, in this case a subclass of java.applet.Applet. Here is an example declaration:
import java.awt.*; //imports the class libraries*****library*****
import java.awt.image.*;
import java.applet.*;
public
class HelloWorldApplet extends Applet {
public void paint (Graphics g){
g.drawString("Hello World!", 10, 10);
}
}
After compiling the source code for the applet, you will need to create an HTML document that will be used by your browser or the applet viewer to display the applet. Here is a sample HTML document for the HelloWorldApplet program:
<HTML>
<HEAD>
<TITLE>HelloWorldApplet</TITLE>
</HEAD>
<BODY>
<APPLET CODE="HelloWorldApplet" WIDTH=300 HEIGHT=300></APPLET>
</BODY>
</HTML>
Applets generally must be more "bulletproof" than applications because of the environment in which they run. Not only must they run, but they must be able to handle events like mouse clicks, repaints, suspensions, and others. Many things appear on the surface to be handled by the browser but in reality are handled by applet code.
An example of this is the following sequence of events: an applet prints an image on the screen. The user brings up another window that partially obscures the original image, and then removes the new window. Who is responsible for repainting the applet image? It is not the browser, but the applet. The browser will inform the applet that a repaint is needed, but it is then up to the applet to actually do the repaint or take other action.
Applets begin execution differently than applications. Applications begin program execution by calling method main. Applets instead use methods init and start. The browser will invoke the init method followed by the start method every time a Java applet is started. These do not have to be explicitly declared in the applet.
Look again at the HelloWorldApplet example. Notice that there is no call to invoke paint, yet the screen was painted anyway. Many things happen behind the scenes in a Java applet. This is an example of an applet beginning execution without an explicit declaration of methods init and start, instead relying on the default init and start methods. The default start method automatically calls the repaint method, which among other duties, invokes the paint method. paint must be explicitly declared for anything to be written to the screen.
This summarizes the series of events that occur when a viewer invokes an applet. It is important to understand this sequence if any of the applet startup methods are overridden. The applet will not execute to expectations if anything is left out. When a viewer calls an applet, first the applet calls init. This is either explicitly declared or it is the default. The viewer then calls start, which is once again either implicitly or explicitly declared. Within start is a call to repaint, which schedules a call to paint. The implicit paint method does not write anything to the screen, and therefore the method must be overridden.
The init method is only called the first time an applet is loaded into a viewer. One-time-only initializations take place in the call to method init. This is also a good place to get command-line arguments from the HTML page from which the applet was invoked. (Command-line arguments are discussed in the "Declaring a Package" section.)
The init method has a fixed format. It always must be named init, have a return type of void, be declared public, and have no arguments. An example of the init method follows:
public void init () {
counter = 0;
}
The only action in this example of an init method is to set the variable counter to zero.
An init method is not required. It actually overrides an existing init method in class java.applet.Applet; this is why the name, return type, and other information are fixed.
The start method is called after the init method the first time an applet is loaded into a viewer or if an applet has been suspended and must be restarted. If the compiler can't find a start method explicitly declared in the applet, it will default to the start method in java.applet.Applet. An example of the start method follows:
public void start() {
run = true;
while (run == true) {
count += 1;
repaint(); //note that repaint is explicitly called
try {Thread.sleep(1000); }
catch(InterruptedException e) {}
}
}
This start method sets variable run to true and then enters a while loop that will run as long as run is equal to true. The loop increments the counter that was initially set to zero in the init method. The repaint call causes the screen to be repainted after every increment. The try and catch lines cause the program to sleep for 1,000 milliseconds after every counter increment. (try and catch are exceptions covered in Chapter 8, "Tying It All Together: Threads, Exceptions, and More.")
A start method is not required. Like init, it actually overrides a default start method provided by the java.applet.Applet library. It also has a fixed format, as shown in the previous example.
The stop method is called whenever an applet must be stopped or suspended. Without the method the applet continues to run, consuming resources even when the user has left the page on which the applet is located. There may be times when continued execution is desirable, but in general it is not. An example of the stop method follows:
public void stop() {
run = false;
}
This stop method sets the run variable to false. This causes the loop in the start method from the previous example to exit the loop and fall through the end of the method. The result is that the application stops running. Note that only the run variable is changed.
Just as with init and start, a stop method is not required. It overrides a default stop method provided by the java.applet.Applet library. The stop method has a fixed format declaration, as shown in the previous example.
The method paint is used to paint or repaint the screen. It is automatically called by repaint or can be called explicitly by the applet. The applet calls paint when the browser requires a repaint, such as when an obscured applet is brought to the front of the screen again.
paint has a fixed format. It always must be named paint, have a return type of void, and be declared public. However, unlike init and start, it does have an argument, of type Graphics. This is a predefined type in Java that contains many of the methods for writing graphics to the screen. Here is an example of a paint method:
public void paint(Graphics g) {
g.drawstring("counter = " + counter, 10, 10);
}
This method writes the value of counter to the screen each time it is invoked. Combine this with the start and repaint methods. The start method increments counter and then calls repaint. The screen displays the new value of counter every time it is updated.
The compiler does not require a paint method. It overrides a default method provided by java.applet.Applet. However, if you do not override it, you will not be able to write anything to the screen.
import java.awt.Graphics;
public class Counter extends java.applet.Applet {
int counter;
boolean run;
public void init() {
counter = 0;
}
public void start() {
run = true;
while (run == true) {
counter++;
repaint();
try { Thread.sleep(1000); }
catch(InterruptedException e) {}
}
}
public void stop() {
run = false;
}
public void paint(Graphics g) {
g.drawString("counter = " + counter, 10, 10);
}
}
This method will not yet run in this form; we still need threads and exceptions (discussed in Chapter 8).
Modifiers alter certain aspects of classes. They are specified in the declaration of a class before the class name. Several modifiers have been used in the examples in this chapter.
Class modifiers are used to specify two aspects of classes: access and type. Access modifiers are used to regulate internal and external use of classes. Type modifiers declare the implementation of a class. A class can be used as either a template for subclasses or a class in and of itself. (These modifiers are subsequently described in detail.)
Classes can be declared with security so they can be accessed outside their package by using the public statement in the class declaration. If no explicit statement is made at declaration time, the class may be accessed only from within its own package. A compile error is generated if any other security modifier is used in the declaration, such as private or protected, which are reserved for methods.
The following are examples of declaring class security:
public class classname { //able to be accessed outside of
//own package
}
class classname { //only able to be accessed within
//own package
}
This is another example of code reuse in the same or different applications in Java. Java's structure makes this easy and allows for some protection if necessary.
There are only two types available to classes: abstract or default. Abstract classes must be explicitly declared.
The abstract modifier is used to create template classes. These are classes that normally are used to provide a superclass for other classes. An abstract class can contain such things as variable declarations and methods but cannot contain code for creating new instances.
An abstract class can also contain abstract methods. These are methods that define return type, name, and arguments but do not include any method body. Subclasses are then required to implement those abstract methods in which they supply a body. If an abstract class contains only methods and no variables, it is better to use interfaces (covered in the "Interfaces" section). Here is an example of an abstract class declaration:
abstract class AClass {
int globalVariable;
abstract void isBlack (boolean) {
}
}
This abstract class declares a global variable called globalVariable and defines a template for a method called isBlack. Notice that there is no body for the isBlack method. That is left to subclasses of AClass. The subclasses must implement isBlack or they will receive a compile-time error.
Chapter 6 discusses variables in a detached, stand-alone manner. This section shows how variables are used in a class.
Variables are used in classes to hold data to be used later. Good programming form places variables immediately following the class declaration statement, as in the following:
class ShowVariables {
int Int1, Int2; //declare some integer variables
int Int3 = 37; //declare and initialize a variable
char OneChar; //declare a character type variable;
float FloatArray[]; //declare single dimensional array of floating-point
boolean AmITrue; //declare boolean variable
//here would be code to do something...
}
The variable declaration statements in this example just set up the variables to be used in this class. There are no methods or expressions set up to do anything at this point.
Every class can have variables associated with it. Variables fall into two categories: those that are particular to an instance, called instance variables, and those that are global to all instances of a particular class, known as class variables. The use of the variable determines its type.
Instance variables exist only for a particular instance of an object. This means that different instances of a given class each have a variable of the same name, but Java stores different values for that variable in different places in memory. Each of these instance variables is manipulated individually. If an instance goes away, so does the variable. You can access instance variables from other instances, but the variable itself exists only in a particular instance.
Instance variables have been shown in the examples so far in this chapter. They are declared after a class declaration but before method declarations. Every instance of that class has a copy of this variable and can modify it as needed without affecting any other instance copies of the variable. Here is an example of an instance variable declaration:
class ACat {
String[] name;
String[] color;
int weight;
}
Here every instance of class ACat has a name, color, and weight variable used to store information about a particular cat. If you had two cats and wanted to put them on your Web page, you could specify the name, color, and weight of each cat in individual instances without worrying about overwriting the information.
You can modify variable (and method) declarations with the static modifier. Static variables exist in only one location and are globally accessible by all instances of a class. A variable cannot be changed by a subclass if it has been declared static and final. Further, static variables have the same information in all instances of the class.
This is a valuable tool in situations in which a variable is shared by several instances of a class and/or subclasses. All instances will have the same value for the variable. All classes accessing that variable point to the same place in memory. This variable will remain there until the last instance accessing the variable is flushed from memory.
A static variable is declared in the same way as an instance variable but has the keyword static in front of it. In the following code, the variable animalType is declared static and is thus the same for all instances of class ACat:
class ACat {
static String animalType[] = "cat";
}
In this way, all the instances can check this variable for the value of animalType. An external class can query this variable for the same information. Its value needs to be specified only once and stored in one location because the information is the same for all instances of class ACat.
Java comes with three predefined object values: null, this, and super. They are used as shortcuts for many common operations in Java.
What happens when the class being created is a superclass? A variable
can be created that is simply a placeholder for subclasses to
fill with values. In this situation, a variable can be declared
null,
meaning that no value is assigned to a variable, as in the following:
int PlaceHolder = null; //PlaceHolder is an empty object
Following is a code fragment using null:
class ACat{
static String name = null;
public void main (String args[]) {
ACat cat = new ACat();
if (cat.name == null) {
promptForName("Enter name> ");
}
}
In this example the variable name is initialized to null. It is then tested in main to see if the variable is null. If so, the user is prompted to enter the cat's name. null cannot be used with primitive data types.
To refer to the current object, use the keyword this, which allows the current instance of a variable to be referenced explicitly. This is valuable when the current instance of a variable is to be passed to another class that will also use the variable:
void promptForName (String prompt){
StringBuffer name;
char ch = '\0';
name = new StringBuffer();
System.out.print(prompt);
System.out.flush();
While (ch != '\n') {
try {ch = (char)System.in.read(); }
catch (IOException e) {};
name.append(ch);
}
this.name = name.toString();
}
The compiler understands implicitly that the current instance of a class variable is this. Do not explicitly reference it unless necessary.
super is a reference to the superclass. It is often used as a shortcut or explicit way to reference a member in the superclass of the current class. In the following code, a subclass named APersianCat uses the super keyword to reference the method promptForName in its superclass, ACat:
class APersianCat extends ACat {
void getCatInfo {
super.promptForName() :
}
}
APersianCat would look for a method called promptForName in the current class if super were not used, generating a compile error.
We have been using methods throughout this chapter, but now a better definition of methods is in order. Methods are functionally similar to functions in C and C++. They provide a way to group a block of code together and then refer to it by name. You can use the block of code again simply by referring to the name of the method. Also, the code does not have to intrude into the middle of other code.
Methods do not have an explicit declaration keyword as classes do. They do have names but also have arguments and return types, which classes do not.
Arguments are parameters that are passed to the method when it is called so that a method can be made to do different tasks. The code internal to the method knows how to manipulate the input parameters.
Methods also have a return type. This is a value that can be returned to the code that called the method. Return values can be of any valid Java type, including strings and numeric values.
Methods can be called from not only the current class, but also from subclasses, superclasses, and even entirely unrelated classes. This is the structure internal to Java that makes it so flexible and time-effective.
An example of a method follows:
public static void main (String args[]) {
...body of method....
}
This method has been used many times in this book. It is the method main, which is the first method called when a stand-alone Java application is started. In this example, public and static are method modifiers, void is the return type, main is the name of the method, and (String args[]) is the list of method arguments. This is followed by an opening { that marks the beginning of the body of the method. The method body can consist of any valid block of Java code. It can also include calls to other methods, even itself. Finally, the method body is followed by a final }.
Methods can return any valid Java type. This could be a simple boolean, an array, an object representing an entire apartment complex-anything. The return type immediately precedes the variable name of a method. The following code declares a method named isBlack to return a variable with type boolean:
boolean isBlack (Color color) {
if (color == black)
return (true);
else
return (false);
}
The method body tests whether the color passed is black. If it is, the method uses the return keyword to specify that value to return to the calling method is boolean true. Otherwise, it will use return to return the boolean false.
void is a special return type in Java that indicates that there is no return type of any kind. This is used for methods that have no need to return anything to the calling program, or that modify only method arguments or global variables.
An example of a method that does not need to return anything is one that only prints output to the screen. There is no need to return anything because there is no further processing to be done. There is no need to use the keyword return anywhere in the method if the method is declared void.
The concept of modifiers presented with classes also can be applied to methods. Method modifiers control access to a method. Modifiers also are used to declare a method's type. However, methods have more modifiers available than do classes.
Method-declaration statements provide information to the compiler about allowable access. In Java terms, accessibility is security. The five levels of access follow:
public | All other classes |
private | No other classes |
protected | Subclasses or same package |
private protected | Subclasses only |
<default> | Same package |
The private modifier specifies that no classes, including subclasses, can call this method. This can be used to completely hide a method from all other classes. If no other classes can access the method, it can be changed as needed without causing problems. Here is an example of a method declared private:
private void isBlack (Color color) {
....
}
The protected modifier specifies that only the class in which the method is defined or subclasses of that class can call the method. This allows access for objects that are part of the same application, but not other applications. Here is an example:
protected void isBlack (Color color) {
....
}
The private protected modifier is a special combination of private and protected access modifiers. This modifier specifies that only the class in which the method is defined or subclasses of the class can call the method. It does not allow package access as protected does but also allows subclasses as private does not. Here is an example:
private protected void isBlack (Color color) {
.....
}
The method defaults to an access control in which the class itself, subclasses, and classes in the same package can call the method. In this case, no access type is explicit in the method-declaration statement. Here is an example:
void isBlack (Color color) {
....
}
The static modifier is associated only with methods and variables, not classes. The static modifier is used to specify a method that can only be declared once. No subclasses are allowed to implement a method of the same name. This is used for methods, such as main, that are entry points into a program. The operating system would not know which method to call first if there were two main methods. Static methods are used to specify methods that should not be overridden in subclasses. Here's an example:
static void isBlack (Color color) {
....
}
The final modifier indicates that an object is fixed and cannot be changed. When you use this modifier with a class-level object, it means that the class can never have subclasses. When you apply this modifier to a method, the method can never be overridden. When you apply this modifier to a variable, the value of the variable remains constant. You will get a compile-time error if you try to override a final method or class. You will also get a compile-time error if you try to change the value of a final variable.
Here is how you can use the final modifier:
class neverChanging {
// a final variable
final int unchangingValue = 21;
// a final method
final int unchangingMethod(int a, int b) {
}
}
The abstract modifier is used to create template methods, which are very similar to function prototypes in C and C++. Abstract methods define return type, name, and arguments but do not include any method body. Subclasses are then required to implement the abstract method and supply a body. Here is an example of an abstract class:
abstract void isBlack (Color color) {
}
This defines a template for a method named isBlack. Notice that no body of isBlack is specified; that is left to subclasses of the class in which the abstract method is declared. The subclasses must implement a body for method isBlack, or they will cause a compile-time error.
The concept of overloading methods seems nonsensical at first. But when the idea seeps in, you will see that overloading adds to the power of Java. Why would anyone want to define methods with the same name but very different functionality? This is precisely what overloading does. You can define a method with the same name multiple times.
Java selects which method of the same name to call on the basis of the calling parameters. Every overloaded method must have a different and unique parameter list.
With Java you can overload any method in the current class or any superclass unless the method is declared static. For example, in the following code, method isBlack has been overloaded three times:
class CheckForBlack {
public boolean isBlack (Color color) {
if (color == black)
return(true);
else
return(false);
}
public boolean isBlack (String desc) {
return(desc.compareTo("black");
}
public boolean isBlack (Paint paint) {
if (paint.reflectedLight < .05)
return(true);
else
return(false);
}
}
Each method has the same name and return type, but each has a different argument list. The first checks to see if the color passed to the method is equal to the color black. The second method compares a string passed to the argument to see if it is equal to the string "black". The last method determines if the paint object passed in to the method has a reflected light percentage of less than 5%. If it does, it is considered black.
In all these cases, the method name and return type are the same, but the arguments are different. The Java compiler automatically uses the method that matches the arguments passed in the call to determine the correct method. The Java compiler issues an error if no method call has arguments to match any of the methods.
Method overloading allows for flexible use of method calls. In the isBlack example, if you need another type of test, simply add it to the existing CheckForBlack class. A Java program can make isBlack calls using the new object.
In this way, you can cover multiple types of tests with only one method name. Overriding means that the programmer does not need to come up with different names for a method that accomplishes the same thing, but perhaps in a different way. The programmer can concentrate on readability and consistency without having to worry about method names. This also means that in a large project, a programmer does not need to worry about using a name that some other programmer has already used (as long as the arguments are different).
Use of objects is one of the main differences between a procedural language such as C and an object-oriented language such as C++ or Java. In a procedural language, data is usually thought of as being distinct from the code. In object-oriented programming, the code is thought of as being one with the data.
Space is usually pre-allocated for data in procedural languages. The compiler knows the size, types, and number of variables and allocates space accordingly. The data-allocation space is fixed in memory and continues to be allocated until the application ends.
Data space is not pre-allocated in Java. Instead, it is created as needed. Space is used for as long as something is accessing the data, and then memory is automatically deallocated. This is similar to using the malloc and free system calls in C.
The biggest difference between Java and C is that there is no need to release memory explicitly after the program finishes with it. In Java, an automatic garbage-collection system finds any memory that is not in use and automatically frees it. This is a huge bonus for the programmer because "memory leaks" are a source of considerable problems in standard languages (and one of the reasons reboots are a regularly scheduled event on UNIX systems!).
Java and C++ have differences, also. In C++, constructor and destructor functions run whenever an object is created or destroyed. Java has an equivalent to the constructor function named constructor, but there is no exact equivalent to a C++ destructor. All objects in Java are removed using automatic garbage collection. There is no way to invoke destructors manually. There is a method named finalize that can be used like a C++ destructor to do final cleanup on an object before garbage collection occurs, but finalizers have several limitations.
Java allocates memory space similar to malloc in C using the keyword new. new creates an object of virtually any type, the exceptions being primitive data types. A variable is then assigned that points to that object. An example of this follows:
String desc;
desc = new String(10);
This allocates enough space in memory for a 10-character string and associates it with the variable desc. desc is now considered an object of type String and has all the methods associated with class String. String methods include methods to determine length, compare with other strings, obtain substrings, and many others.
Method invocation in Java differs from that in C. In a procedural language like C, the variable desc is normally passed as an argument to the appropriate function call. In Java, the object associated with the variable desc already has all the methods built into it. This is a result of the methods associated with type String. These methods became a part of object desc when it was created with new, as in the following example:
sizeOfString = desc.length //gets the size of desc
if (desc.compareTo("black") //tests to see if desc is equal to "black"
locOfSubString = desc.indexOf("black"); // returns index to substring
These examples focus on the object, not the functions; thus the name object-oriented programming.
The destroy method is always called when an applet has completed or is being shut down. Any final cleanup takes place here. The destroy method always must be named destroy, have a return type of void, be declared public, and have no arguments. An example of a destroy method follows:
public void destroy() {
counter = 0;
}
This destroy method only sets the variable counter to zero. destroy can accomplish many things, such as cleanly terminating network connections, logging information to files, and other final actions.
A destroy method is not required. It is actually overriding an existing destroy method in class java.applet.Applet; therefore, the name, return type, access security and arguments are fixed.
Recall that Java does not allocate memory for objects at application startup time but rather when the instance is created by keyword new. Several things occur when new is invoked. First, Java allocates enough memory to hold the object. Second, Java initializes any instance variables to default values.
Third, Java makes calls to any constructors that exist for that class. Constructors are special methods that are used to initialize an object. A constructor can do anything a normal method can, but usually is used simply to initialize variables within the object to some starting value.
Constructors do not have an explicit keyword to mark them as such. Instead, the constructor method name is the same as the name of the class in which the constructor is declared. In the following example, there is a method named Acat:
class. ACat {
void ACat (String breed[]) {
this.breed = breed;
}
}
Constructor methods always have a return type of void and a name that matches the class in which they are defined.
When new is used to create a new object, the constructor in the previous example is invoked. For example, in the following code, the call to new passes the value Siamese to the constructor, which initializes the breed instance variable to that value:
ACat cat;
cat = new ACat("Siamese");
Constructors are like regular explicitly declared methods in that they can be overloaded by declaring them more than once with different arguments. Multiple constructors are created by declaring several methods with the same name as the class. Also, like overloaded methods, Java determines which constructor to use on the basis of the arguments passed to new, as in the following example:
class. ACat {
void ACat (String breed[]) {
this.breed = breed;
}
void ACat (Color color, int weight) {
this.color = color;
this.weight = weight;
}
}
Here a second constructor with arguments of color and weight is declared in addition to the first constructor, which has an argument of breed. The second constructor is invoked as follows:
ACat cat;
cat = new ACat(black, 15);
This lends itself to great flexibility in the way a new object is constructed. Appropriate constructors are created on the basis of the needs of the application.
Constructors can call other constructors in the same class or a superclass. Java extends the constructor naming convention by using the this and super keywords to refer to constructors in the current or superclass, as in the following example:
class AMammal {
anAnimal(String mammalType[]) {
}
class ACat extends AMammal {
void ACat (String breed[]) {
this.breed = breed;
}
void ACat (Color color, int weight) {
this.color = color;
this.weight = weight;
}
void ACat (Color color, int weight, String breed[]) {
this(breed);
this(color,weight);
super("cat");
}
}
The third constructor takes all the arguments that were used for the two other constructors. However, instead of duplicating the initialization code in the other two constructors, it simply calls them directly with the this() syntax. This example also shows a superclass named AMammal with a constructor that defines a mammal's type. The third ACat constructor uses this type using the super() syntax.
When an object is no longer referenced by any objects, Java reclaims the memory space using garbage collection. Java calls a destructor method before garbage collection takes place. Unlike the constructor methods, destructor methods have a specific name: finalize. Here is an example:
void finalize() {
body of finalize method
}
Finalize methods always have a return type of void and override a default destructor in java.object.Object.
A program can call finalize directly just as it would any other method. However, calling finalize will not initiate any type of garbage collection. It is treated as any other method if called directly. When Java does garbage collection, finalize is still called even if it has already been called directly by the program.
The finalize method is like other methods in that it can be overloaded. Remember, however, that Java calls finalize automatically and does not pass any arguments at that time. If Java finds a finalize method with arguments at garbage-collection time, it will look for a finalize method with no arguments. If it does not find one, Java uses the default finalize method instead.
One difference between finalize and a C++ destructor is that the system only calls finalize when it is ready to reclaim the memory associated with the object. This is not immediately after an object is no longer referenced. Cleanup is scheduled by the system on an as-needed basis. There can be a significant delay between when the application finishes and when finalize is called. This is true even if the system is busy, but there is no immediate need for more memory. For these reasons, it may be better to call finalize directly at program termination rather than wait for garbage collection to invoke it automatically. It depends on the application.
How do packages fit into the hierarchy? Once again we return to the apartment example. In our town, there are several large complexes owned by a single company. This real estate company also owns malls, empty commercially zoned land, and warehouses. These are alike in that they are real property. Think of the company as the largest unit of reference for the properties. The company knows about each of the individual apartment units and can make use of them as needed.
In Java terms, the company is the package. The package groups together class libraries, such as the libraries containing information about different commercial properties, as well as tract land for suburbs. A package is the largest logical unit of objects in Java.
Packages in Java group a variety of classes and/or interfaces together. In packages, classes can be unique compared with classes in other packages. Packages also provide a method of handling access security. Finally, packages provide a way to "hide" classes, preventing other programs or packages from accessing classes that are for internal use of an application only.
Packages are declared using the package keyword followed by a package name. This must occur as the first statement in a Java source file, excluding comments and whitespace. Here is an example:
package mammals;
class AMammal {
...body of class AMammal
}
In this example, the package name is mammals. The class AMammal is now considered a part of this package. Including other classes in package mammals is easy: Simply place an identical package line at the top of those source files as well. Because every class is generally placed in its own source file, every source file that contains classes for a particular package must include this line. There can be only one package statement in any source file.
Note that the Java compiler only requires classes that are declared public to be put in a separate source file. Nonpublic classes can be put in the same source file. Although it is good programming practice to put each of these in its own source file, one package statement at the top of a file will apply to all classes declared in that file.
Java also supports the concept of package hierarchy. This is similar to the directory hierarchy found in many operating systems. This is done by specifying multiple names in a package statement, separated by a period. In the following code, class AMammal belongs to the package mammal that is in the animal package hierarchy:
package animal.mammal;
class AMammal {
...body of class Amammal
}
This allows grouping of related classes into a package and then grouping related packages into a larger package. To reference a member of another package, the package name is prepended to the class name. This is an example of a call to method promptForName in class ACat in subpackage mammal in package animal:
animal.mammal.Cat.promptForName();
The analogy to a directory hierarchy is reinforced by the Java interpreter. The Java interpreter requires that the .class files be physically located in a subdirectory with a name that matches the subpackage name, when accessing a member of a subpackage. If the previous example were located on a UNIX system, the class promptForName would be located as follows:
animal/mammal/ACat.class
Of course, the directory-naming conventions will be different for different operating systems. The Java compiler will happily place the .class files into the same directory as the source files. It may be necessary to move the resulting class files into the appropriate directory if the source files are not in the class files.
The class files can also be placed directly into the desired directory by specifying the -d (directory) option on the javac command line. To continue the example, the following code places the resulting output files in the subdirectory animal/mammal/ACat of the current directory:
> javac -d animal/mammal/ACat ACat.java
All classes actually belong to a package even if not explicitly declared. Even though in the examples used throughout most of this book no package name has been declared, the programs can be compiled and run without problems. As usual in Java, what is not explicitly declared automatically gets default values. In this case, there is a default unnamed package to which all such packages belong. The package does not have an explicit name, and it is not possible for other packages to reference an unnamed package. Therefore, no other package is able to reference most of the examples in this book as they are now. It is a good idea to place all nontrivial classes into packages.
Recall that you can reference packages by prepending a complete package name to a class. A shortcut-using the import statement-can be used when there are many references to a particular package or the package name is long and unwieldy.
The import statement is used to include a list of packages to be searched for a particular class. The syntax of an import statement follows:
import packagename;
import is a keyword and packagename is the name of the package to be imported. The statement must end with ;. The import statement should appear before any class declarations in a source file. Multiple import statements can also be made. The following is an example of an import statement:
import mammal.animal.Cat;
In this example all the members (for example, variables, methods) of class Cat can now be directly accessed by simply specifying their name without prepending the entire package name.
This shortcut poses both an advantage and a disadvantage. The advantage is that the code is no longer cluttered with long names and is easier to type. The disadvantage is that it is more difficult to determine from which package a particular member came. This is especially true when a large number of packages are imported.
import statements can also include the wildcard character *. The asterisk specifies that all classes located in a hierarchy be imported, rather than just a single class. For example, the following code imports all classes that are in the mammal.animal subpackage:
import mammal.animal.*;
This is a handy way to bring all classes from a particular package.
The import statement has been used quite heavily in the examples in this book. It has typically been used to bring in various parts of the Java API. By default the java.lang.* set of classes is always imported. The other Java class libraries must be explicitly imported. For example, the following code brings in all the windowing toolkit graphic and image classes (see Part IV):
import java.awt.Graphics;
import java.awt.Image;
Packages can be named anything that follows the standard Java naming scheme. By convention, however, packages begin with lowercase letters to make it simpler to distinguish package names from class names when looking at an explicit reference to a class. This is why class names are, by convention, begun with an uppercase letter. For example, when using the following convention, it is immediately obvious that mammal and animal are package names and Cat is a class name. Anything following the class name is a member of that class:
mammal.animal.Cat.promptForName();
Java follows this convention for the Java internals and API. The System.out.println() method that has been used follows this convention. The package name is not explicitly declared because java.lang.* is always imported implicitly. System is the class name from package java.lang.*, and it is capitalized. The full name of the method is
java.lang.System.out.println();
Every package name must be unique to make the best use of packages. Naming conflicts that will cause runtime errors will occur if duplicate package names are present. The class files may step on each other in the class directory hierarchy if there is duplication.
It is not difficult to keep package names unique if a single individual or small group does the programming. Large-group projects must iron out package-name conventions early in the project to avoid chaos.
No single organization has control over the World Wide Web, and many Java applications will be implemented over the Web. Remember also that Web servers are likely to include Java applets from multiple sources. It seems impossible to avoid duplicate package names.
Sun recognized this problem late in the development stage but before officially releasing Java. It developed a convention that ensures package-name uniqueness using a variation on domain names, which are guaranteed to be unique: to use the domain name in a reverse manner. Domain mycompany.com would prefix all package names with com.mycompany. Educational institute city.state.edu would prefix package names with edu.state.city. This neatly solves the naming problem and generates a very nice tree structure for all Java class libraries.
The Java interpreter must find all the referenced class libraries when running a Java application. By default, Java looks in the Java install tree for the libraries. It is usually better when developing code to put your own class libraries someplace else. An environment variable named CLASSPATH can be used to tell Java where these libraries are located. CLASSPATH contains a list of directories to search for Java class library trees. The syntax of the list will vary according to the operating system being used. On UNIX systems, CLASSPATH contains a colon-separated list of directory names. Under Windows, the list is separated by ;. The following is a CLASSPATH statement for a UNIX system:
CLASSPATH=/grps/IT/Java/classes:/opt/apps/Java
This tells the Java interpreter to look in the /grps/IT/Java/classes and /opt/apps/Java directories for class libraries.
Packages are used for grouping related classes together. They can be used to access related classes as well as to hide the internals of a package from outside programs.
Packages are declared using the package keyword, which must be located at the beginning of all source files that are to be a part of the same package. Package members can be accessed by prepending the complete package name separated by . to the needed member. The contents of packages can also be accessed by importing the package into a class using the import keyword. Doing so enables the code to specify package members directly without explicitly specifying the complete package name.
Package names by convention begin with lowercase letters. This distinguishes them from class names, which by convention begin with uppercase letters. Sun encourages programmers to use a reverse Internet domain name as the top level of a package name to keep package names unique on a global scale.
Packages can contain subpackages. Java requires that the resulting .class files reside in a directory structure that mirrors the package name hierarchy. By default, the Java interpreter looks for packages where the Java programs were installed. Use the CLASSPATH environment variable to list a set of additional directories to search.
One of the more powerful concepts in an object-oriented language is the concept of inheritance, which is a methodology whereby the code developed for one use can be extended for use elsewhere without having to make an actual copy of the code.
In Java, inheritance is done by creating new classes that are extensions of other classes. The new class is known as a subclass. The original class is known as a superclass. The subclass has all the attributes of the superclass, and in addition has attributes that it defines itself. A class can have only one superclass. This is known as single inheritance. A superclass can have multiple subclasses. To better clarify this concept, let's look at the apartment building example again.
Recall that an apartment building class is made up of individual apartments and may be part of a larger complex made up of other types of buildings such as schools, stores, and houses. One way to implement this in Java is to start with the concept of a building. All buildings share certain characteristics, including building size (height, width, depth), size of lot, and type of heating. Using these characteristics, we could build up a class that stores and manipulates these characteristics.
An apartment building can be thought of as being a more specialized version of a building. Apartment buildings are made up of a group of individual apartments, any of which might be empty at any given moment. These characteristics can be incorporated as part of the building class but would not apply to a school. Instead, an apartment building class is created that inherits characteristics of the building class but also adds its own particular characteristics. This creates a more specific version of building that is customized for describing an apartment building. More specialized versions of buildings could be created for a school, store, or house.
Declaring inheritance in classes is simply an extension of the class-declaration rules discussed earlier. This is done with the extends keyword:
class classname extends anotherclass {
... body of class
}
The extends keyword immediately follows the class name. It is followed by the name of the superclass from which this class will inherit characteristics. There can be only one class name following the extends keyword.
Recall from the examples in the "Applet Classes" section that the applets were declared as subclasses of java.applet.Applet. A declaration such as this provides an applet with all its inherent characteristics. There is no reason to code them because they are inherited from the superclass.
Let's use the apartment building analogy to demonstrate inheritance, starting with the building class:
public class Bldg {
int height, width, depth, lotsize;
String heatType;
public String getHeatType () {
return(heatType);
}
}
In this simplistic version of a building class, variables for holding height, width, depth, and lotsize and a method for printing out the type of heating have all been declared. These are the characteristics that all buildings share.
The next step is to create an apartment building class that extends the previously declared building class:
public class AptBldg extends Bldg {
int numApts;
Apt apt[];
public int findVacatApt () {
int i;
for (i = 0; i < numApts; i++) {
if (apt[i].isVacant == true) {
return(i);
}
}
return(-1);
}
}
public class Apt {
boolean vacant;
int aptNumber;
public boolean isVacant() {
return(vacant);
}
}
In this example, two classes are declared: an apartment building class (AptBldg) and an apartment class (Apt). The apartment building class is declared using extends Bldg, which declares that AptBldg is a subclass of Bldg. By doing this, AptBldg inherits all the variables and methods declared in Bldg. AptBldg extends Bldg by declaring a variable for number of apartments and an array containing apartment objects. There is also a method that returns the first vacant apartment in the array of apartments.
The Apt class is a brand-new class and does not extend any other classes. It declares two variables, vacant and aptNumber, as well as method isVacant, which can be used to determine if the apartment is vacant. The next step is to use the classes that are now declared.
It is now time to create a program for apartment managers. This program must be able to do things such as find an empty apartment. The classes declared previously are used in a new Java program that can do this:
public AptManager {
public findEmptyApt (AptBldg aptBldg) {
int AptNum;
String heatType;
AptNum = aptBldg.findVacantApt();
if (AptNum > 0) {
heatType = aptBldg.getHeatType();
System.out.println("Apartment " + AptNum + "is available for rent");
System.out.println("Type of heat is" + heatType);
}
}
}
In this example, a method for finding an empty apartment has been created. It is passed an apartment building object as an argument. The method then calls the findVacantApt method in the aptBldg object to locate a vacant apartment. If one is found, a call to the method getHeatType is made to determine the type of heat in the building. Both of these pieces of information are then printed out.
Notice that although findVacantApt is explicitly declared in AptBldg, the getHeatType method is not. It is instead declared in the Bldg class. Because AptBldg is declared a subclass of Bldg, it automatically inherits all of Bldg's methods, including getHeatType. By using inheritance we have saved the effort of having to recode the method of determining the heat type. If other subclasses of Bldg, such as school or house, were declared, they would also inherit this same method. On a grander scale, you can save large amounts of coding effort.
Another important characteristic of Java is that it avoids the "fragile superclass" problem of C++; that is, a recompile of all subclasses must occur every time an upper-level class is changed. The very nature of Java is such that, because it is an interpreted language, no such recompile needs to take place. All superclass characteristics are passed down through inheritance. This is a great improvement over C++.
Inheritance is a method of reusing existing code while allowing for customization. When you're working with a group of objects that are similar to each other in some, but not all, ways, inheritance can be helpful.
Inheritance is used with class declarations. It is declared using the extends keyword. When a class extends another class, it is known as a subclass. The class it extends is known as the superclass. A class can have any number of subclasses but can have only one superclass. This is single inheritance.
With inheritance, a subclass can have full access to all the variables and methods declared in its superclass. This allows for items that are common to a group of classes to be placed in a single location. Other variables and methods can be declared in each subclass to add functionality or information as needed.
Inheritance provides a powerful mechanism for reusing existing code. This can be important in a large project. It also makes it simple to add new classes at any time.
Interfaces are Java's way of cutting down on the complexity of a project. Single inheritance makes it easy to find out method or class origins. However, this may be somewhat limiting. C++ permits multiple inheritance, which can be a breeding ground of needless complexity but does allow for ultimate flexibility. Java is less flexible but also less complex. It uses single inheritance for simplicity's sake but uses interfaces to bring in functionality from other classes.
A review of methods is in order before continuing. Methods are similar to functions in other languages. A method is a unit of code that is called and returns a value. Methods perform work on the variables and contain executable code. They are always internal to a class and are associated with an object.
The concept of interfaces is one of the main differences in project design between traditional C and Java application development. The C and other procedural programming language systems' development life cycle often begins with the definition of the application function names and their arguments as empty "black boxes." In other words, the programmers know the necessary argument parameters when programming code that calls these functions without knowing how they are implemented in the function. Thus they can develop code without first fully fleshing out all the functions. In C, this could be done by defining a function prototype for all the functions and then implementing them as resources and schedules permit. How is this accomplished in Java? Through interfaces.
An interface only defines a method's name, return type, and arguments. It does not include executable code or point to a particular method. Think of an interface as a template of structure, not usage.
Interfaces are used to define the structure of a set of methods that will be implemented by classes yet to be designed and coded. In other words, the calling arguments and the return value must conform to the definition in the interface. The compiler checks this conformity. However, the code internal to one method defined by the interface may achieve the intended result in a wildly different way than a method in another class.
The concept of using interfaces is a variation on inheritance used heavily in Java. The chief benefit of interfaces is that many different classes can all implement the same interface. This guarantees that all such classes will implement a few common methods. It also ensures that all the classes will implement these common methods using the same return type, name, and arguments.
Let's get theoretical. Say that a method is defined in the interface as boolean. The only argument is an input string. In the comments section, the method is said to test something to see if it is black and return true or false. Methods using this interface could be written to test cats, pavement, teeth, screen color-just about anything. The only thing these methods have in common is that they have the same definition. Any programmer seeing the method name knows the purpose of the method and the calling arguments.
Java, of course, does not require all classes that implement a method of a certain name to use the interface for argument verification. No language can make up for poor project management. It does, however, provide the structure for use.
An interface is declared in much the same manner as a class, but instead uses the interface keyword instead of the class keyword:
interface name {
... body of interface
}
The interface body is declared between the curly braces. The body of an interface consists of declarations for variables and methods.
The same modifiers-public and default-available to classes can be applied to an interface's declaration. The default is nonpublic, which means accessible by any member of a given package. Most interfaces are public because interfaces are the only means to share variable and method definitions between different packages. Here is an example of a public interface declaration:
public interface AnInterface {
... //body of interface
}
Variables and methods declared inside an interface also have modifiers associated with them. However, the modifiers are limited to particular combinations for variables and methods.
Modifiers for variables are limited to one specific set: public static final. In other words, variables declared in interfaces can only function as constants. public static final are the default modifiers. It is not necessary to declare the modifiers explicitly, but it makes the code more self-documenting. Trying to assign other modifiers such as protected results in a compile-time error. Here are examples of variable declarations:
public static final int smtpSocket = 25;
public static final float pie = 3.14159;
public static final String = "The quick brown fox";
Modifiers for methods are limited to one specific set as well: public abstract, meaning that methods declared inside an interface can only be abstract. These are the default modifiers for methods. It is not necessary to declare them explicitly, but once again, it makes the code easier to read.
Trying to assign other modifiers, such as protected, results in a compile-time error. Here are example interface method declarations:
public abstract boolean isBlack(Color);
public abstract boolean isBlack(String);
public abstract StringBuffer promptForName(String);
As you can see in this example, overloaded methods can be declared in an interface just as in a class. An entire interface based on these examples follows:
public interface MyInterface {
public static final int smtpSocket = 25;
public static final float pie = 3.14159;
public static final String = "The quick brown fox";
public abstract boolean isBlack(Color);
public abstract boolean isBlack(String);
public abstract StringBuffer promptForName(String);
}
Interfaces also can extend other interfaces, just as classes can extend other classes, using the extends keyword. In the following code, the interface AnInterface declares a variable named theAnswer:
public interface AnInterface {
public static final int theAnswer = 42;
}
public interface MyInterface extends AnInterface {
public static final int smtpSocket = 25;
public static final float pie = 3.14159;
public static final String = "The quick brown fox";
public abstract boolean isBlack(Color);
public abstract boolean isBlack(String);
public abstract StringBuffer promptForName(String);
}
The interface MyInterface specifies that it extends AnInterface. This means that any classes that use MyInterface will have access to not only the variables and methods declared in MyInterface, but also those in AnInterface.
You can also list multiple interfaces after the extends keyword. Multiple, possibly disparate, interfaces can be combined into a logical whole if desired, as in the following:
public interface YourInterface extends HerInterface, HisInterface {
body of interface
}
A continuation of the apartment example can be used to go into more detail on interfaces. Recall that an apartment building class AptBldg was defined previously. It contains all the information needed to define an apartment building.
Another piece of information that could be added to the apartment
building class is address.
Address can be manipulated
after it is added to the class; one way to do this is simply to
add address- manipulation methods to the existing apartment building
class. This limits the address information to just one class.
If all the address-manipulation methods were placed in an interface,
however, all classes would have access. Here is an example of
such an interface:
public interface Address {
public abstract void printAddress (anAddress);
public abstract void setStreet (anAddress);
public abstract String getState (anAddress);
}
The interface is a template for manipulating the address information through methods. The methods print the entire address, change the street name, and return only the state. Each of these methods is specific to manipulating addresses. So why place methods in an interface if it is easy to add to the existing class? Reusability is the hallmark of Java!
Now that the interface is defined, a class can make use of it with the implements keyword. Here is the syntax:
class name [extends classname ] implements interfacename [, interfacename] {
... body of class
}
The implements keyword follows the name of the class (or extends declaration) and in turn is followed by one or more comma-separated interface names. The capability to specify a list of interfaces enables a class to have access to a variety of predefined interfaces. In the building example, the class is declared as follows:
public class AptBuilding implements Address {
... body of class
}
Other classes can make use of these same methods by also implementing the same interface after it has been declared.
Suppose we decide to implement other classes, such as schools, stores, and high-rise office buildings. Each of these could use the same method definitions for manipulating addresses that were defined for class AptBldg. Not only does that save programming steps by reusing an existing template, it also enables all the different building classes to use the same methods for manipulating addresses. The call needed to look up a school's address is exactly the same as that needed for a store's address. This also means that the same method name is used. There is no need to come up with a unique name for every class's address. This is important in large projects.
This methodology covers the similarities between classes. What about the differences? Another application might need phone numbers in addition to addresses. This is easy in Java! Java does not specify that only methods derived from interfaces can be used. Any class is free to add methods for anything necessary. This customization only applies to one class, though. There is no need to worry about affecting other classes that also use the interface if an extension is used.
This chapter covers the concepts of classes, packages, inheritance, and interfaces. A solid knowledge of the relationships among these parts of Java is essential to creating applets and applications.
Classes in Java are used to define an object. Objects can consist of information and methods for manipulating that information. An instance is a specific implementation of an object.
Packages are groups of class libraries, which are made up of related classes. Packages can be used to control access security and make it easier to import an entire set of classes.
Inheritance allows for the reuse of existing code while providing a means for customization of new code. Java provides single inheritance between classes and subclasses. Subclasses always inherit the characteristics of their superclass. This is a building block of object-oriented programming.
Interfaces are templates of methods used to standardize method definitions. These are necessary due to Java's lack of multiple inheritance. Interfaces allow unrelated classes to share related methods.