Object ConstructionYou have seen how to write simple constructors that define the initial state of your objects. However, because object construction is so important, Java offers quite a variety of mechanisms for writing constructors. We go over these mechanisms in the sections that follow. OverloadingRecall that the GregorianCalendar class had more than one constructor. We could use: GregorianCalendar today = new GregorianCalendar(); or: GregorianCalendar deadline = new GregorianCalendar(2099, Calendar.DECEMBER, 31);
This capability is called
overloading
. Overloading occurs if several
NOTE
Default Field Initialization
If you don't set a field explicitly in a constructor, it is automatically set to a default value:
NOTE
For example, consider the Employee class. Suppose you don't specify how to initialize some of the fields in a constructor. By default, the salary field would be initialized with and the name and hireDay fields would be initialized with null .
However, that would not be a good idea. If
Date h = harry.getHireDay(); calendar.setTime(h); // throws exception if h is null Default ConstructorsA default constructor is a constructor with no parameters. For example, here is a default constructor for the Employee class:
public Employee()
{
name = "";
salary = 0;
hireDay = new Date();
}
If you write a class with no constructors whatsoever, then a default constructor is provided for you. This default constructor sets all the instance fields to their default values. So, all numeric data contained in the instance fields would be , all Booleans would be false , and all object variables would be set to null .
If a class
Employee(String name, double salary, int y, int m, int d)
With that class, it was not legal to construct default
e = new Employee(); would have been an error.
CAUTION
Explicit Field InitializationBecause you can overload the constructor methods in a class, you can obviously build in many ways to set the initial state of the instance fields of your classes. It is always a good idea to make sure that, regardless of the constructor call, every instance field is set to something meaningful. You can simply assign a value to any field in the class definition. For example,
class Employee
{
. . .
private String name = "";
}
This assignment is carried out before the constructor executes. This syntax is particularly useful if all constructors of a class need to set a particular instance field to the same value.
The initialization value doesn't have to be a constant value. Here is an example in which a field is initialized with a method call. Consider an
Employee
class where each employee has an
id
field. You can initialize it as
class Employee
{
. . .
static int assignId()
{
int r = nextId;
nextId++;
return r;
}
. . .
private int id = assignId();
}
C++ NOTE
Parameter NamesWhen you write very trivial constructors (and you'll write a lot of them), then it can be somewhat frustrating to come up with parameter names.
We have
public Employee(String n , double s ) { name = n; salary = s; } However, the drawback is that you need to read the code to tell what the n and s parameters mean. Some programmers prefix each parameter with an " a ": public Employee(String aName , double aSalary ) { name = aName; salary = aSalary; } That is quite neat. Any reader can immediately figure out the meaning of the parameters.
Another commonly used trick relies on the fact that parameter variables
shadow
instance fields with the same name. For example, if you call a parameter
salary
, then
salary
refers to the parameter, not the instance field. But you can still access the instance field as
this.salary
. Recall that
this
denotes the implicit parameter, that is, the object that is being
public Employee(String name , double salary ) { this. name = name; this. salary = salary; }
C++ NOTE
Calling Another ConstructorThe keyword this refers to the implicit parameter of a method. However, the keyword has a second meaning. If the first statement of a constructor has the form this(. . .) , then the constructor calls another constructor of the same class. Here is a typical example:
public Employee(double s)
{
// calls Employee(String, double)
this(
"Employee #" + nextId, s
)
;
nextId++;
}
When you call new Employee(60000) , then the Employee(double) constructor calls the Employee(String, double) constructor. Using the this keyword in this manner is useful—you only need to write common construction code once.
C++ NOTE
Initialization BlocksYou have already seen two ways to initialize a data field:
There is actually a third mechanism in Java; it's called an initialization block . Class declarations can contain arbitrary blocks of code. These blocks are executed whenever an object of that class is constructed. For example,
class Employee
{
public Employee(String n, double s)
{
name = n;
salary = s;
}
public Employee()
{
name = "";
salary = 0;
}
. . .
private static int nextId;
private int id;
private String name;
private double salary;
. . .
// object initialization block
{
id = nextId;
nextId++;
}
}
In this example, the id field is initialized in the object initialization block, no matter which constructor is used to construct an object. The initialization block runs first, and then the body of the constructor is executed. This mechanism is never necessary and is not common. It usually is more straightforward to place the initialization code inside a constructor.
NOTE
With so many ways of initializing data fields, it can be quite confusing to give all possible
Naturally, it is always a good idea to organize your initialization code so that another programmer could easily understand it without having to be a language lawyer. For example, it would be quite
You initialize a static field either by supplying an initial value or by using a static initialization block. You have already seen the first mechanism:
static int nextId
= 1
;
If the static fields of your class require complex initialization code, use a static initialization block. Place the code inside a block and tag it with the keyword static . Here is an example. We want the employee ID numbers to start at a random integer less than 10,000. // static initialization block static { Random generator = new Random(); nextId = generator.nextInt(10000); } Static initialization occurs when the class is first loaded. Like instance fields, static fields are , false , or null unless you explicitly set them to another value. All static field initializers and static initialization blocks are executed in the order in which they occur in the class declaration.
NOTE
The program in Example 4-5 shows many of the features that we discussed in this section:
Example 4-5. ConstructorTest.java
1. import java.util.*;
2.
3. public class ConstructorTest
4. {
5. public static void main(String[] args)
6. {
7. // fill the staff array with three Employee objects
8. Employee[] staff = new Employee[3];
9.
10. staff[0] = new Employee("Harry", 40000);
11. staff[1] = new Employee(60000);
12. staff[2] = new Employee();
13.
14. // print out information about all Employee objects
15. for (Employee e : staff)
16. System.out.println("name=" + e.getName()
17. + ",id=" + e.getId()
18. + ",salary=" + e.getSalary());
19. }
20. }
21.
22. class Employee
23. {
24. // three overloaded constructors
25. public Employee(String n, double s)
26. {
27. name = n;
28. salary = s;
29. }
30.
31. public Employee(double s)
32. {
33. // calls the Employee(String, double) constructor
34. this("Employee #" + nextId, s);
35. }
36.
37. // the default constructor
38. public Employee()
39. {
40. // name initialized to ""--see below
41. // salary not explicitly set--initialized to 0
42. // id initialized in initialization block
43. }
44.
45. public String getName()
46. {
47. return name;
48. }
49.
50. public double getSalary()
51. {
52. return salary;
53. }
54.
55. public int getId()
56. {
57. return id;
58. }
59.
60. private static int nextId;
61.
62. private int id;
63. private String name = ""; // instance field initialization
64. private double salary;
65.
66. // static initialization block
67. static
68. {
69. Random generator = new Random();
70. // set nextId to a random number between 0 and 9999
71. nextId = generator.nextInt(10000);
72. }
73.
74. // object initialization block
75. {
76. id = nextId;
77. nextId++;
78. }
79. }
java.util.Random 1.0
Object Destruction and the finalize MethodSome object-oriented programming languages, notably C++, have explicit destructor methods for any cleanup code that may be needed when an object is no longer used. The most common activity in a destructor is reclaiming the memory set aside for objects. Because Java does automatic garbage collection, manual memory reclamation is not needed and so Java does not support destructors. Of course, some objects utilize a resource other than memory, such as a file or a handle to another object that uses system resources. In this case, it is important that the resource be reclaimed and recycled when it is no longer needed.
You can add a
finalize
method to any class. The
finalize
method will be called before the garbage collector
NOTE
If a resource needs to be closed as soon as you have finished using it, you need to manage it manually. Supply a method such as dispose or close that you call to clean up what needs cleaning. Just as importantly, if a class you use has such a method, you need to call it when you are done with the object. |
|
|