Java基础系列(十):对象构造

默认域初始化

如果在构造器中没有显式地给域赋予初值,如下这样:

1
2
3
4
5
6
7
private String name;
private double salary;
 
//默认域初始化
public User() {
 
}

那么就会自动地赋为默认值:数值为0,布尔值为false,对象为null,但是我们一般不建议这么做,这样会大大的增加了阅读程序的难度,比如说上面的程序中,name被初始化为null,当我们调用一个新建User对象的 getName()方法,就会抛出一个空指针异常。所以,我们应该在初始化域的时候尽量不要采用默认值,而是赋予一些有意义的初始值。

无参构造

很多类都包含一个无参数的构造函数,对象由无参构造函数创建的时候,它的状态会被设置成适当的默认值,比如:

1
2
3
4
5
6
7
8
private String name;
private double salary;
 
//无参构造,初始化值
public User() {
    name = "";
    salary = 0;
}

如果在编写一个类的时候忘记写构造器,不用担心,系统会提供一个无参的构造器,这个构造器将所有的实例域都设置为默认值,但是需要注意一点,如果类中提供了至少一个构造器,但是却没有提供无参构造,则在构造对象的时候没有提供参数就会视为不合法。 也就是说,仅仅当类没有提供任何构造器的时候,系统才会提供一个无参的构造器,如果在编写类的时候,给出了一个构造器,如果想要使用 newClassName()来构造对象,就必须提供一个默认的构造器(即不带参数的构造器)。

显式域初始化

当一个类的所有构造器都希望把相同的值赋予某个特定的实例域时,我们可以在类定义中,直接将一个值赋给这个特定的域:

1
2
3
4
class User {
 
    private String name = "";
}

当然,初始域也不一定非要是常量值,也可以调用方法对域进行初始化,比如我们使用方法来初始化User类的id,达到自动赋值的效果:

1
2
3
4
5
6
7
8
9
10
11
12
class User {
 
    private static int nextId;
 
    private int id = assignId();
 
    private static int assignId() {
        int r = nextId;
        nextId++;
        return r;
    }
}

参数名

一般来说,我们在使用有参数的构造函数时,通常会用到this关键字用来区分实例域和参数名:

1
2
3
4
public User(String name, double salary) {
    this.name = name;
    this.salary = salary;
}

调用另一个构造器

如果构造器的第一个语句形如this(...),这个构造器将调用同一个类的另一个构造器,比如:

1
2
3
4
5
public User(double s) {
 
    this("User #" + nextId, s)
    nextId++;
}

比如我们调用new User(3000)时候,User(double) 构造器将调用User(String, double)构造器。

采用着这种方式使用this关键字很有用,这样对公共的构造器代码部分只编写一次即可。

初始化块

在一个类的声明中,可以包含多个代码块。只要构造类的对象,这些块就会被执行。比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class User {
 
    private static int nextId;
    private int id;
    private String name;
    private double salary;
 
    {
        id = nextId;
        nextId++;
    }
 
    public User(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
 
    public User() {
        this.name = "";
        this.salary = 0
    }
}

在这个实例中,无论使用哪个构造器构造对象,id域都在对象初始化块中被初始化,首先运行初始化块,然后才运行构造器的主体部分。

总结

上面我们一共说了三种构造器,下面时调用构造器的具体处理步骤

  1. 所有数据域被初始化为默认值(0,false或null)
  2. 按照在类声明中出现的次序,依次执行所有域初始化语句和初始化块
  3. 如果构造器第一行调用了第二个构造器,则执行第二个构造器主体
  4. 执行构造器主体。

 

评论:

3 条评论,访客:3 条,站长:0 条
  1. 笑着哭
    笑着哭发布于: 

    默认域初始化中提到“ 比如说上面的程序中,name被初始化为null,当我们调用一个新建User对象的 getName()方法,就会抛出一个空指针异常”。这句话有误,实际不会报空指针,getName()只是返回null,并没有调用不存在的属性或者方法。验证代码:User user= new User();System.out.println(user.getName());

  2. 浅陌洛城
    浅陌洛城发布于: 

    弱弱的问一下, User这种通过静态属性实现id赋值的,虽然每次都加1,但是如果数据库中User对应的表id是从1001开始的,这种静态属性实现id自增长的方式是否可靠。怎么让user对象创建时,从数据库中取得最新的id呢。有点不太明白

    • Vi的技术博客
      Vi的技术博客发布于: 

      首先呢,这个例子只是一个小demo,作为一个对象构造的小技巧的讲解,实际上,在实战中,我们并不会这么去做,最简单的做法是我们可以在mysql在创建表的时候设置为主键自增来解决这个问题,当然还有其他的主键策略,我会在后面的实战项目教程中讲解哒,敬请期待~
      如果还有其他问题可以加我微信 cm_950825 并注明在哪看到我的~

发表评论