8.7 构造函数
对于一个新定义的类,我们之所以能够用类似于这样的语句:
Studentstu=newStudent();
来声明一个新的对象,其实是调用了一个函数,这个函数叫做构造函数。
什么是构造函数呢,有什么作用呢?
构造函数的作用是给新创建的对象分配内存,初始化属性的值。对于分配内存,我们不用做什么,编译器已经做好了,我们要自己写的是后面的那个:初始化属性。事实上,你就算是一个这样的类,里面什么都没有:
public class Chapter{
}
即使这个类貌似什么“内容”都没有
,编译器也自动给你添加了一个没有参数的构造函数,这个自动添加的构造函数,使得你可以这样写:
Chapter c=new Chapter();
new后面的Chapter()就是编译器添加的构造函数。
虽然编译器会提供一个缺省构造函数,但我们也可以手动写。看下面10到12行代码:
public class Chapter{ int id; String code; String name; String content; String parentCode; public int getId(){ return id; } public Chapter(){ System.out.println("缺省构造函数被调用"); } public static void main(String[]args){ Chapter c=new Chapter(); Chapter c2=new Chapter(); Chapter c1=new Chapter(); Chapter c4=new Chapter(); System.out.println(c.getId()); }}
这样,上面的代码声明Chapter对象,都会打印“缺省构造函数被调用”:
现在来看看上面的构造函数(10到12行代码)的特别之处:
1.函数必须和类名字一样,大小写也一样;
2.函数没有返回类型,void也不可以写;
3.只要是构造函数,编译器就会插入分配内存的代码,而这段代码我们是看不到的,也无需关心的。
手动写的无参数的构造函数,编译器也会插入分配内存的代码。编译器总是在构造函数最开始的地方插入分配内存的代码,上面的构造函数打印“缺省构造函数被调用”之前,已经分配好内存了。
事实上,只要是构造函数,编译器都会插入分配内存的代码。下面的代码创建一个对象a,然后打印a的两个属性id和parentCode,相当于是构造函数创建出来的对象,其属性都有了默认值,id默认值是-1,parentCode默认值是000:
public class Chapter{ int id; String code; String name; String content; String parentCode; public int getId(){ return id; } public Chapter(){ System.out.println("缺省构造函数被调用"); this.id=-1; this.name=""; this.content=""; this.parentCode="000"; } public static void main(String[]args){ Chapter a=new Chapter(); System.out.println(a.id); System.out.println(a.parentCode); }}
4.构造函数只要参数不一样,可以有多个,参数的类型不同、参数个数不同可视为另一个函数。关于这点,在重载方法里有详细介绍。
除了上面没有参数的构造函数。
现在我们来添加一个带一个参数的构造函数:
public class Chapter{ int id; String code; String name; String content; String parentCode; public Chapter(){ System.out.println("缺省构造函数被调用"); }}
假设id值是任何对象都必须要有的值,它很重要。那么我们可以写一个这样的构造函数(第16到20行代码):
public class Chapter{ int id; String code; String name; String content; String parentCode; public int getId(){ return id; } public Chapter(){ this.id=-1; this.name=""; this.content=""; this.parentCode="000"; } public Chapter(int id){ //这个this()指的是上面的不带参数的构造函数Chapter(),作用就是初始化属性 this(); this.id=id; }}
这个构造函数需要一个参数,用来设置id值。那么新生成一个对象的时候,就可以这样调用了,Chapter对象c被创建,同时c的id属性赋值为8898,同时还可以生成一个新的对象c2:
public static void main(String[]args){
Chapter c=new Chapter(8898);
Chapter c2=new Chapter();
System.out.println(c.getId());
}
5.如果类里面没有任何构造函数,编译器会自动生成一个无参数的构造函数;如果类里面已经有构造函数,那么编译器不会生成无参数的构造函数。(你无我生成,你有我不管)
关于这点,我们用代码来说明:
public class Chapter{
}
这个类没有构造方法,但是仍然可以使用Chapterc=newChapter();来声明一个对象。这是因为编译器生成了一个无参数的构造函数,然后就可以用newChapter()生成对象。这就是所谓“你无我生成”。看这段代码:
public class Chapter{ int id; String code; String name; String content; String parentCode; public int getId(){ return id; } public Chapter(int id){ this.id=id; } public static void main(String[]args){ //以下四个Chapter对象的声明都会出错,因为没有无参数的购置函数; Chapter c=new Chapter(); Chapter c2=new Chapter(); Chapter c1=new Chapter(); Chapter c4=new Chapter(); System.out.println(c.getId()); }}
这里面有一个带一个参数的构造函数,于是可以用Chapterc=newChapter(12);这样的代码来创建一个对象,同时给对象的id属性赋值,但是!上面这个类,已经不能使用Chapterc=newChapter();这个无参数的构造函数了,因为这个是编译器在你没有构造函数的时候为你生成的,一旦你有构造函数,那么编译器就不再为你生成构造函数了,如果你想要无参数构造函数,那么必须要这样写:
public class Chapter{ int id; String code; String name; String content; String parentCode; public Chapter(int id){ this.id=id; } //虽然看上去"什么也没有",但这个方法一定要有; public Chapter(){ } public static void main(String[]args){ //此时才是正确的; Chapter c=new Chapter(123); Chapter c2=new Chapter(); Chapter c1=new Chapter(); Chapter c4=new Chapter(12); System.out.println(c.getId()); }}
看上去似乎很多余,publicChapter()是一个什么内容也没有的函数,但只有这样写了,你才可以调用下面的代码来声明一个Chapter对象:
Chapter c=new Chapter();