1.重载
有些类有多个构造器。例如,可以如下构造一个空的StringBuilder对象:
StringBuilder message = new StringBuilder();
或者,可以指定一个初始字符串:
StringBuilder message = new StringBuilder("Hello World!");
这种特征叫做重载(overloading)。如果多个方法(比如,StringBuilder构造器方法)有相同的名字、不同的参数,便产生了重载。编译器必须挑选出具体执行哪个方法,它通过用各个方法给出的参数类型与特定方法调用所使用的值类型进行匹配来挑选出相应的方法。如果编译器找不到匹配的参数,就会产生编译时错误,因为根本不存在匹配,或者没有一个比其他更好(这个过程被称为重载解析(overloading resolution))。值得注意的是,Java允许重载任何方法,而不只是构造器方法。
2.默认域初始化
如果在构造器中没有显式的给域赋予初值,那么就会被自动的赋予为默认值:数值为0、布尔值为false、对象应用为null。然而,只有缺少程序设计经验的人才会这么做。确实,如果不明确的对域进行初始化,就会影响程序代码的可读性。就比如说上篇的Employee类,假定没有在构造器中对某些域进行初始化,就会默认的将name和sex域初始化为null,将age域初始化为0。但是这不是一个良好的编程习惯。如果此时,我们调用getName方法或者getSex方法,将会得到一个null值的引用,这应该不是我们所希望的结果。
3.无参构造器
很多类都包含一个无参的构造函数,对象由无参构造函数创建时,其状态会设置为适当的默认值。如果编写一个类时没有编写构造器,那么系统就会提供一个无参的构造器。这个构造器将所有的实力域设置为默认值。
注意:如果类中提供了至少一个构造器,但是没有提供无参数的构造器,则在构造对象时如果没有提供参数就会被视为不合法。例如Employee类只提供了一个构造方法:
package jackWe; public class Employee { private String name; private int age; private String sex; public Employee(String name, int age, String sex) { super(); this.name = name; this.age = age; this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public void getOneHeight() { System.out.println("姓名:"+ this.name + ";"); System.out.println("年龄:"+ this.age + ";"); System.out.println("性别:"+ this.sex + ";"); System.out.println("身高:"+ (int)(Math.random() * 200) + "cm。"); } }
对于这个类,构造默认的雇员就会是不合法的。也就是,调用
Employee employee = new Employee(); // 创建将会产生错误
将会产生错误。
警告:请记住,仅当类没有提供任何构造器的时候,系统才会提供一个默认的构造器。如果在编写类的时候,给出了一个构造器,哪怕是很简单的,要想让这个类的用户能够按照下列方式构造实例:
new className()
就必须提供一个默认的构造器(即不带参数的构造器)。当然,如果希望所有域被赋予默认值,可以采用下列格式:
public Employee() { // 默认构造器 }
4.调用另一个构造器
关键字this引用方法的隐式参数。然而,这个关键字还有另外一个含义。如果构造器的第一个语句如同this(...),那么,这个构造器将调用同一类中的另外一个构造器,下面给个例子:
public Employee() { this("Jack",18,"男"); //调用另外一个构造器 }
5.初始化块
前面已经说过了两种初始化数据域的方法:
①、构造器中设置值;
②、在声明中赋值。
实际上,Java还有第三种机制,成为初始化块。在一个类的声明中,可以包含多个代码块。只要构造类的对象,这些块就会被执行。
由于初始化数据域有多种途径,所以列出构造过程的所有路径可能相当混乱。下面是调用构造器的具体处理步骤:
①、所有数据域被初始化为默认值(0,false,null);
②、按照在类声明中出现的次序,依次执行所有域初始化语句和初始化块;
③、如果构造器第一行调用了第二个构造器,则执行第二个构造器主体;
④、执行这个构造器的主体。
当然,我们应该精心的组织好初始化代码,这样有利于其他程序员的理解。例如,如果让类构造器行为依赖于数据域声明的顺序,那就会显得很奇怪并且容易引起错误。
可以通过提供一个初始化值,或者使用一个静态的初始化块来对静态域进行初始化:
private static int nextId = 1;
如果对类的静态域进行初始化的代码比较复杂,那么可以使用静态的初始化块。
将代码放在一个块中,并标记关键字 static 。例如:
// static initialization block static { Employee employee = new Employee(); int age = (int) (Math.random() * 100); employee.setAge(age); }
在类第一次加载的时候,将会进行静态域的初始化。与实力域不一样,除非将它们显式的设置成其他值,否则默认的初始值为0、false或null。所有的静态初始化语句以及静态初始化块都将依照类定义的顺序执行。
6.对象析构域finalize方法
由于Java有自动的垃圾回收器,不需要人工回收内存,所以Java不支持析构器。当然,某些对象使用了内存之外的其他资源,例如,文件或使用了系统资源的另一个对象的句柄。在这种情况下,当资源不在需要时,将其回收再利用将显得十分重要。
可以为任何一个类添加finalize方法。finalize方法将在垃圾回收器清除对象之前调用。在实际应用中,不要依赖于使用finalize方法回收任何短缺的资源,这是因为很难知道这个方法什么时候才能够调用。
如果某个资源需要在使用完毕后立刻被关闭,那么就需要人工来管理。对象用完时,可以应用一个close方法来完成相应的清理操作。