anonymous的Java学习笔记(9)——深入构造器

[toc]

深入构造器

构造器简介及用途

  • 构造器是一个特殊的方法,这个特殊方法用于创建实例对象时执行初始化。
  • 构造器是创建对象的重要途径(即使使用工厂模式、反射等方式创建对象,其实质依然是依赖于构造器)。因此,Java类必须包含一个或一个以上的构造器。

使用构造器进行初始化

  1. 当创建一个类的实例对象时,系统为这个对象的实例变量进行默认初始化。
    • 数值类型的实例变量初始化默认值为0
    • boolean类型的实例变量初始化默认值是false
    • 引用类型的实例变量初始化默认值是null
  2. 如果想改变默认初始化值,则需要通过构造器让系统创建对象时就为该对象的实例变量显式指定初始值。
    • 如果程序员没有为Java类提供任何构造器,则系统会为这个类提供一个无参数的构造器,这个构造器的执行体为空,不做任何事情。
    • 一旦程序员提供了自定义的构造器,系统就不再提供默认的构造器。代码示例
      package com.abc.part4;
      
      /**
       * 构造器初始化demo
       *
       * @author mi
       */
      public class ConstructorInitialization {
          private String name;
          private int age;
          private char gender;
      
          /**
           * 创建一个自定义的构造器
           *
           * @param name   名字
           * @param age    年龄
           * @param gender 性别
           */
          public ConstructorInitialization(String name, int age, char gender) {
              //this代表它进行初始化的对象,分别将构造器形参中的name,age,gender传给ConstructorInitialization类中的实例变量:name,age,gender
              this.name = name;
              this.age = age;
              this.gender = gender;
          }
      
          public static void main(String[] args) {
              //使用自定义的构造器来创建对象,初始化类实例对象时需要传入三个参数name,age,gender分别对应构造器中的name,age,gender。
              ConstructorInitialization ci = new ConstructorInitialization("小花花", 21, '女');
              // 初始化ConstructorInitialization类的实例对象时为ConstructorInitialization类的实例变量显式指定默认值:name="小花花",age=21,gender='女'
              // 我的名字叫小花花, 我今年21岁了, 我的性别是女。
              System.out.println("我的名字叫" + ci.name + ", 我今年" + ci.age + "岁了, 我的性别是" + ci.gender + "。");
          }
      }
      

构造器重载

同一个类里具有多个构造器,多个构造器的形参列表不同,即被称为构造器重载。

构造器重载允许Java类里包含多个初始化逻辑,从而允许使用不同的构造器来初始化Java对象。

构造器重载的条件

  1. 同一个类里构造器名字都相同,形参列表不相同。
  2. 因为构造器一定与类名相同,所以所有的构造器名字也都必须相同。

代码示例

package com.abc.part4;

/**
 * @author mi
 */
public class ConstructorOverload {
    private String name;
    private int age;

    /**
     * 手动定义一个无参构造器
     */
    public ConstructorOverload() {

    }

    /**
     * 定义一个有参构造器
     *
     * @param name 名字
     * @param age  年龄
     */
    public ConstructorOverload(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {
        ConstructorOverload co = new ConstructorOverload();
        //我叫null, 我的年龄是0。
        System.out.println("我叫" + co.name + ", 我的年龄是" + co.age + "。");
        ConstructorOverload co1 = new ConstructorOverload("小花花", 21);
        //我叫小花花, 我的年龄是21。
        System.out.println("我叫" + co1.name + ", 我的年龄是" + co1.age + "。");
        
    }
}

上面的ConstructorOverload类提供了两个重载的构造器,两个构造器的名字相同,但形参列表不同。系统通过new调用构造器时,系统将根据传入的实参列表来决定调用哪个构造器。

两个构造器属于包含关系的情况

还有一种情况,如果系统中包含了多个构造器,其中一个构造器的执行体里完全包含另一个构造器的执行体。如下图所示
构造器包含关系
对于这种完全包含的情况

  • 如果是两个方法之间存在这种关系,则可在方法B中调用方法A。
  • 但是构造器不能直接被调用,构造器必须使用new关键字来调用。但一旦使用new关键字来调用构造器,将会导致系统重新创建一个对象。

为了在构造器B中调用构造器A中的初始化代码,又不会重新创建一个Java对象,可以使用this关键宇来调用相应的构造器

代码示例

package com.abc.part4;

/**
 * @author mi
 */
public class Apple {
    private String name;
    private String color;
    private double weight;

    /**
     * 手动定义一个无参构造器
     */
    public Apple() {

    }

    /**
     * 两个形参的构造器
     *
     * @param name  名字
     * @param color 颜色
     */
    public Apple(String name, String color) {
        this.name = name;
        this.color = color;
    }

    /**
     * 三个形参的构造器
     *
     * @param name   名字
     * @param color  颜色
     * @param weight 重量
     */
    public Apple(String name, String color, double weight) {
        //通过this关键字来调用另一个重载的构造器的初始化代码
        this(name, color);
        //下面this关键字引用该构造器正在初始化的Java对象。
        this.weight = weight;
    }

    public static void main(String[] args) {
        Apple apple = new Apple("青苹果", "青绿色", 0.3);
        //我有一个青苹果, 颜色是青绿色, 重量是0.3kg。
        System.out.println("我有一个" + apple.name + ", 颜色是" + apple.color + ", 重量是" + apple.weight + "kg。");
    }
}
  • 使用this调用另一个重载的构造器只能在构造器中使用,而且必须作为构造器执行体的第一条语句
  • 使用this调用重载的构造器时,系统会根据this后括号里的实参来调用形参列表与之对应的构造器

为什么要用this来调用另一个重载的构造器?

为了实现代码复用,尽量避免相同的代码重复出现,充分复用每一段代码,既可以让程序代码更加简洁,也可以降低软件的维护成本。