Java核心技术第4章(6)

4.6 对象构造

    前面已经学会了编写简单的构造器,以便对定义的对象进行初始化.但是,由于对象构造非常重要,所以Java提供了多种编写构造器的方式.下面将详细地介绍一下.

4.6.1   重载

    从前面可以看到,GregorianCalendar类有多个构造器,可以使用:
GregorianCalendar today = new GregorianCalendar();
    或者
GregorianCalendar deadline = new GregorianCalendar(2009, Calendar.DECEMBER, 31);
    这种特征叫做重载.如果多个方法有相同的名字,不同的阐述,便产生了重载.
    注释:Java允许重载任何方法,而不只是构造器方法.因此,要完整地描述一个方法需要指出方法名以及参数类型.这叫做方法的签名. 返回值类型不是方法签名的一部分,也就是说,不能有两个名字相同,参数类型也相同但返回值类型却不同的方法.

4.6.2   默认域初始化

    如果在构造器中没有显式地给域赋予初值,那么就会被自动地赋为默认值:数值为0,布尔值为 false,对象引用为 null .然而,只有缺少程序设计经验的人才会这样做/如果不明确地对域进行初始化,就会影响代码的可读性.
    注释:这是域与局部变量的主要不同点.必须明确地初始化方法中的局部变量,但是如果没有初始化类中的域,将会被初始化为默认值(0,false,null).

4.6.3   无参数的构造器

    很多类都包含一个无参数的构造器,对象由无参数构造函数创建时,其状态会设置为适当的默认值.例如,以下是Employee类的无参数构造函数:
public Employee()
{
    name = "";
    salary = 0;
    hireDay = new Date();
}
    如果在编写一个类时没有编写构造器,那么系统就会提供一个无参数构造器.这个构造器将所有的实例域设置为默认值.于是,实例域中的数值型数据设置为0,布尔型数据设置为 false,所有对象变量设置为 null .
    如果类中提供了至少一个构造器,但是没有提供无参数的构造器,则在构造对象时如果没有提供参数就会被视为不合法.
    警告:请记住,仅当类没有提供任何构造器时,系统才会提供一个默认的构造器.如果在编写类的时候,给出了一个构造器,要想让这个类的用户能够采用下列方式构造实例:
new ClassName()
    就必须提供一个默认的构造器(即不带参数的构造器).

4.6.4   显式域初始化

    由于类的构造器方法可以重载,所有可以采用多种形式设置类的实例域的初始状态.确保不管怎样调用构造器,每个实例域都可以被设置为一个有意义的初值.这是一种良好的编程习惯.
    可以在类定义中,直接将一个值赋给任何域,例如:
class Employee
{
    private String name = "";
    ...
}
    在执行构造器之前,先执行赋值操作.当一个类的所有构造器都希望把相同的值赋予某个特定的实例域时,这种方式特别有用.
    初始值不一定是常量.在下面的例子中,可以调用方法对域进行初始化.仔细看一下Employee类,其中每个对象都有一个id域.可以使用下列方式进行初始化:
class Employee
{
    private static int nextId;
    private int id = assignId();
    ...
    private static int assignId()
    {
        int r = nextId;
        nextId++;
        return r;
    }
    ...
}
    注释:在C++中,不能直接初始化类的实例域.所有的域必须在构造器中设置.但是,有一个特殊的初始化器列表语法,如下所示:
Employee::Employee(String n, double s, int y, int m, int d)
    : name(n), salary(s), hireDay(y, m, d)
{}
    C++使用这种特殊的语法来调用域构造器.在Java中没有这种必要,因为对象没有子对象,只有指向其他对象的指针.

4.6.5   参数名

    在编写很小的构造器时,常常在参数名上出现错误.
    通常,参数用单个字符命名:
public Employee(String n, double s)
{
    name = n;
    salary = s;
}
    但这样有一个缺陷:只有阅读代码才能够了解参数n和参数s的含义.于是有些 程序员在每个参数前面加上一个前缀"a":
