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();