Java核心思想学习笔记003(Object Construction )

原创 2006年05月26日 16:35:00

Object Construction

You 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 will go over these mechanisms in the sections that follow.


Recall that the GregorianCalendar class had more than one constructor. We could use:

GregorianCalendar today = new GregorianCalendar();


GregorianCalendar deadline
   = new GregorianCalendar(2099, Calendar.DECEMBER, 31);

This capability is called overloading. Overloading occurs if several methods have the same name (in this case, the GregorianCalendar constructor method) but different parameters. The compiler must sort out which method to call. It picks the correct method by matching the parameter types in the headers of the various methods with the types of the values used in the specific method call. A compile-time error occurs if the compiler cannot match the parameters or if more than one match is possible. (This process is called overloading resolution.)

Java allows you to overload any method—not just constructor methods. Thus, to completely describe a method, you need to specify the name of the method together with its parameter types. This is called the signature of the method. For example, the String class has four methods called indexOf. They have signatures

indexOf(int, int)
indexOf(String, int)

The return type is not part of the method signature. That is, you cannot have two methods with the same names and parameter types but different return types.

Default Field Initialization

If you don't set a field explicitly in a constructor, it is automatically set to a default value: numbers to zero, Booleans to false, and object references to null. But it is considered poor programming practice to rely on this. Certainly, it makes it harder for someone to understand your code if fields are being initialized invisibly.

This is an important difference between fields and local variables. You must always explicitly initialize local variables in a method. But if you don't initialize a field in a class, it is automatically initialized to a default (zero, false or null).

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 0 and the name and hireDay fields would be initialized with null.

However, that would not be a good idea. If anyone called the getName or getHireDay method, then they would get a null reference that they probably don't expect:

Date h = harry.getHireDay();
calendar.setTime(h); // throws exception if h is null

Default Constructors

A default constructor is a constructor with no parameters. (This constructor is sometimes called a no-arg constructor.) 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 zero, all Booleans would be false, and all object variables would be set to null.

If a class supplies at least one constructor but does not supply a default constructor, it is illegal to construct objects without construction parameters. For example, our original Employee class in Example 4-2 provided a single constructor:

Employee(String name, double salary, int y, int m, int d)

With that class, it was not legal to construct default employees. That is, the call

e = new Employee();

would have been an error.

Please keep in mind that you get a free default constructor only when your class has no other constructors. If you write your class with even a single constructor of your own, and you want the users of your class to have the ability to create an instance via a call to

new ClassName()

then you must provide a default constructor (with no parameters). Of course, if you are happy with the default values for all fields, you can simply supply:

public ClassName()

Explicit Field Initialization

Since 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 where a field is initialized with a method call. Consider an Employee class where each employee has an id field. You can initialize it as follows:

class Employee
   . . .
   static int assignId()
   {  int r = nextId;
      return r;
   . . .
   private int id = assignId();

In C++, you cannot directly initialize instance fields of a class. All fields must be set in a constructor. However, C++ has a special initializer list syntax, such as:

   Employee::Employee(String n, double s,
   int y, int m, int d) // C++
:  name(n),
   hireDay(y, m, d)

C++ uses this special syntax to call field constructors. In Java, there is no need for it because objects have no subobjects, only pointers to other objects.

Parameter Names

When 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 generally opted for single-letter parameter names:

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.

There is another commonly used trick. It 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 constructed. Here is an example:

public Employee(String name, double salary)
{ = name;
   this.salary = salary;

In C++, it is common to prefix instance fields with an underscore or a fixed letter. (The letters m and x are common choices.) For example, the salary field might be called _salary or mSalary. Programmers don't usually do that in the Java programming language.

Calling Another Constructor

The keyword this refers to the implicit parameter of a method. However, there is a second meaning for the keyword.

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);

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.

The this object in Java is identical to the this pointer in C++. However, in C++ it is not possible for one constructor to call another. If you want to factor out common initialization code in C++, you must write a separate method.

Initialization Blocks

You have already seen two ways to initialize a data field:

  • By setting a value in a constructor;

  • By assigning a value in the declaration.

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;
   . . .
   // must define before use in initialization block
   private int id;
   private static int nextId;

   // object initialization block
      id = nextId;
   . . .
   private String name;
   private double salary;

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.

With so many ways of initializing data fields, it can be quite confusing to give all possible pathways for the construction process. Here is what happens in detail when a constructor is called.

  1. All data fields are initialized to their default value (0, false, or null).

  2. All field initializers and initialization blocks are executed, in the order in which they occur in the class declaration.

  3. If the first line of the constructor calls another constructor, then the body of that constructor is executed.

  4. The body of the constructor is executed.

Naturally, it is always a good idea to organize your initialization code so that it is easy to understand without having to be a language lawyer. For example, it would be quite strange and somewhat error-prone to have a class whose constructors depend on the order in which the data fields are declared.

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
   Random generator = new Random();
   nextId = generator.nextInt(10000);

Static initialization occurs when the class is first loaded. Like instance fields, static fields are 0, 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.

Here is a Java trivia fact to amaze your fellow Java coders: You can write a "Hello, World" program in Java without ever writing a main method.

public class Hello
      System.out.println("Hello, World");

When you invoke the class with java Hello, the class is loaded, the static initialization block prints "Hello, World," and only then do you get an ugly error message that main is not defined. You can avoid that blemish by calling System.exit(0) at the end of the static initialization block.

The program in Example 4-5 shows many of the features that we discussed in this section:

  • Overloaded constructors

  • Calling another constructor with this(...)

  • A default constructor

  • An object initialization block

  • A static initialization block

  • An instance field initialization

Example 4-5
 1. import java.util.*;
 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];
10.       staff[0] = new Employee("Harry", 40000);
11.       staff[1] = new Employee(60000);
12.       staff[2] = new Employee();
14.       // print out information about all Employee objects
15.       for (int i = 0; i < staff.length; i++)
16.       {
17.          Employee e = staff[i];
18.          System.out.println("name=" + e.getName()
19.             + ",id=" + e.getId()
20.             + ",salary=" + e.getSalary());
21.       }
22.    }
23. }
25. class Employee
26. {
27.    // three overloaded constructors
28.    public Employee(String n, double s)
29.    {
30.       name = n;
31.       salary = s;
32.    }
34.    public Employee(double s)
35.    {
36.       // calls the Employee(String, double) constructor
37.       this("Employee #" + nextId, s);
38.    }
40.    // the default constructor
41.    public Employee()
42.    {
43.       // name initialized to ""--see below
44.       // salary not explicitly set--initialized to 0
45.       // id initialized in initialization block
46.    }
48.    public String getName()
49.    {
50.       return name;
51.    }
53.    public double getSalary()
54.    {
55.       return salary;
56.    }
58.    public int getId()
59.    {
60.       return id;
61.    }
63.    // object initialization block
64.    {
65.       id = nextId;
66.       nextId++;
67.    }
69.    // static initialization block
70.    static
71.    {
72.       Random generator = new Random();
73.       // set nextId to a random number between 0 and 9999
74.       nextId = generator.nextInt(10000);
75.    }
77.    private String name = ""; // instance variable initialization
78.    private double salary;
79.    private int id;
80.    private static int nextId;
81. }

java.util.Random 1.0

  • Random()

    constructs a new random number generator

  • int nextInt(int n) 1.2

    returns a random number between 0 and n - 1

Object Destruction and the finalize Method

Some 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. Since Java does automatic garbage collection, manual memory reclamation is not needed, and 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 sweeps away the object. In practice, do not rely on the finalize method for recycling any resources that are in short supply—you simply cannot know when this method will be called.