public Employee(String aName, double aSalary)
{
    name = aName;
    salary = aSalary;
}
    这样很清晰.
    注释:在C++中,经常用下划线或某个固定的字母(一般选用m或x)作为实例域的前缀.例如salary域可能被命名为_salary,mSalary或xSalary.Java程序员通常不这样做.

4.6.6   调用另一个构造器

    关键字 this 引用方法的隐式参数.然而,关键字还有另外一个含义.
    如果构造器的第一个语句形如 this(...),这个构造器将调用同一个类的另一个构造器.下面是一个典型的例子:
public Employee(double s)
{
    // call Employee(String, double)
    this("Employee #" + nextId, s);
    nextId++;
}
    当调用 new Employee(6000)时,Employee(double)构造器将调用Employee(String, double)构造器.
    采用这种方式使用 this 关键字非常有用,这样对公共的构造器代码部分只编写一次即可.
    注释:在Java中,this 引用等价于C++的 this 指针.但是在C++中,一个构造器不能调用另一个构造器.在C++中必须将抽取出的公共初始化代码写成一个独立的方法.

4.6.7   初始化块

    前面已经讲过两种初始化数据域的方法:
    在构造器中设置值
    在声明中赋值
    实际上,Java还有第三种机制,称为 初始化块(initialization block).在一个类的声明中,可以包含多个代码块.只要构造类的对象,这些块就会被执行.例如,
class Employee
{
    private static 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;
    }
    ...
}
    在这个示例中,无论使用哪种构造器构造对象,id域都在对象初始化块中被初始化. 首先运行初始化块,然后才运行构造器的主体部分.
    这种机制不是必须的,也不常见.通常,直接将初始化代码放在构造器中.
    调用构造器的具体处理步骤:
    1.所有数据域被初始化为默认值(0,false,null)
    2.按照在类声明中出现的次序,依次执行所有域初始化语句和初始化块
    3.如果构造器第一行调用了第二个构造器,则执行第二个构造器主体
    4.执行这个构造器的主体

    下面这个程序展示了本节讨论的很多特性:
    重载构造器
    用 this(...)调用另一个构造器
    无参数构造器
    对象初始化块
    静态初始化块
    实例域初始化
    constructorTest.java如下所示:
import java.util.*;
public class ConstructorTest
{
    public static void main(String[] args)
    {
        // fill the staff array with three Employee objects
        Employee[] staff = new Employee[3];

        staff[0] = new Employee("harry", 4000);
        staff[1] = new Employee(6000);
        staff[2] = new Employee();

        // print out information about all Employee objects
        for (Employee e : staff)
            System.out.println("name = " + e.getName() + ", id = " + e.getId() + ", salary = " + e.getSalary());
    }
}
class Employee
{
    private static int nextId;

    private int id;
    private String name = "";   // instance field initialization
    private double salary;

    // static initialization block
    static
    {
        Random generator = new Random();
        // set nextId to a random number between 0 and 9999
        nextId = generator.nextInt(10000);
    }
    // object initialization block
    {
        id = nextId;
        nextId++;
    }
    // three overloaded constructors
    public Employee(String n, double s)
    {
        name = n;
        salary = s;
    }
    public Employee(double s)
    {
        // call the Employee(String, double) constructor
        this("Employee #" + nextId, s);
    }
    // the default construct
    public Employee()
    {
    }
    public String getName()
    {
        return name;
    }
    public double getSalary()
    {
        return salary;
    }
    public int getId()
    {
        return id;
    }
}

4.6.8   对象析构与finalize方法

    C++有显式的析构器方法,其中放置一些对当对象不再使用时需要执行的清理代码.在析构器中最常见的操作是回收分配给对象的存储空间.由于Java有自动的垃圾回收器,不需要人工回收内存,所以Java不支持析构器.
    如果某个资源需要在使用完毕后立刻被关闭,那么就需要由人工来管理.对象用完时,可以应用一个close方法来完成相应的清理操作.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值