Java中的构造器在创建对象(实例)的时候执行初始化。Java类必须包含一个或一个以上的构造器。
Java中的构造器类似C++中的构造函数。
Java中对象(object)的默认初始化规则是:
- 数值型变量初始化为0;
- 布尔型变量初始化为
false
; - 引用变量初始化为
null
。
如果类中没有任何显式定义的构造器,系统会提供一个无参数的构造器,这个构造器执行体为空,不做任何事情(但是实际上执行了默认初始化)。
通过自定义构造器就能执行自定义的初始化:
public class ConstructorTest
{
public String name;
public int count;
// 这个自定义的构造器允许从外面传递两个参数进来进行成员变量name和count的初始化
public ConstructorTest(String name, int count)
{
this.name = name;
this.count = count;
}
public static void main(String[] args)
{
var tc = new ConstructorTest("疯狂Java讲义", 90000);
System.out.println(tc.name); // 初始化为”疯狂Java讲义“
System.out.println(tc.count); // 初始化为90000
}
}
构造器是创建Java对象的途径,是不是说构造器完全负责创建Java对象?
不是。当通过关键字new
调用构造器时,构造器返回了该类的对象。但是,这个对象并不是完全由构造器创建的。
-
实际上,调用构造器时,系统首先为该对象分配内容,并对这个对象执行默认初始化,这时这个对象已经产生了。只不过这个对象不能被外部程序访问。
-
而在构造器内,这个临时对象以
this
的形态存在。 -
当构造器执行完毕后,这个对象以构造器的返回值(注意构造器并没有显式定义返回值,但默认返回值就是对象的引用)被返回,通常直接赋值给一个引用变量,从而让外部程序可以访问这个对象。
在上述过程中,一个默认的对象首先在内存中出现;接着是使用构造器实现自定义的初始化;最后是返回这个对象供外部使用。
一旦类中提供了自定义的构造函数,默认的无参构造函数就不存在了
当然,用户可以自行定义无参的构造函数。实际上,可以为这个类提供多个构造器(想一想方法的重载)。
一般情况下,构造器是public
的。但是,如果仅供子类使用,可以修饰为protected
;如果不想让其他类创建该类的实例,可以用private
修饰。
构造器的访问权限,是实现几种设计模式的基础。
构造器在重载时,有一种情况是构造器B的执行体覆盖了构造器A的执行体,如图所示:
这种情况下,可以在方法B中调用方法A来简化代码。但是,构造器当做普通函数来直接调用,而必须通过new
来调用。而一旦在构造器B中使用new
来调用构造器A,就会在内存中重新创建一个对象。
既要在B中调用A,又不想创建额外的对象,方法就是依然使用构造器B来new
对象,但是利用this
来调用构造器A的构造器:
public class Apple
{
public String name;
public String color;
public double weight;
public Apple(){}
public Apple(String name, String color)
{
this.name = name;
this.color = color;
}
public Apple(String name, String color, double weight)
{
// 利用this调用另一个重载的构造器
this(name, color); // 取代了new关键字,避免分配新的对象内存
this.weight = weight; // 使用this初始化其他部分
}
}
使用this
调用其他构造器的话,这句话必须做为当前构造器执行体的第一句。系统会根据this
后括号里的实参来确定是哪一个构造器。
这么做有啥用?我为啥不能把构造器A的代码粘贴过来?
当然可以!但是软件工程有一个理念:不要把相同的代码书写两次以上!
想象一下,如果有多个构造器A,B,C……,他们都包含了一段相同的代码。如果有一天,A的代码改了,那么就要把B,C……都打开,把对应修改的代码给粘贴进来。但是使用
this
的话,A修改完,B,C……就自动被修改了。既省事,又避免冗长代码带来的可能的错误。
我们的理念:充分复用每一段代码。