作者:CHAITANYA SINGH
来源:https://www.koofun.com//pro/kfpostsdetail?kfpostsid=25
构造函数是用来初始化新创建的对象的代码块。构造函数类似于java中的实例方法(Instance Method),但它不是一个方法(Method),因为它没有返回类型。简而言之,构造函数和方法是不同的(在本指南的末尾将有更多的介绍)。人们经常把Java中的构造函数称为特殊类型的方法。
构造函数的名称必须与与类名相同,如下图中的java代码所示:
1
2
3
4
5
6
|
public
class
MyClass{
//This is the constructor
MyClass(){
}
..
}
|
注意,构造函数名与类名必须一致,并且没有返回类型。
构造函数是如何工作的?
下面我们通过一个例子来帮助大家理解构造函数的工作原理。我们先创建一个名字叫MyClass的类:
1
|
MyClass obj =
new
MyClass();
|
以上代码的意思就是通过new关键字来创建类MyClass的对象并调用这个类的构造函数来初始化这个新创建的对象,然后把这个对象赋值给对象变量obj。语句new MyClass()表示创建一个新的关于类MyClass的对象并调用构造函数来初始化这个新创建的对象。
下面我们来通过代码实例来解释什么是新创建对象的初始化。
一个简单的java构造函数程序
这里我们基于类Hello创建了一个对象obj,然后我们显示这个对象的实例变量name的值。在构造函数里面,我们给变量name赋值“BeginnersBook.com”,在赋值语句里面的this关键字表示引用当前的对象。我们将在下一个教程中详细介绍this关键字。
1
2
3
4
5
6
7
8
9
10
11
|
public
class
Hello {
String name;
//Constructor
Hello(){
this
.name =
"BeginnersBook.com"
;
}
public
static
void
main(String[] args) {
Hello obj =
new
Hello();
System.out.println(obj.name);
}
}
|
输出:
1
|
BeginnersBook.com
|
构造函数的类型
构造函数有三种类型:默认构造函数、无参数构造函数和参数化构造函数。
默认构造函数
如果在类(class)中没有定义代码来实现任何构造函数,Java编译器将会在编译的字节码里面为你插入默认构造函数。不会在源代码(java文件)中找到它,因为默认构造函数是在编译期间由编译器插入到代码中,并且存在于.class文件中,所以你在源代码(java文件)里面是看不到这个默认构造函数的。这个过程如下图所示:
注:如果你在类(class)里面实现了任何构造函数,那么Java编译器将不会在你的代码中插入默认构造函数。
无参数构造函数
不带参数的构造函数称为无参数构造函数,其形式看上去与默认构造函数相同,但是无参数构造函数里面可以有任何代码,而默认构造函数里面则是空的。
注:虽然您可能会看到一些人声称默认构造函数和无参数构造函数是相同的,但实际上它们并不相同,只要是你写的构造函数,就不能称之为默认构造函数。
示例:无参数构造函数
1
2
3
4
5
6
7
8
9
10
|
class
Demo
{
public
Demo()
{
System.out.println(
"This is a no argument constructor"
);
}
public
static
void
main(String args[]) {
new
Demo();
}
}
|
输出:
This is a no argument constructor
参数化构造函数
带参数的构造函数称为参数化构造函数。
示例1:参数化构造函数
在这个例子中,我们定义的类Employee里面有一个带有两个参数(id,name)的参数化构造函数。用这个类Employee创建两个对象,obj1和obj2,在创建obj1和obj2时都调用了这个参数化构造函数,分别往里面传了两套不同的参数:10245、"Chaitanya"和92232、"Negan",传进去的参数都赋值给了类的成员变量empId和empName,后面的代码把obj1和obj2的empId和empName分别打印出来,见下图:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public
class
Employee {
int
empId;
String empName;
//parameterized constructor with two parameters
Employee(
int
id, String name){
this
.empId = id;
this
.empName = name;
}
void
info(){
System.out.println(
"Id: "
+empId+
" Name: "
+empName);
}
public
static
void
main(String args[]){
Employee obj1 =
new
Employee(
10245
,
"Chaitanya"
);
Employee obj2 =
new
Employee(
92232
,
"Negan"
);
obj1.info();
obj2.info();
}
}
|
输出:
1
2
|
Id:
10245
Name: Chaitanya
Id:
92232
Name: Negan
|
例2:参数化构造函数
在这个例子中,我们有两个构造函数,一个默认构造函数和一个参数化构造函数。当我们在使用new关键字创建对象而且不传递任何参数时,系统将调用默认构造函数,但是如果我们在使用new关键字创建对象的时候传递了参数,那么系统将调用与传递的参数列表匹配的参数化构造函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class
Example2
{
private
int
var;
//default constructor
public
Example2()
{
this
.var =
10
;
}
//parameterized constructor
public
Example2(
int
num)
{
this
.var = num;
}
public
int
getValue()
{
return
var;
}
public
static
void
main(String args[])
{
Example2 obj =
new
Example2();
Example2 obj2 =
new
Example2(
100
);
System.out.println(
"var is: "
+obj.getValue());
System.out.println(
"var is: "
+obj2.getValue());
}
}
|
输出:
1
2
|
var is:
10
var is:
100
|
如果我们类中只实现了参数化构造函数,而没有实现默认构造函数(不带参数的),会发生什么?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class
Example3
{
private
int
var;
public
Example3(
int
num)
{
var=num;
}
public
int
getValue()
{
return
var;
}
public
static
void
main(String args[])
{
Example3 myobj =
new
Example3();
System.out.println(
"value of var is: "
+myobj.getValue());
}
}
|
输出:编译报错
原因:在代码Example3 myobj = new Example3();中,我们调用了类Example3里面的不含参数的默认构造函数,还记得我们在上面提到的,一旦我们在类里面有实现带参数的构造函数,那么编译器就不会自动在背后给我们插入不含参数的默认构造函数,而代码new Example3();调用了类(class)Example3的不存在的默认构造函数,当然编译就会报错啦!
如果我们从上面的代码中删除参数化构造函数的实现代码,那么程序编译和运行都不会有问题,因为编译器会在编译的过程中将默认构造函数插入到您的代码中。
构造函数链
当构造函数调用同一个类的另一个构造函数时,这称为构造函数链,见下图:
super()
每当我们调用子类构造函数时,它都隐式地调用了父类的构造函数,可以把这理解为编译器在子类构造函数的开头插入了super();语句。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
MyParentClass {
MyParentClass(){
System.out.println(
"MyParentClass Constructor"
);
}
}
class
MyChildClass
extends
MyParentClass{
MyChildClass() {
System.out.println(
"MyChildClass Constructor"
);
}
public
static
void
main(String args[]) {
new
MyChildClass();
}
}
|
输出:
1
2
|
MyParentClass Constructor
MyChildClass Constructor
|
构造函数重载
构造函数重载指的是同一个类里面定义实现了多个不同参数列表的构造函数,虽然这些构造函数的名字都是一样的(和类名一样),但在创建这个类的对象的时候,如果传进去的参数不同,调用的构造函数也是不同的。
Java的复制构造函数
复制构造函数用于将一个对象的值复制到另一个对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
class
JavaExample{
String web;
JavaExample(String w){
web = w;
}
/* This is the Copy Constructor, it
* copies the values of one object
* to the another object (the object
* that invokes this constructor)
*/
JavaExample(JavaExample je){
web = je.web;
}
void disp(){
System.out.println("Website: "+web);
}
public static void main(String args[]){
JavaExample obj1 = new JavaExample("BeginnersBook");
/* Passing the object as an argument to the constructor
* This will invoke the copy constructor
*/
JavaExample obj2 =
new
JavaExample(obj1);
obj1.disp();
obj2.disp();
}
}
|
输出:
1
2
|
Website: BeginnersBook
Website: BeginnersBook
|
要点回顾:
1. 每个类都有一个构造函数,不管它是普通类还是抽象类。
2. 构造函数不是方法,它们没有任何返回类型。
3. 构造函数名应该与类名匹配。
4. 构造函数可以使用任何访问说明符,它们也可以声明为私有。在java中可以使用私有构造函数,但是私有构造函数的作用域局限在类里面。
5. 我们也可以定义一个和类名相同的方法,但方法必须要定义返回值的数据类型,我们可以据此判断和类名相同的方法和构造函数的区别。
6. 如果我们没有在类中定义和实现任何构造函数,编译器就会在编译的时候自动在后台的代码中插入默认构造函数。
7. this()和super()这两个语句如果放在构造函数代码里面,一定要放在构造函数代码里面的第一行。如果我们的代码里面没有这两个语句,编译器会在后台的代码中自动插入这两个语句。
8. 构造函数可以重载(overload),但不能重写(override)。这意味着我们可以在类中重载(overload)构造函数,但不能重写(override)构造函数。
9. 构造函数不能继承。
10. 如果父类(super class)没有定义无参数构造函数(默认构造函数),那么编译器也不会自动在子类(child class)的后台代码里面自动插入默认构造函数。
11. 接口没有构造函数。
12. 抽象类可以有构造函数,抽象类的构造函数在创建具体类的对象的时候被调用到。
13. 构造函数代码里面还可以调用同一个类的另一个构造函数,使用方法就是通过调用this()方法,例如,在一个构造函数里面要调用另一个带参数的构造函数,可以用这样的写法:this(parameter list),这里parameter list就是传进去的几个参数。
构造函数(constructor)与方法(method)的区别
1. 构造函数的目的是初始化类的对象,而方法的目的是通过执行java代码来执行任务。
2. 构造函数不能是抽象的(abstract)、最终的(final)、静态的(static)和同步的(synchronised),而方法则可以是。
3. 构造函数没有返回类型,而方法有返回类型。