There is a method call System.runFinalizersOnExit(true)to guarantee that finalizer methods are called before Java shuts down. However, this method is inherently unsafe and has been deprecated. An alternative is to add "shutdown hooks" with the method Runtime.addShutdownHook—see the API documentation for details.

If a resource needs to be closed as soon as you have finished using it, you need to manage it manually. Add a dispose method that you call to clean up what needs cleaning. Just as importantly, if a class you use has a dispose method, you will want to call it when you are done with the object. In particular, if your class has an instance field that has a dispose method, provide a dispose method that disposes of the instance fields.

Maven学习笔记(三) :Maven使用入门

编写POM:      Maven项目的核心是pom.xml。POM(Project Object Model,项目对象模型)定义了项目的基本信息,用于描述项目如何构建,声明项目依赖,等等。     ...
  • Troy__
  • Troy__
  • 2014年08月26日 18:43
  • 3171


第一章 多线程 多线程和多进程之间的区别:   本质区别在于每个进程有他自己的变量的完备集,线程则共享相同的数据,这个听起来似乎有些危险,事实上也的确如此,你将会在本章后面的内容中看到这个问题,尽...
  • cxming007
  • cxming007
  • 2014年06月04日 18:38
  • 2289

js 构造函数(construction)与原型(prototype)

1.js原型 java有class和instance,js只有构造函数(function Cat(name,age){;this.age=age}),为了实现数据共享和抽象...
  • tang7837010
  • tang7837010
  • 2014年12月24日 11:21
  • 1031

java studyd 003 --object clone

class Point implements Cloneable {  int x;  int y;    Date dt;    Point(int x, int y)  {   ...
  • nuoshueihe
  • nuoshueihe
  • 2012年06月01日 09:12
  • 249

编译原理及实践(Compiler Construction Principles and Practice)——简单的递归下降程序计算器

这是书中P109页的示例源码,修改了getchar输入方式,而是提前放在一个字符串中,程序只能处理一位的数字字符串,源码: #include #include #include ...
  • dijkstar
  • dijkstar
  • 2012年07月14日 14:50
  • 2100

POJ 1785 Binary Search Heap Construction 【笛卡尔树构造,线段树RMQ(Range Max/Min Query)】

题目注意: 两个要求,1. 第一关键字满足搜索序,2. 第二关键字满足堆的性质。 这道题目的标准(简单)解法:笛卡尔树。 但是还有: 分治+RMQ, 其中,RMQ问题有两种方法:线段树和Spars...
  • yang_7_46
  • yang_7_46
  • 2013年01月24日 20:24
  • 3220

STL乱搞 Codeforces675D Tree Construction

传送门:点击打开链接 题意:按BST插入节点。最后输出每个节点的父节点的值是多少 思路:这场的cf脑洞都很大。。 首先,假如我们要插入的节点的值为x,我们发现父节点的值一定是x的最小值的左节点 ...
  • qwb492859377
  • qwb492859377
  • 2016年05月18日 21:10
  • 418


1.IoC是什么? IoC(Inversion of Control)控制反转,IoC是一种新的Java编程模式,目前很多轻量级容器都在广泛使用的模式。 2.IoC解决了什么问题? 在...
  • Baple
  • Baple
  • 2016年12月15日 11:19
  • 2836

The Server Object construction attempt has failed because the Startup Timeout Interval has elapsed解决

ArcGISServer发布地图时出现“The Server Object construction attempt has failed because the Startup Timeout In...
  • feifei2211
  • feifei2211
  • 2013年04月25日 17:18
  • 1493


一、为什么要有并发 ①、能够提高运行速度。 我们可以将程序拆分成多个部分,让每个部分在不同的cpu上执行。 但是其单cpu上使用并发,反而会增加负担。因为上下文的切换会浪费大量的时间。 但是也会带来有...
  • chen19960724
  • chen19960724
  • 2016年08月11日 16:04
  • 171
您举报文章:Java核心思想学习笔记003(Object Construction )