4.6. Object Construction
You have seen how to write simple constructors that define the initial state of your objects. However, since 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.
4.6.1. Overloading
Recall 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 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.)


Note
Java allows you to overload any method—not just constructor methods(Java允许重载任何方法). Thus, to completely describe a method, you need to specify its name together with its parameter types. This is called the signature of the method. For example, the String class has four public methods called indexOf. They have signatures
indexOf(int)
indexOf(int, int)
indexOf(String)
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.


4.6.2. Default Field Initialization
If you don't set a field explicitly in a constructor(显式构造函数), it is automatically set to a default value: numbers to 0, boolean values to false, and object references to null. Some people consider it poor programming practice to rely on the defaults. Certainly, it makes it harder for someone to understand your code if fields are being initialized invisibly.


Note
This is an important difference between fields and local variables. You must always explicitly initialize local variables in a method(一个方法中的局部变量必须显式初始化). But in a class, if you don't initialize a field, it is automatically initialized to a default (0, 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, 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

4.6.3. The Constructor with No Arguments
Many classes contain a constructor with no arguments(无参构造方法) that creates an object whose state is set to an appropriate default. For example, here is a constructor with no arguments for the Employee class:

public Employee()
{
name = "";
salary = 0;
hireDay = new Date();
}

If you write a class with no constructors whatsoever, then a no-argument constructor is provided for you. This constructor sets all the instance fields to their default values. So, all numeric data contained in the instance fields would be 0, all boolean values 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 no-argument constructor, it is illegal to construct objects without supplying arguments(如果有一个构造方法,但是不是无参构造方法,则通过无参构造方法构造一个对象是不合法的。). For example, our original Employee class in Listing 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.


Caution
Please keep in mind that you get a free no-argument 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 by a call to

new ClassName()

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

public ClassName()
{
}

4.6.4. Explicit Field Initialization
By overloading the constructor methods in a class, you can build 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 follows:

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

C++ Note
In C++, you cannot directly initialize instance fields of a class(C++中不可以直接初始化类的域). 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),
salary(s),
hireDay(y, m, d)
{
}

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


