This section shows you the main components of a class by using a small example that implements a stacka data structure whose items are added and removed in a last-in-first-out (LIFO) fashion. Figure 56 lists the Stack [1] class and identifies the structure of the code.
|
Figure 56. The Stack class and the structure of a class definition.
A class definition has two main components: the class declaration and the class body. The class declaration is the first line of code in a class. At a minimum, the class declaration declares the name of the class.
The class body follows the class declaration and appears between braces{ and }. The class body contains all the code that provides for the life cycle of the objects created from the class: constructors for initializing new objects, declarations for the variables that provide the state of the class and its objects, and methods to implement the behavior of the class and its objects. The Stack class defines one member variable within the class bodythe items vector. The Stack class also defines one constructora no-argument constructorand three methods: push, pop, and isEmpty.
Declaring a Class
You've seen many class definitions of the following form:
class MyClass { //member variable and method declarations }
The first line of code is called the class declaration. The preceding class declaration is a minimal class declaration; it contains only those components of a class declaration that are required. Certain aspects of this class, though unspecified, are assumed. The most important is that the direct superclass of MyClass is the Object class. You can provide more information about the class, such as the name of its superclass, whether it implements any interfaces, whether it can be subclassed, and so on, within the class declaration.
Figure 57 shows all the possible components of a class declaration in the order they should or must appear. The right-hand side describes the purpose of each component. The required components are shown in boldface. All the other components are optional, and each appears on a line by itself within the diagram (thus, extends Super is a single component), but you don't have to write your code that way. Italic indicates an identifier, such as the name of a class or an interface. If you do not explicitly declare the optional items, the Java™ platform assumes certain defaults: a nonpublic, nonabstract, nonfinal subclass of Object that implements no interfaces.
Figure 57. The components of a class declaration and their purposes.
The following list provides a few more details about each class declaration component. The list also provides references to this chapter's sections that talk about what each component means, how to use each, and how it affects your class, other classes, and your program.
public
The public modifier declares that the class can be used by any other class. Without the public modifier, your class can be used only by classes in the same package. Look in the section Creating and Using Packages (page 234) for information.
abstract
The abstract modifier declares that the class cannot be instantiated. For a discussion about when abstract classes are appropriate and how to write them, see the section Writing Abstract Classes and Methods (page 214).
final
The final modifier declares that the class cannot be subclassed. The section Writing Final Classes and Methods (page 213) discusses the reasons for writing final classes.
class NameOfClass
The class keyword indicates to the compiler that this is a class declaration. The name of the classNameOfClass follows the class keyword.
extends Super
The extends clause identifies Super as the superclass of the class, thereby inserting the class within the class hierarchy. The section Managing Inheritance (page 204) discusses the responsibilities and benefits of subclasses.
implements Interfaces
To declare that your class implements one or more interfaces, use the keyword implements followed by a comma-separated list of the names of the interfaces implemented by the class. Details about writing your own interfaces and how to use them can be found in the section Creating and Using Interfaces (page 228).
Declaring Member Variables
Stack uses the following line of code to define its single member variable:
private Vector items;
This code declares a member variable and not another type of variable, such as a local variable, because the declaration appears within the class body but outside any methods or constructors. The member variable declared is named items and its data type is Vector, which is a class provided by the Java platform. Also, the private keyword identifies items as a private member. This means that only the Stack class has access to it.
The declaration of the items vector is a simple member variable declaration, but declarations can be more complex. You can specify not only type, name, and access level but also other attributes, including whether the variable is a class variable and whether it's a constant. Figure 58 shows all the possible components of a member variable declaration.
Figure 58. The possible components of a member variable declaration and their purposes. Only the type and the name are required. The rest are optional.
Each component of a member variable declaration is further defined and discussed in later sections of this chapter, as follows:
accessLevel
Lets you control what other classes have access to a member variable by specifying one of four access levels: public, protected, package, and private. You control access to methods in the same way. Controlling Access to Members of a Class (page 193) covers access levels in detail.
static
Declares that this is a class variable rather than an instance variable. You also use static to declare class methods. Understanding Instance and Class Members (page 198) talks about instance and class variables.
final
Indicates that the value of this member cannot change. The following variable declaration defines a constant named AVOGADRO, whose value is Avogadro's number (6.023 x 1023) and cannot be changed:
final double AVOGADRO = 6.023e23;It's a compile-time error if your program ever tries to change a final variable. By convention, the names of constant values are spelled in uppercase letters.
transient
Marks member variables that should not be serialized. This component is used in object serialization, which is covered in the chapter I/O: Reading and Writing (page 313).
volatile
Prevents the compiler from performing certain optimizations on a member. This advanced feature, used by few programmers, is outside the scope of this book.
type
Like other variables, a member variable must have a type. You can use primitive type names, such as int, float, or boolean. Or, you can use reference types, such as array, object, or interface names.
name
A member variable's name can be any legal identifier and, by convention, begins with a lowercase letter. A member variable cannot have the same name as any other member variable in the same class.
Defining Methods
Figure 59 shows the code for Stack's push method. This method puts the object argument onto the top of the stack and returns the object.
Figure 59. The push method and the structure of a method definition.
Like a class, a method definition has two major parts: the method declaration and the method body. The method declaration defines all the method's attributes, such as access level, return type, name, and arguments, as shown in Figure 60. The method body is where all the action takes place. It contains the instructions that implement the method.
Figure 60. The components of the method declaration for the push method.
The only required elements of a method declaration are the method's name, return type, and a pair of parentheses: ( and ). A method declaration can provide more information about the method, including the return type of the method, the number and type of the arguments required by the method, and which other classes and objects can call the method. Figure 61 shows all possible elements of a method declaration.
Figure 61. The possible components of a method declaration and their purposes.
Each element of a method declaration can be further defined and is discussed as indicated in the following list:
accessLevel
As with member variables, you control what other classes have access to a method by using one of four access levels: public, protected, package, and private. The section Controlling Access to Members of a Class (page 193) covers access levels in detail.
static
As with member variables, static declares this method as a class method rather than as an instance method. The section Understanding Instance and Class Members (page 198) talks about declaring instance and class methods.
abstract
An abstract method has no implementation and must be a member of an abstract class. Refer to the section Writing Abstract Classes and Methods (page 214) for information about why you might want to write an abstract method and how such methods affect subclasses.
final
A final method cannot be overridden by subclasses. The section Writing Final Classes and Methods (page 213) discusses why you might want to write final methods, how they affect subclasses, and whether you might want to write a final class instead.
native
If you have a significant library of functions written in another language, such as C, you may wish to preserve that investment and to use those functions from a program written in the Java programming language. Methods implemented in another language are called native methods and are declared as such, using the native keyword. Learn about writing native methods with the Java Native Interface. [1]
[1] You can learn more about the Java Native Interface in the book The Java Tutorial Continued and online at http://java.sun.com/docs/books/tutorial/jni/index.html
synchronized
Concurrently running threads often invoke methods that operate on the same data. Mark these methods with the synchronized keyword to ensure that the threads access information in a thread-safe manner. Synchronizing method calls is covered in the chapter Threads: Doing Two or More Tasks at Once (page 269). Take particular note of the section Synchronizing Threads (page 291).
returnType
A method must declare the data type of the value that it returns. If your method does not return a value, use the keyword void for the return type. The section Returning a Value from a Method (page 190) talks about the issues related to returning values from a method.
methodName
A method name can be any legal identifier. You need to consider code conventions, name overloading, and method overriding when naming a method. These topics are covered next.
( parameterList )
You pass information into a method through its arguments. See the section Passing Information into a Method or a Constructor (page 187).
throws exceptionList
If your method throws any checked exceptions, your method declaration must indicate the type of those exceptions. See the chapter Handling Errors Using Exceptions (page 243) for information. In particular, refer to the section Specifying the Exceptions Thrown by a Method (page 255).
Two of these components comprise the method signature: the method's name and the parameter list.
Naming a Method
Although a method name can be any legal identifier, code conventions [1] restrict method names. In general, method names should be verbs and should be in mixed case, with the first letter in lowercase and the first letter of each internal word in uppercase. Here are some examples:
[1] Sun Microsystems' code conventions for the Java programming language are available online at http://java.sun.com/docs/codeconv/
toString compareTo isDefined setX getX
A method name should not be the same as the class name, because constructors are named for the class. The JavaBeans™ naming conventions [2] further describe how to name methods for setting and getting properties.
[2] The JavaBeans naming conventions are outlined in the JavaBeans specification, available online at http://java.sun.com/beans/spec.html
Typically, a method has a unique name within its class. However, three situations might cause a method to have the same name as other methods in the class or in a superclass: overriding methods, hiding methods, and name overloading.
A method with the same signature and return type as a method in a superclass overrides or hides the superclass method. The section Overriding and Hiding Methods (page 204) describes what each means, shows you how to override and to hide methods, and discusses related issues.
The Java programming language supports name overloading for methods, which means that multiple methods in the same class can share the same name if they have different parameter lists. Suppose that you have a class that can draw various types of data (strings, integers, and so on) and that contains a method for drawing each data type. In other languages, you have to think of a new name for each method, for example, drawString, drawInteger, drawFloat, and so on. In the Java programming language, you can use the same name for all the drawing methods but pass a different type of argument to each method. Thus, the data drawing class might declare three methods named draw, each of which takes a different type of argument.
public class DataArtist { ... public void draw(String s) { ... } public void draw(int i) { ... } public void draw(float f) { ... } }
Overloaded methods are differentiated by the number and the type of the arguments passed into the method. In the code sample, draw(String s) and draw(int i) are distinct and unique methods because they require different argument types. You cannot declare more than one method with the same name and the same number and type of arguments, because the compiler cannot tell them apart. The compiler does not consider return type when differentiating methods, so you cannot declare two methods with the same signature even if they have a different return type.
Providing Constructors for Your Classes
All classes have at least one constructor. A constructor is used to initialize a new object of that type and has the same name as the class. For example, the name of the Stack class's single constructor is Stack:
public Stack() { items = new Vector(10); }
A constructor is not a method, so it has no return type. A constructor is called by the new operator, which automatically returns the newly created object. You cannot use the return statement in a constructor.
Following is another constructor that could be defined by the Stack class. This particular constructor sets the initial size of the stack according to its integer argument:
public Stack(int initialSize) { items = new Vector(initialSize); }
Both constructors share the same name, Stack, but they have different argument lists. As with methods, the Java platform differentiates constructors on the basis of the number of arguments in the list and their types. You cannot write two constructors that have the same number and type of arguments for the same class, because the platform would not be able to tell them apart. Doing so causes a compile-time error.
When writing a class, you should provide it with whatever constructors make sense for that class. Recall the Rectangle class on page 124. That class contains four constructors that allow the user to initialize a new rectangle object in a variety of ways. You don't have to provide any constructors for your class if that's what makes sense. The runtime system automatically provides a no-argument, default constructor for any class that contains no constructors. The default provided by the runtime system doesn't do anything.
You can use one of the following access specifiers in a constructor's declaration to control what other classes can call the constructor:
private
Only this class can use this constructor. If all constructors within a class are private, the class might contain public class methods (called factory methods) that create and initialize an instance of this class. Other classes can use the factory methods to create an instance of this class.
protected
Subclasses of this class and classes in the same package can use this constructor.
public
Any class can use this constructor.
no specifier
Gives package access. Only classes within the same package as this class can use this constructor.
Constructors provide a way to initialize a new object. The section Initializing Instance and Class Members (page 201) describes other ways you can provide for the initialization of your class and a new object created from the class. That section also discusses when and why you would use each technique.
Passing Information into a Method or a Constructor
The declaration for a method or a constructor declares the number and the type of the arguments for that method or constructor. For example, the following is a method that computes the monthly payments for a home loan, based on the amount of the loan, the interest rate, the length of the loan (the number of periods), and the future value of the loan:
public double computePayment(double loanAmt, double rate, double futureValue, int numPeriods) { double I, partial1, denominator, answer; I = rate / 100.0; partial1 = Math.pow((1 + I), (0.0 - numPeriods)); denominator = (1 - partial1) / I; answer = ((-1 * loanAmt) / denominator) - ((futureValue * partial1) / denominator); return answer; }
This method takes four arguments: the loan amount, the interest rate, the future value, and the number of periods. The first three are double-precision floating-point numbers, and the fourth is an integer.
As with this method, the set of arguments to any method or constructor is a comma-separated list of variable declarations, where each variable declaration is a type/name pair. As you can see from the body of the computePayment method, you simply use the argument name to refer to the argument's value.
Argument Types
You can pass an argument of any data type into a method or a constructor. This includes primitive data types, such as doubles, floats, and integers, as you saw in the computePayment method, and reference data types, such as classes and arrays. Here's an example of a factory method that accepts an array as an argument. In this example, the method creates a new Polygon object and initializes it from a list of Points (assume that Point is a class that represents an x, y coordinate):
public static Polygon polygonFrom(Point[] listOfPoints) { ... }
The Java programming language doesn't let you pass methods into methods. But you can pass an object into a method and then invoke the object's methods.
Argument Names
When you declare an argument to a method or a constructor, you provide a name for that argument. This name is used within the method body to refer to the data.
The name of an argument must be unique in its scope. It cannot be the same as the name of another argument for the same method or constructor, the name of a local variable within the method or constructor, or the name of any parameter to a catch clause within the same method or constructor.
An argument can have the same name as one of the class's member variables. If this is the case, the argument is said to hide the member variable. Hiding member variables can make your code difficult to read and is conventionally used only within constructors and methods that set a particular member variable. For example, consider the following Circle class and its setOrigin method:
public class Circle { private int x, y, radius; public void setOrigin(int x, int y) { ... } }
The Circle class has three member variables: x, y, and radius. The setOrigin method accepts two arguments, each of which has the same name as one of the member variables. Each method argument hides the member variable that shares its name. So using the simple names x or y within the body of the method refers to the argument, not to the member variable. To access the member variable, you must use a qualified name. See the section Using the this Keyword (page 192) for details.
Pass by Value
Arguments are passed by value. When invoked, a method or a constructor receives the value of the variable passed in. When the argument is of primitive type, "pass by value" means that the method cannot change its value. When the argument is of reference type, "pass by value" means that the method cannot change the object reference but can invoke the object's methods and modify the accessible variables within the object.
To get a better idea of what this means, let's look at a method called getRGBColor within a class called Pen. This method is attempting to return three values by setting the values of its arguments:
public class Pen { private int redValue, greenValue, blueValue; ... //This method does not work as intended. public void getRGBColor(int red, int green, int blue) { red = redValue; green = greenValue; blue = blueValue; } }
This simply does not work. The red, green, and blue variables exist only within the scope of the getRGBColor method. When that method returns, those variables are gone and any changes to them lost.
Let's rewrite the getRGBColor method so that it does what was intended. First, we need a new type of object, RGBColor, that can hold the red, green, and blue values of a color in RGB space:
public class RGBColor { public int red, green, blue; }
Now we can rewrite getRGBColor so that it accepts an RGBColor object as an argument. The getRGBColor method returns the current color of the pen by setting the red, green, and blue member variables of its RGBColor argument:
public class Pen { private int redValue, greenValue, blueValue; ... public void getRGBColor(RGBColor aColor) { aColor.red = redValue; aColor.green = greenValue; aColor.blue = blueValue; } }
The changes made to the RGBColor object within the getRGBColor method persist after the method returns, because aColor is a reference to an object that exists outside the scope of the method.
Returning a Value from a Method
You declare a method's return type in its method declaration. Within the body of the method, you use the return statement to return the value. Any method declared void doesn't return a value and cannot contain a return statement. Any method that is not declared void must contain a return statement. Let's look at the isEmpty method in the Stack class:
public boolean isEmpty() { if (items.size() == 0) { return true; } else { return false; } }
The data type of the return value must match the method's declared return type; you can't return an integer value from a method declared to return a boolean. The declared return type for the isEmpty method is boolean, and the implementation of the method returns the boolean value true or false, depending on the outcome of a test.
The isEmpty method returns a primitive type. A method can return a reference type. For example, Stack declares the pop method that returns the Object reference type:
public synchronized Object pop() { int len = items.size(); Object obj = null; if (len == 0) { throw new EmptyStackException(); } obj = items.elementAt(len - 1); items.removeElementAt(len - 1); return obj; }
When a method uses a class name as its return type, such as pop does, the class of the type of the returned object must be either a subclass of or the exact class of the return type. Suppose that you have a class hierarchy in which ImaginaryNumber is a subclass of java.lang.Number, which is in turn a subclass of Object, as illustrated in Figure 62.
Figure 62. ImaginaryNumber is a subclass of java.lang.Number, which is a subclass of Object.
Now suppose that you have a method declared to return a Number:
public Number returnANumber() { ... }
The returnANumber method can return an ImaginaryNumber but not an Object. ImaginaryNumber is a Number because it's a subclass of Number. However, an Object is not necessarily a Numberit could be a String or another type.
You also can use interface names as return types. In this case, the object returned must implement the specified interface.
Using the this Keyword
Within an instance method or a constructor, this is a reference to the current objectthe object whose method or constructor is being called. You can refer to any member of the current object from within an instance method or a constructor by using this. The most common reason for doing so is that a member variable is hidden by an argument to the method or the constructor.
For example, the following constructor for the HSBColor class initializes the object's member variables according to the arguments passed into the constructor. Each argument to the constructor hides one of the object's member variables, so this constructor must refer to the object's member variables through this:
public class HSBColor { private int hue, saturation, brightness; public HSBColor (int hue, int saturation, int brightness) { this.hue = hue; this.saturation = saturation; this.brightness = brightness; }
From within a constructor, you can also use the this keyword to call another constructor in the same class. Doing so is called an explicit constructor invocation. Here's another Rectangle class, with a different implementation from the one shown on page 124:
public class Rectangle { private int x, y; private int width, height; public Rectangle() { this(0, 0, 0, 0); } public Rectangle(int width, int height) { this(0, 0, width, height); } public Rectangle(int x, int y, int width, int height) { this.x = x; this.y = y; this.width = width; this.height = height; } ... }
This class contains a set of constructors. Each constructor initializes some or all of the rectangle's member variables. The constructors provide a default value for any member variable whose initial value is not provided by an argument. For example, the no-argument construc-tor calls the four-argument constructor, using 0s as default values. As before, the compiler determines which constructor to call, based on the number and the type of arguments.
If present, an explicit constructor invocation must be the first line in the constructor.
Controlling Access to Members of a Class
An access specifier determines whether other classes can use a particular member variable or call a particular method. The Java programming language supports four distinct access levels for member variables and methods: private, protected, public, and, if left unspecified, package. [1] Table 40 shows the access permitted by each level.
[1] The 1.0 release of the Java programming language supported five access levels: the four listed plus private protected. The private protected access level is not supported in versions of the JDK higher than 1.0; you should not use it.
Specifier |
Class |
Package |
Subclass |
World |
---|---|---|---|---|
private |
_ |
_ |
_ |
|
no specifier |
_ |
_ |
||
protected |
[a] |
_ |
||
public |
[a] The protected/subclass case has an interesting twist discussed in detail on page 197.
The first column indicates whether the class itself has access to the member defined by the access specifier. As you can see, a class always has access to its own members. The second column indicates whether classes in the same package as the class (regardless of their parentage) have access to the member. A package groups related classes and interfaces and provides access protection and namespace management. You'll learn more about packages in the section Creating and Using Packages (page 234). The third column indicates whether subclasses of the classregardless of which package they are inhave access to the member. The fourth column indicates whether all classes have access to the member.
Access levels affect you in two ways. First, when you use classes that come from another source, such as the classes in the Java platform, access levels determine which members of those classes your classes can use. Second, when you write a class, you need to decide what access level every member variable and every method in your class should have. One way of thinking about access levels is in terms of the API: Access levels directly affect the public API of a class and determine which members of the class can be used by other classes. You need to put as much effort into deciding the access level for a member as you put into making other decisions about your class's API, such as naming methods.
Let's look at a collection of classes and see access levels in action. Figure 63 shows the four classes that comprise this example and how they are related.
Figure 63. The classes and packages that comprise the example used to illustrate access levels.
Here's a listing of a class, Alpha, [1] whose members other classes will be trying to access. Alpha contains one member variable and one method per access level. Alpha is in a package called One:
|
package One; public class Alpha { //member variables private int iamprivate = 1; int iampackage = 2; //package access protected int iamprotected = 3; public int iampublic = 4; //methods private void privateMethod() { System.out.println("iamprivate Method"); } /* */void packageMethod() { //package access System.out.println("iampackage Method"); } protected void protectedMethod() { System.out.println("iamprotected Method"); } public void publicMethod() { System.out.println("iampublic Method"); } public static void main(String[] args) { Alpha a = new Alpha(); a.privateMethod(); //legal a.packageMethod(); //legal a.protectedMethod(); //legal a.publicMethod(); //legal System.out.println("iamprivate: " + a.iamprivate); //legal System.out.println("iampackage: " + a.iampackage); //legal System.out.println("iamprotected: " + a.iamprotected); //legal System.out.println("iampublic: " + a.iampublic); //legal } }
As you can see, Alpha can refer to all its member variables and all its methods, as shown by the Class column in Table 40. The output from this program is:
iamprivate Method iampackage Method iamprotected Method iampublic Method iamprivate: 1 iampackage: 2 iamprotected: 3 iampublic: 4
A member's access level determines which classes have access to that member, not which instances have access. So, for example, instances of the same class have access to one another's private members. Thus, we can add to the Alpha class an instance method that compares the current Alpha object (this) to another object, based on their iamprivate variables:
package One; public class Alpha { ... public boolean isEqualTo(Alpha anotherAlpha) { if (this.iamprivate == anotherAlpha.iamprivate) { //legal return true; } else { return false; } } }
Now consider the following class, DeltaOne, [1] which is in the same package as Alpha. The methods and the variables this class can use are predicted by the Package column in Table 40:
|
package One; public class DeltaOne { public static void main(String[] args) { Alpha a = new Alpha(); //a.privateMethod(); //illegal a.packageMethod(); //legal a.protectedMethod(); //legal a.publicMethod(); //legal //System.out.println("iamprivate: " + a.iamprivate); //illegal System.out.println("iampackage: " + a.iampackage); //legal System.out.println("iamprotected: " + a.iamprotected); //legal System.out.println("iampublic: " + a.iampublic); //legal } }
DeltaOne cannot refer to the iamprivate variable or invoke privateMethod but can access the other members of Alpha. If you remove the comment from the lines of code that are commented out and try to compile the class, the compiler will generate errors. Here's the output from the program when you run it as shown:
iampackage Method iamprotected Method iampublic Method iampackage: 2 iamprotected: 3 iampublic: 4
The next class, AlphaTwo, [2] is a subclass of Alpha but is in a different package. You can predict what member variables and methods it can use by looking at the Subclass column in Table 40:
|
package Two; import One.*; public class AlphaTwo extends Alpha { public static void main(String[] args) { Alpha a = new Alpha(); //a.privateMethod(); //illegal //a.packageMethod(); //illegal //a.protectedMethod(); //illegal a.publicMethod(); //legal //System.out.println("iamprivate: " + a.iamprivate); //illegal //System.out.println("iampackage: " + a.iampackage); //illegal //System.out.println("iamprotected: " + a.iamprotected); //illegal System.out.println("iampublic: " + a.iampublic); //legal AlphaTwo a2 = new AlphaTwo(); a2.protectedMethod(); //legal System.out.println("iamprotected: " + a2.iamprotected);//legal } }
This particular case has an interesting twist. Note that AlphaTwo cannot call protectedMethod or access iamprotected on the Alpha instance but can call protectedMethod and access iamprotected on an instance of AlphaTwo. The protected access level allows a subclass to refer to a protected member only through an object reference that is the same type as the class or one of its subclasses. If AlphaTwo had subclasses, it could call protectedMethod through a reference to an instance of any of its subclasses. The output displayed when running AlphaTwo is:
iampublic Method iampublic: 4 iamprotected Method iamprotected: 3
Finally, DeltaTwo [1] is not related through the class hierarchy to Alpha and is in a different package than Alpha. As the World column in Table 40 shows, DeltaTwo can access only the public members of Alpha:
|
package Two; import One.*; public class DeltaTwo { public static void main(String[] args) { Alpha alpha = new Alpha(); //alpha.privateMethod(); //illegal //alpha.packageMethod(); //illegal //alpha.protectedMethod(); //illegal alpha.publicMethod(); //legal //System.out.println("iamprivate: " + a.iamprivate); //illegal //System.out.println("iampackage: " + a.iampackage); //illegal //System.out.println("iamprotected: " + a.iamprotected); //illegal System.out.println("iampublic: " + a.iampublic); //legal } }
Here's the output from DeltaTwo:
iampublic Method iampublic: 4
Tips on Choosing an Access Level
If other programmers use your class, you want to ensure that errors from misuse cannot happen. Access levels can help you do this. The following tips can help you decide what access level is appropriate for a particular member.
[1] Many of the examples in the tutorial use public member variables. Examples and nonproduction code don't have to live up to the rigid design standards that an API does.
Understanding Instance and Class Members
You learned briefly about instance and class members in Chapter 2, Object-Oriented Programming Concepts (page 45). This section shows you how to declare and to use class and instance members. The following class, AClass, [2] declares an instance member variable, an instance method, a class variable, a class method, and main, which is a class method:
|
public class AClass { public int instanceInteger = 0; public int instanceMethod() { return instanceInteger; } public static int classInteger = 0; public static int classMethod() { return classInteger; } public static void main(String[] args) { AClass anInstance = new AClass(); AClass anotherInstance = new AClass(); //Refer to instance members through an instance. anInstance.instanceInteger = 1; anotherInstance.instanceInteger = 2; System.out.println(anInstance.instanceMethod()); System.out.println(anotherInstance.instanceMethod()); //Illegal to refer directly to instance members //from a class method //System.out.println(instanceMethod()); //illegal //System.out.println(instanceInteger); //illegal //Refer to class members through the class... AClass.classInteger = 7; System.out.println(classMethod()); //...or through an instance. System.out.println(anInstance.classMethod()); //Instances share class variables anInstance.classInteger = 9; System.out.println(anInstance.classMethod()); System.out.println(anotherInstance.classMethod()); } }
Here's the output from the program:
1 2 7 7 9 9
Figure 64 shows the objects and member variables in the program and how they are related.
Figure 64. The objects in the AClass example and their instance and class member variables.
Unless otherwise specified, a member declared within a class is an instance member. So instanceInteger and instanceMethod are both instance members. The runtime system creates one copy of each instance variable for each instance of a class created by a program. Thus, the objects referred to by anInstance and anotherInstance each have their own copy of instanceInteger. You can access an instance member and call an instance method only through a reference to an instance. If you remove the two slashes from the beginning of the lines marked illegal and try to compile the program, the compiler will display an error message.
An instance variable is in contrast to a class variable, which is declared by using the static modifier. Besides the main method, AClass declares one class variable and one class method, called classInteger and classMethod, respectively. The runtime system allocates a class variable once per class, regardless of the number of instances created of that class. The system allocates memory for a class variable the first time it encounters the class. All instances of that class share the same copy of the class's class variables. You can access class variables either through an instance or through the class itself. Similarly, class methods can be invoked on the class or through an instance reference. Note that when the program changes the value of classVariable, its value changes for all instances.
Initializing Instance and Class Members
You can provide an initial value for a class or an instance member variable in its declaration:
public class BedAndBreakfast { public static final int MAX_CAPACITY = 10; //initialize to 10 private boolean full = false; //initialize to false }
This works well for member variables of primitive data types. Sometimes, it even works when creating arrays and objects. But this form of initialization has limitations.
If these limitations prevent you from initializing a member variable in its declaration, you have to put the initialization code elsewhere. To initialize a class member variable, put the initialization code in a static initialization block, as the following section shows. To initialize an instance member variable, put the initialization code in a constructor.
Using Static Initialization Blocks
Figure 5.1 shows an example of a static initialization block.
Figure 5.1 A static initialization block can be used to initialize class member variables.
import java.util.ResourceBundle; class Errors { static ResourceBundle errorStrings; static { try { errorStrings = ResourceBundle.getBundle("ErrorStrings"); } catch (java.util.MissingResourceException e) { // error recovery code here } } }
The errorStrings resource bundle must be initialized in a static initialization block because the getBundle method can throw an exception if the bundle cannot be found. The code should perform error recovery. Also, errorStrings is a class member, so it should not be initialized in a constructor. A static initialization block begins with the static keyword and is a normal block of code enclosed in braces: { and }.
A class can have any number of static initialization blocks that appear anywhere in the class body. The runtime system guarantees that static initialization blocks and static initializers are called in the order (left to right, top to bottom) that they appear in the source code.
Initializing Instance Members
If you want to initialize an instance variable and cannot do it in the variable declaration for the reasons cited previously, put the initialization in the constructor(s) for the class. If the errorStrings bundle in the previous example were an instance variable rather than a class variable, you'd move the code that initializes errorStrings to a constructor for the class, as follows:
import java.util.ResourceBundle; class Errors { ResourceBundle errorStrings; Errors() { try { errorStrings = ResourceBundle.getBundle("ErrorStrings"); } catch (java.util.MissingResourceException e) { // error recovery code here } } }
Summary of Creating Classes
A class definition has two parts: a class declaration and a class body. For details about the components of a class declaration, refer to Figure 57. The class body contains member variables, methods, and constructors for the class. A class uses member variables to contain state and uses methods to implement behavior. Figure 58 shows all the possible components of a member variable declaration and Figure 61 shows all possible components of a method declaration. Constructors initialize a new instance of a class and have the same name as the class.
You control access to member variables and methods in the same way: by using an access specifier, such as private or public, in the member's declaration. Table 40 (page 193) shows the access specifiers and the effect of each.
You specify a class member variable or a class method by using the static keyword in the member's declaration. A member that is not declared as static is implicitly an instance member. Class variables are shared by all instance of a class and can be accessed through the class name. Instances of a class get their own copy of each instance variable, which must be accessed through an instance reference.
Questions and Exercises: Creating Classes
Questions
1: |
Consider the following class: public class IdentifyMyParts { public static int x = 7; public int y = 3; }
|
Exercises
1: |
Write a class whose instances represent a playing card from a deck of cards. |
2: |
Write a class whose instances represents a deck of cards. |
3: |
Write a small program to test your deck and card classes. The program can be as simple as creating a deck of cards and displaying its cards. |
Answers
You can find answers to these Questions and Exercises online:
http://java.sun.com/docs/books/tutorial/java/javaOO/QandE/creating-answers.html
Getting Started
Object-Oriented Programming Concepts
Language Basics
Object Basics and Simple Data Objects
Classes and Inheritance
Interfaces and Packages
Handling Errors Using Exceptions
Threads: Doing Two or More Tasks at Once
I/O: Reading and Writing
User Interfaces That Swing
Appendix A. Common Problems and Their Solutions
Appendix B. Internet-Ready Applets
Appendix C. Collections
Appendix D. Deprecated Thread Methods
Appendix E. Reference