Making Your Own Classes
Okay, class, it's time to learn how to create your own classes.
In this chapter, you discover the basics of creating classes in Java. All Java programs are classes, so you've already seen many examples of classes. For example, you've seen class headers such as public class GuessingGame and static methods such as public static void main. Now, in this chapter, I show you how to create programs that have more than one class.
Declaring a Class
All classes must be defined by a class declaration-lines of code that provide the name for the class and the body of the class. Here's the most basic form of a class declaration:
[public] class ClassName { class-body }
The public keyword indicates that this class is available for use by other classes. Although it is optional, you usually include it on your class declarations. After all, the main reason you write class declarations is so other classes can create objects from the class you're defining. (Find out more about using the public keyword in the section "Where classes go" later in this chapter.)
TECHNICAL STAUFF |
In later chapters of this book, you find out about some additional elements that can go in a class declaration. The format I'm describing here is just the basic format used to create basic classes. |
Picking class names
The ClassName is an identifier that provides a name for your class. You can use any identifier you want to name a class, but the following three guidelines can simplify your life:
- Begin the class name with a capital letter. If the class name consists of more than one word, capitalize each word. For example, Ball, RetailCustomer, and GuessingGame.
- Whenever possible, use nouns for your class names. Classes create objects, and nouns are the words you use to identify objects. Thus most class names should be nouns.
- Avoid using the name of a Java API class. No rule says you absolutely have to, but if you create a class that has the same name as a Java API class, you have to use fully qualified names (such as java.util. Scanner) to tell your class apart from the API class with the same name.
Tip There are literally thousands of Java API classes, so avoiding them all is pretty hard. But at the least, you should avoid commonly used Java class names-as well as any API classes your application is likely to use. For example, creating a class named String or Math is just asking for trouble.
What goes in the class body
The class body of a class is everything that goes within the braces at the end of the class declaration. The public class ClassName part of a class declaration takes just one line, but the body of the class declaration may take hundreds of lines. Or thousands if you get carried away.
The class body can contain the following elements:
- Fields: Variable declarations define the public or private fields of a class.
- Methods: Method declarations define the methods of a class.
- Constructors: A constructor is a block of code that's similar to a method but is run to initialize an object when an instance is created. A constructor must have the same name as the class itself and, although it resembles a method, it doesn't have a return type.
- Initializers: These are stand-alone blocks of code that are run only once, when the class is initialized. There are actually two types, called static initializers and instance initializers. Although you won't use them often, I talk about instance initializers later in this chapter, in the section "Using Initializers." (For information about static initializers, refer to Book III, Chapter 3.)
- Other classes and interfaces: A class can include another class, which is then called an inner class or a nested class. Classes can also contain interfaces. (For more information about inner classes, see Book III, Chapter 7. And for information about interfaces, refer to Book III, Chapter 5.)
Tip |
Unlike some programming languages, Java doesn't care about the order in which items appear in the class body. Still, being consistent about the order in which you place things in your classes is a good idea. That way you know where to find them. I usually code all the fields together at the start of the class, followed by constructors and then methods. If the class includes initializers, I place them near the fields they initialize. And if the class includes inner classes, I usually place them after the methods that use them. |
Some programmers like to place the fields at the end of the class rather than at the beginning. Whatever brings you happiness is fine with me.
TECHNICAL STAUFF |
The fields, methods, classes, and interfaces contained within a class are called the members of the class. Constructors and initializers aren't considered to be members, for reasons that are too technical to explain at this point. (You can find the explanation in Book III, Chapter 3.) |
Where classes go
A public class must be written in a source file that has the same name as the class, with the extension java. For example, a public class named Greeter must be placed in a file named Greeter.java.
As a result, you can't place two public classes in the same file. For example, the following source file (named DiceGame.java) won't compile:
public class DiceGame { public static void main(String[] args) { Dice d = new Dice(); d.roll(); } } public class Dice { public void roll() { // code that rolls the dice goes here } }
The compiler coughs up a message indicating that Dice is a public class and must be declared in a file named Dice.java.
This problem has two solutions. The first is to remove the public keyword from the Dice class:
public class DiceGame { public static void main(String[] args) { Dice d = new Dice(); d.roll(); } } class Dice { public void roll() { // code that rolls the dice goes here } }
The compiler gladly accepts this program.
Tip |
This is not the same thing as an inner class. An inner class is a class that's defined within the body of another class, and is available only from within that class. (For more information about inner classes, see Book III, Chapter 7.) |
TECHNICAL STAUFF |
When you code more than one class in a single source file, Java still creates a separate class file for each class. Thus, when you compile the DiceGame. java file, the Java compiler creates two class files-named DiceGame.class and Dice.class. |
Removing the public keyword from a class is acceptable for relatively small programs. But its limitation is that the Dice class is available only to the classes defined within the DiceGame.java file. If you want the Dice class to be more widely available, opt for the second solution: Place it-with the public keyword-in a separate file named Dice.java.
Tip |
If you're going to create an application that has several public classes, create a separate folder for the application. Then, save all the class files for the application to this folder. If you keep your class files together in the same folder, the Java compiler can find them. If you place them in separate folders, you may need to adjust your ClassPath environment variable to help the compiler find the classes. |
Working with Members
The members of a class are the fields and methods defined in the class body. (Technically, classes and interfaces defined within a class are members too. But I don't discuss them in this chapter, so you can ignore them for now.)
The following sections describe the basics of working with fields and methods in your classes.
Fields
A field is a variable that's defined in the body of a class, outside of any of the class's methods. Fields, which are also called class variables, are available to all the methods of a class. In addition, if the field specifies the public keyword, the field is visible outside of the class. If you don't want the field to be visible outside of the class, use the private keyword instead.
A field is defined the same as any other Java variable, but it can have a modifier that specifies whether the field is public or private. Here are some examples of public field declarations:
public int trajectory = 0; public String name; public Player player;
To create a private field, specify private instead of public:
private int x_position =0; private int y_position =0; private String error-message = "";
Fields can also be declared as final:
public final int MAX_SCORE = 1000;
The value of a final field can't be changed once it has been initialized. Note: Spelling final field names with all capital letters is customary (but not required).
Methods
You define methods for a class using the same techniques that I describe in Book II, Chapter 7. To declare a method that's available to users of your class, add the public keyword to the method declaration:
public boolean isActive() { return this.isActive; }
To create a private method that can be used within the class but isn't visible outside the class, use the private keyword:
private void calculateLunarTrajectory() { // code to get the calculated lunar trajectory }
Understanding visibility
In the preceding sections, I mention that both fields and methods can use the public or private keyword to indicate whether the field or method can be accessed from outside the class. This is called the visibility of the field or method.
The combination of all the members that have public access is sometimes called the public interface of your class. These members are the only means that other objects have to communicate with objects created from your class. As a result, carefully consider which public fields and methods your class declares.
The term expose is sometimes used to refer to the creation of public fields and methods. For example, if a class has a public method named isActive, you could say that the class exposes the isActive method. That simply means that the method is available to other classes.
Warning |
You can use private fields and methods within a class-but not from other classes. Private fields and methods provide implementation details that may be crucial to the operation of your class, but that shouldn't be exposed to the outside world. Private fields and methods are sometimes called internal members because they're available only from within the class. |
Getters and Setters
One of the basic goals of object-oriented programming is to hide the implementation details of a class inside the class while carefully controlling what aspects of the class are exposed to the outside world. As a general rule, you should avoid creating public fields. Instead, you can make all your fields private. Then you can selectively grant access to the data those fields contain by adding special methods called accessors to the class.
There are two types of accessors: A get accessor (also called a getter) is a method that retrieves a field value, while a set accessor (setter) is a method that sets a field value. These methods are usually named get FieldName and set FieldName, respectively. For example, if the field is named count, the getter and setter methods are named getCount and setCount.
DESIGN PATTERN |
The Accessor pattern The use of accessors (as described in the section "Getters and Setters" in this chapter) is an example of a design pattern that's commonly used by Java programmers. The Accessor pattern is designed to provide a consistent way to set or retrieve the value of class fields without having to expose the fields themselves to the outside world. Most Java programmers quickly learn that one of the basic guidelines of object-oriented programming is to avoid public fields. Unfortunately, they often respond to this guideline by making all fields private, and then providing get and set accessors for every field whether they need them or not. So they write classes that look like this: public class MyClass { private int fieldX; private int fieldY; public int getX() { return x; } public void setX(int xValue) { this.x = xValue; } public int getY() { return y; } public void setY(int yValue) { this.y = yValue; } } Why not just make fieldX and fieldY public fields and skip the accessors? To be honest, you may as well. The point of making your fields private is so you can carefully control access to them. If you blindly create accessors for all your fields, you might as well just make the fields public. Instead, carefully consider which fields really should be accessible to the outside world-and provide accessors only for those fields that really need them. |
Here's a class that uses a private field named Health to indicate the health of a player in a game program:
public class Player { private int health; public int getHealth() { return health; } public void setHealth(int h) { health = h; } }
Here the health field itself is declared as private, so it can't be accessed directly. Instead, it can be accessed only through the methods getHealth and setHealth.
Creating classes with accessors rather than simple public fields offers several benefits:
- You can create a read-only property by providing a get accessor but not a set accessor. Then other classes can retrieve the property value-but can't change it.
- Instead of storing the property value in a private field, you can calculate it each time the get accessor method is called. For example, suppose you have a class named Order that includes fields named unitPrice and quantityOrdered. This class might also contain a getOrderTotal method that looks like this:
public double getOrderTotal() { return unitPrice * quantityOrdered; }
Here, instead of returning the value of a class field, the get accessor calculates the value to be returned.
- You can protect the class from bad data by validating data in a property set accessor and either ignoring invalid data or throwing an exception if invalid data is passed to the method. For example, suppose you have a set accessor for an int property named Health whose value can be from 0 to 100. Here's a set accessor that prevents the Health property from being set to an incorrect value:
public void setHealth(int h) { if (h < 0) health = 0; else if (h > 100) health = 100; else health = h; }
Here, if the setHealth method is called with a value less than zero, health is set to zero. Likewise, if the value is greater than 100, health is set to 100.
For a little added insight on the use of accessors, see the sidebar "The Accessor pattern."
Overloading Methods
A Java class can contain two or more methods with the same name, provided those methods accept different parameters. This is called overloading and is one of the keys to building flexibility into your classes. With overloading, you can anticipate different ways someone might want to invoke an object's functions, and then provide overloaded methods for each alternative.
Tip |
The term overloading is accurate, but a little unfortunate. Normally, when you say something is overloaded, there's a problem. For example, I once saw a picture of a Volkswagen Jetta loaded down with 3,000 pounds of lumber. (You can find the picture courtesy of http://www.Snopes.com, the Urban Legend Reference Page Web site, at http://www.snopes.com/photos/lumber.asp.) That's a classic example of overloading in the ordinary sense. Fortunately, you don't have to worry about Java collapsing under the weight of overloaded methods. |
You're already familiar with several classes that have overloaded methods, though you may not realize it. For example, the PrintWriter class (which you access via System.out) defines ten different versions of the println method that allow you to print different types of data. The following lines show the method declaration for each of these overloads:
void println() void println(boolean x) void println(char x) void println(char[] x) void println(double x) void println(float x) void println(int x) void println(long x) void println(Object x) void println(String x)
The basic rule when creating overloaded methods is that every method must have a unique signature. A method's signature is the combination of its name and the number and types of parameters it accepts. Thus each of the println methods has a different signature because although each has the same name, each accepts a different parameter type.
Two things that are not a part of a method's signature are
- The method's return type: You can't code two methods with the same name and parameters but with different return types.
- The names of the parameters: All that matters to the method signature are the types of the parameters and the order in which they appear. Thus the following two methods have the same signature:
double someMethodOfMine(double x, boolean y) double someMethodOfMine(double param1, boolean param2)
Creating Constructors
A constructor is a block of code that's called when an instance of an object is created. In many ways, a constructor is similar to a method, but with a few differences:
- A constructor doesn't have a return type.
- The name of the constructor must be the same as the name of the class.
- Unlike methods, constructors are not considered to be members of a class. (That's only important when it comes to inheritance, which is covered in Book III, Chapter 4.)
- A constructor is called when a new instance of an object is created. In fact, it's the new keyword that calls the constructor. After creating the object, you can't call the constructor again.
Here's the basic format for coding a constructor:
public ClassName (parameter-list) [throws exception...] { statements... }
The public keyword indicates that other classes can access the constructor. That's usually what you want, although in the next chapter you see why you might want to create a private constructor. ClassName must be the same as the name of the class that contains the constructor. And you code the parameter list the same as you code it for a method.
Notice also that a constructor can throw exceptions if it encounters situations it can't recover from. (For more information about throwing exceptions, refer to Book II, Chapter 8.)
Basic constructors
Probably the most common reason for coding a constructor is to provide initial values for class fields when you create the object. For example, suppose you have a class named Actor that has fields named firstName and lastName. You can create a constructor for the Actor class:
public Actor(String first, String last) { firstName = first; lastName = last; }
Then, you create an instance of the Actor class by calling this constructor:
Actor a = new Actor("Arnold", "Schwarzenegger");
A new Actor object for Arnold Schwarzenegger is created.
Like methods, constructors can be overloaded. In other words, you can provide more than one constructor for a class, provided each constructor has a unique signature. For example, here's another constructor for the Actor class:
public Actor(String first, String last, boolean good) { firstName = first; lastName = last; goodActor = good; }
This constructor lets you create an Actor object with additional information besides the actor's name:
Actor a = new Actor("Arnold", "Schwarzenegger", false);
Default constructors
I grew up watching Dragnet. I can still hear Joe Friday reading some thug his rights: "You have the right to an attorney during questioning. If you desire an attorney and cannot afford one, an attorney will be appointed to you free of charge."
Java constructors are like that. Every class has a right to a constructor. If you don't provide a constructor, Java appoints one for you, free of charge. This free constructor is called the default constructor. It doesn't accept any parameters and it doesn't do anything, but it does allow your class to be instantiated.
Thus the following two classes are identical:
public Class1 { public Class1() { } } public Class1 { }
In the first example, the class explicitly declares a constructor that doesn't accept any parameters and has no statements in its body. In the second example, Java creates a default constructor that works just like the constructor shown in the first example.
Warning |
The default constructor is not created if you declare any constructors for the class. As a result, if you declare a constructor that accepts parameters and still want to have an empty constructor (with no parameters and no body), you must explicitly declare an empty constructor for the class. |
An example might clear this point up. The following code does not compile:
public class BadActorApp { public static void main(String[] args) { Actor a = new Actor(); // error: won't compile } } class Actor { private String lastName; private String firstName; private boolean goodActor; public Actor(String last, String first) { lastName = last; firstName = first; } public Actor(String last, String first, boolean good) { lastName = last; firstName = first; goodActor = good; } }
This program won't compile because it doesn't explicitly provide a default constructor for the Actor class; because it does provide other constructors, the default constructor isn't automatically generated.
Calling other constructors
A constructor can call another constructor of the same class by using the special keyword this as a method call. This technique is commonly used when you have several constructors that build on each other.
For example, consider this class:
public class Actor { private String lastName; private String firstName; private boolean goodActor; public Actor(String last, String first) { lastName = last; firstName = first; } public Actor(String last, String first, boolean good) { this(last, first); goodActor = good; } }
Here the second constructor calls the first constructor to set the lastName and firstName fields. Then it sets the goodActor field.
You have a few restrictions on how to use the this keyword as a constructor call:
- You can call another constructor only in the very first statement of a constructor. Thus the following code won't compile:
public Actor(String last, String first, boolean good) { goodActor = good; this(last, first); // error: won't compile }
If you try to compile a class with this constructor, you get a message saying call to this must be first statement in constructor.
- Each constructor can call only one other constructor. However, you can chain constructors together. For example, if a class has three constructors, the first constructor can call the second one, which in turn calls the third one.
- You can't create loops where constructors call each other. For example, here's a class that won't compile:
class CrazyClass { private String firstString; private String secondString; public CrazyClass(String first, String second) { this(first); secondString = second; } public CrazyClass(String first) { this(first, "DEFAULT"); // error: won't // compile } }
The first constructor starts by calling the second constructor, which calls the first constructor. The compiler complains that this error is a recursive constructor invocation and politely refuses to compile the class.
TECHNICAL STAUFF |
If you don't explicitly call a constructor in the first line of a constructor, Java inserts code that automatically calls the default constructor of the base class-that is, the class that this class inherits. (This little detail doesn't become too important until you get into inheritance, which is covered in Book III, Chapter 4, so you can just conveniently ignore it for now.) |
More Uses for this
As I describe in the previous section, you can use the this keyword in a constructor to call another constructor for the current class. You can also use this in the body of a class constructor or method to refer to the current object-that is, the class instance for which the constructor or method has been called.
The this keyword is usually used to qualify references to instance variables of the current object. For example:
public Actor(String last, String first) { this.lastName = last; this.firstName = first; }
Here this isn't really necessary because the compiler can tell that lastName and firstName refer to class variables. However, suppose you use lastName and firstName as the parameter names for the constructor:
public Actor(String lastName, String firstName) { this.lastName = lastName; this.firstName = firstName; }
Here the this keywords are required to distinguish between the parameters named lastName and firstName and the instance variables with the same names.
You can also use this in a method body. For example:
public String getFullName() { return this.firstName + " " + this.lastName; }
Because this example has no ambiguity, this isn't really required. However, many programmers like to use this even when it isn't necessary-because it makes it clear that you're referring to an instance variable.
Sometimes you use the this keyword all by itself to pass a reference to the current object as a method parameter. For example, you can print the current object to the console by using the following statement:
System.out.println(this);
TECHNICAL STAUFF |
The println method calls the object's toString method to get a string representation of the object, and then prints it to the console. By default, toString prints the name of the class that the object was created from and the object's hash code. If you want the println method to print something more meaningful, provide a toString method of your own for the class. |
Using Initializers
An initializer (sometimes called an initializer block) is a lonely block of code that's placed outside of any method, constructor, or other block of code. Initializers are executed whenever an instance of a class is created, regardless of which constructor is used to create the instance.
Initializer blocks are similar to variable initializers used to initialize variables. The difference is that with an initializer block, you can code more than one statement. For example, here's a class that gets the value for a class field from the user when the class is initialized:
class PrimeClass { private Scanner sc = new Scanner(System.in); public int x; { System.out.print( "Enter the starting value for x: "); x = sc.nextInt(); } }
Tip |
You can almost always achieve the same effect using other coding techniques that are usually more direct. For example, you could prompt the user for the value in the constructor. Or you could call a method in the field initializer, like this: class PrimeClass { private Scanner sc = new Scanner(System.in); public int x = getX(); private int getX() { System.out.print("Enter the starting value " + "for x: "); return sc.nextInt(); } } |
Either way, the effect is the same.
Here are a few other tidbits of information concerning initializers:
- If a class contains more than one initializer, the initializers are executed in the order in which they appear in the program.
- Initializers are executed before any class constructors.
- A special kind of initializer block called a static initializer lets you initialize static fields. (For more information, refer to the next chapter.)
-
TECHNICAL STAUFF Initializers are sometimes used with anonymous classes, as I describe in Book III, Chapter 6.