💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## [利用构造器保证初始化](https://lingcoder.gitee.io/onjava8/#/book/06-Housekeeping?id=%e5%88%a9%e7%94%a8%e6%9e%84%e9%80%a0%e5%99%a8%e4%bf%9d%e8%af%81%e5%88%9d%e5%a7%8b%e5%8c%96) 你可能想为每个类创建一个`initialize()`方法,该方法名暗示着在使用类之前需要先调用它。不幸的是,用户必须得记得去调用它。在 Java 中,类的设计者通过构造器保证每个对象的初始化。如果一个类有构造器,那么 Java 会在用户使用对象之前(即对象刚创建完成)自动调用对象的构造器方法,从而保证初始化。下个挑战是如何命名构造器方法。存在两个问题:第一个是任何命名都可能与类中其他已有元素的命名冲突;第二个是编译器必须始终知道构造器方法名称,从而调用它。C++ 的解决方法看起来是最简单且最符合逻辑的,所以 Java 中使用了同样的方式:构造器名称与类名相同。在初始化过程中自动调用构造器方法是有意义的。 以下示例是包含了一个构造器的类: ~~~ // housekeeping/SimpleConstructor.java // Demonstration of a simple constructor class Rock { Rock() { // 这是一个构造器 System.out.print("Rock "); } } public class SimpleConstructor { public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Rock(); } } } ~~~ 输出: ~~~ Rock Rock Rock Rock Rock Rock Rock Rock Rock Rock ~~~ 现在,当创建一个对象时:`new Rock()`,内存被分配,构造器被调用。构造器保证了对象在你使用它之前进行了正确的初始化。 有一点需要注意,构造器方法名与类名相同,不需要符合首字母小写的编程风格。在 C++ 中,无参构造器被称为默认构造器,这个术语在 Java 出现之前使用了很多年。但是,出于一些原因,Java 设计者们决定使用无参构造器这个名称,我(作者)认为这种叫法笨拙而且没有必要,所以我打算继续使用默认构造器。Java 8 引入了**default**关键字修饰方法,所以算了,我还是用无参构造器的叫法吧。 跟其他方法一样,构造器方法也可以传入参数来定义如何创建一个对象。之前的例子稍作修改,使得构造器接收一个参数: ~~~ // housekeeping/SimpleConstructor2.java // Constructors can have arguments class Rock2 { Rock2(int i) { System.out.print("Rock " + i + " "); } } public class SimpleConstructor2 { public static void main(String[] args) { for (int i = 0; i < 8; i++) { new Rock2(i); } } } ~~~ 输出: ~~~ Rock 0 Rock 1 Rock 2 Rock 3 Rock 4 Rock 5 Rock 6 Rock 7 ~~~ 如果类**Tree**有一个构造方法,只接收一个参数用来表示树的高度,那么你可以像下面这样创建一棵树: ~~~ Tree t = new Tree(12); // 12-foot 树 ~~~ 如果**Tree(int)**是唯一的构造器,那么编译器就不允许你以其他任何方式创建**Tree**类型的对象。 构造器消除了一类重要的问题,使得代码更易读。例如,在上面的代码块中,你看不到对`initialize()`方法的显式调用,而从概念上来看,`initialize()`方法应该与对象的创建分离。在 Java 中,对象的创建与初始化是统一的概念,二者不可分割。 构造器没有返回值,它是一种特殊的方法。但它和返回类型为`void`的普通方法不同,普通方法可以返回空值,你还能选择让它返回别的类型;而构造器没有返回值,却同时也没有给你选择的余地(`new`表达式虽然返回了刚创建的对象的引用,但构造器本身却没有返回任何值)。如果它有返回值,并且你也可以自己选择让它返回什么,那么编译器就还得知道接下来该怎么处理那个返回值(这个返回值没有接收者)。