4.6.5. Parameter Names
When you write very trivial constructors (and you'll write a lot of them), 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.
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 constructed. Here is an example:

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

C++ Note
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, mSalary, or xSalary. Java programmers don't usually do that.


4.6.6. Calling Another Constructor
The keyword this refers to the implicit parameter of a method. However, this 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(如果一个构造函数的第一个语句是this(...),则这个构造函数调用了这个类中的另外一个构造函数。). Here is a typical example:

public Employee(double s)
{
// calls Employee(String, double)
this("Employee #" + nextId, s);
nextId++;
}

When you call new Employee(60000), 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(这种方式使用this关键字的好处是,只需编写共同的构造方法一次。).


C++ Note
The this reference in Java is identical to the this pointer in C++(Java中this引用和C++的this指针是相同的。). However, in C++ it is not possible for one constructor to call another(C++不可以一个构造方法调用另一个构造方法。). If you want to factor out common initialization code in C++, you must write a separate method.


4.6.7. 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 a third mechanism in Java, 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
{
private static int nextId;
private int id;
private String name;
private double salary;
// object initialization block
{
id = nextId;
nextId++;
}
public Employee(String n, double s)
{
name = n;
salary = s;
}
public Employee()
{
name = "";
salary = 0;
}
. . .
}

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 is usually more straightforward to place the initialization code inside a constructor.


Note
It is legal to set fields in initialization blocks even if they are only defined later in the class. However, to avoid circular definitions, it is not legal to read from fields that are only initialized later. The exact rules are spelled out in section 8.3.2.3 of the Java Language Specification (http://docs.oracle.com/javase/specs). The rules are complex enough to baffle the compiler implementors—early versions of Java implemented them with subtle errors. Therefore, we suggest that you always place initialization blocks after the field definitions.


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(一个构造函数被调用时发生了些什么):

  • All data fields are initialized to their default values (0, false, or null)(所有的数据域初始化为他妈的默认值).
  • All field initializers and initialization blocks are executed, in the order in which they occur in the class declaration(根据声明的顺序,所有的“域初始化”和“初始化块”都被执行).
  • If the first line of the constructor calls a second constructor, then the body of the second constructor is executed(如果构造方法的第一行调用了另一个构造方法,则另一个构造方法被执行).
  • The body of the constructor is executed(构造方法的函数体被执行).

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 strange and somewhat error-prone to have a class whose constructors depend on the order in which the data fields are declared.
To initialize a static field, either supply an initial value or use a static initialization block. You have already seen the first mechanism:

private 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 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.


Note
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
{
static
{
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 Listing 4.5 shows many of the features that we discussed in this section:
• Overloaded constructors
• A call to another constructor with this(...)
• A no-argument constructor
• An object initialization block
• A static initialization block
• An instance field initialization
4.6.8. 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, 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 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.


Note
The method call System.runFinalizersOnExit(true) guarantees 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. Supply a close method that does the necessary cleanup, and call it when you are done with the object. In Section 11.2.4, "The Try-with-Resources Statement," on p. 644, you will see how you can ensure that this method is called automatically.

Core Java Volume I — 4.6. Object Construction的更多相关文章

  1. Core Java Volume I — 1.2. The Java "White Paper" Buzzwords

    1.2. The Java "White Paper" BuzzwordsThe authors of Java have written an influential White ...

  2. Core Java Volume I — 4.7. Packages

    4.7. PackagesJava allows you to group classes in a collection called a package. Packages are conveni ...

  3. Core Java Volume I — 3.1. A Simple Java Program

    Let’s look more closely at one of the simplest Java programs you can have—one that simply prints a m ...

  4. Core Java Volume I — 3.10. Arrays

    3.10. ArraysAn array is a data structure that stores a collection of values of the same type. You ac ...

  5. Core Java Volume I — 5.1. Classes, Superclasses, and Subclasses

    5.1. Classes, Superclasses, and SubclassesLet's return to the Employee class that we discussed in th ...

  6. Core Java Volume I — 4.10. Class Design Hints

    4.10. Class Design HintsWithout trying to be comprehensive or tedious, we want to end this chapter w ...

  7. Core Java Volume I — 4.5. Method Parameters

    4.5. Method ParametersLet us review the computer science terms that describe how parameters can be p ...

  8. Core Java Volume I — 4.4. Static Fields and Methods

    4.4. Static Fields and MethodsIn all sample programs that you have seen, the main method is tagged w ...

  9. Core Java Volume I — 4.1. Introduction to Object-Oriented Programming

    4.1. Introduction to Object-Oriented ProgrammingObject-oriented programming, or OOP for short, is th ...

随机推荐

  1. loadrunner 参数化数据更新方式

    数据分配方式: Select next row[选择下一行]: 顺序(Sequential):按照参数化的数据顺序,一个一个的来取. 随机(Random):参数化中的数据,每次随机的从中抽取数据. 唯 ...

  2. 克隆机器后eth1变为eth0问题

    1. 清空该文件 2.进入网络配置文件把HADDR 和UUID注释掉,并重启 3.成功修改eth0 4. 4.可以结合这篇帖子来看   http://www.cnblogs.com/zydev/p/4 ...

  3. hdu 4606 Occupy Cities

    http://acm.hdu.edu.cn/showproblem.php?pid=4606 两点之间如果有线段相隔的话,他们的最短路就需要经过线段的端点 把所有线段的端点也加入点数组中,求任意两个点 ...

  4. Jmeter java.lang.OutOfMemoryError: GC overhead limit exceeded

    使用这个jmeter工具测试时,遇到这么个gc错误,网上找到了解决方案.原因是jmeter默认分配内存的参数很小,好像是256M吧.故而解决方法,就是增加内存: set HEAP=-Xms4g -Xm ...

  5. Ubuntu 14.04下安装eclipse搭建C++开发环境

    安装过程分为两部分:1.JAVA开发环境,即JDK的安装:2.eclipse的安装: 一.安装包下载 1.JDK官网下载地址:http://www.oracle.com/technetwork/jav ...

  6. uboot启动内核(3)

    nand read.jffs2 0x30007FC0 kernel; 从NAND读出内核:从哪读,从kernel分区 放到哪去   -0x30007FC0 nand read.jffs2 0x3000 ...

  7. Xceed WPF 主题皮肤控件Xceed Professional Themes for WPF详细介绍

    Xceed Professional Themes for WPF是一款为你的整个应用程序提供完美WPF主题风格的控件,包含Office 2007和Windows 7,可以应用到任何微软官方的WPF控 ...

  8. 移动设备和SharePoint 2013 - 第4部分:定位

    博客地址:http://blog.csdn.net/foxdave 原文地址 在该系列文章中,作者展示了SharePoint 2013最显著的新功能概观--对移动设备的支持. 该系列文章: 移动设备和 ...

  9. 对于C#中的一些点滴你真的理解了吗?

    废话不多说看题目,看看我们自己真的理解了吗? 1.如下代码输出的结果是什么? public class A{ public virtual void Func(int  number=10) { Co ...

  10. 一看就会之—利用IIS服务发布网站(实践篇)上

    转自:http://blog.csdn.net/zwk626542417/article/details/9796259 概述 IIS全称为互联网信息服务,是由微软公司提供的基于运行Microsoft ...