首页 / Java 入门教程 / 构造方法

Java 入门教程

构造方法

本教程共 100 篇 · 第 33 篇 · 更新于 2026-08-05 · 约 6 分钟阅读

JavaJava 入门教程构造方法constructor默认构造构造重载

本节目标:搞懂构造方法为什么存在、和普通方法差在哪,会写无参和带参构造,能解释默认构造的消失规则,并用 this() 把多个构造串成一条链。

创建对象时,通常希望”一造出来就带着合理的数据”。构造方法就是干这个的:它在 new 的那一瞬间自动执行。

没有构造方法会怎样

先看不用构造方法的写法:

Student s = new Student();
s.name = "小明";
s.age = 18;
s.school = "一中";

四行才把一个对象装配好。问题有三个:

  • 容易漏。少写一行 age,对象就带着 0 岁上路了,编译器不管。
  • 中间态危险。第一行执行完到第四行之前,这个对象是残缺的。如果中间被别的代码拿去用,行为不可预测。
  • 没法强制。想要求”学生必须有名字”,靠注释提醒是拦不住人的。

构造方法把装配过程压缩成一步,而且是强制的一步

构造方法长什么样

它和类同名,没有返回类型,连 void 都不写。

public class Student {
    String name;
    int age;

    Student() {              // 无参构造,方法名 = 类名
        name = "未知";
        age = 0;
    }
}

new Student() 时,这段代码自动跑一遍,name 和 age 就有了初值。

和普通方法比,它有四点不同:

对比项构造方法普通方法
方法名必须和类名完全一致自己取,小驼峰
返回类型不写,连 void 都不能有必须写,无返回值写 void
调用方式只能被 new 触发(或 this()/super())用”对象.方法名()“主动调
调用次数每个对象一生只跑一次想调多少次调多少次

加了 void 会怎样

这是个经典陷阱:

public class Student {
    String name;

    void Student() {          // 加了 void,它不再是构造方法
        name = "未知";
    }
}

编译完全通过,没有任何警告。但它已经退化成一个”名字碰巧和类同名”的普通方法。new Student() 调的是编译器送的默认构造,你写的这段根本不会执行,name 依然是 null。

Warning

构造方法写了 void 是初学者最难自查的 bug 之一,因为编译器不报错,只是行为不对。发现”构造里的赋值没生效”,第一件事就是看有没有多写了返回类型。

默认构造方法

一个构造都不写时,编译器会偷偷送你一个无参、空实现的构造:

public class Dog {
    String name;
}

// 等价于编译器帮你补了这一段
// Dog() { super(); }

所以 new Dog() 能跑通。这个赠品叫默认构造方法

规则的关键在于:只要你自己写了任何一个构造,赠品立刻收回。

public class Student {
    String name;
    int age;

    Student(String name, int age) {   // 只写了带参构造
        this.name = name;
        this.age = age;
    }
}

这时再写 new Student() 就炸了:

error: constructor Student in class Student cannot be applied to given types;
        Student s = new Student();
                    ^
  required: String,int
  found:    no arguments
  reason:   actual and formal argument lists differ in length

报错读法:required 是”我要的参数”,found 是”你给的参数”,reason 说明对不上的原因是个数不同。需要无参版本,自己补一个就是了。

带参构造:一步到位

public class Student {
    String name;
    int age;

    Student(String name, int age) {
        this.name = name;      // this.name 是字段,右边 name 是参数
        this.age = age;
    }
}

// 使用
Student s = new Student("小明", 18);   // 造出来就是完整的

参数名和字段名保持一致是主流风格,代价是必须写 this.,否则等于把参数赋给自己,字段纹丝不动。this 的细节下一章讲。

带参构造还能顺手做校验,把非法数据挡在对象诞生之前:

Student(String name, int age) {
    if (age < 0 || age > 150) {
        throw new IllegalArgumentException("年龄不合法:" + age);
    }
    this.name = name;
    this.age = age;
}

对象要么造出来就是合法的,要么根本造不出来。这比”造完再检查”可靠得多。

构造方法重载

构造方法也能重载,规则和普通方法一样:参数列表不同即可。

public class Student {
    String name;
    int age;

    Student() {
        this.name = "未知";
        this.age = 0;
    }

    Student(String name) {
        this.name = name;
        this.age = 0;
    }

    Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

调用方按需挑一个:

Student a = new Student();
Student b = new Student("小明");
Student c = new Student("小红", 17);

问题也来了:三个构造里 this.age = 0this.name = name 反复出现。哪天加一条校验规则,三处都得改,漏一处就是 bug。

用 this() 串成构造链

this(参数) 表示”调用本类的另一个构造方法”。把公共逻辑集中到参数最全的那个里:

public class Student {
    String name;
    int age;

    Student() {
        this("未知", 0);           // 转交给双参构造
    }

    Student(String name) {
        this(name, 0);             // 也转交给双参构造
    }

    Student(String name, int age) {   // 唯一真正干活的构造
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("年龄不合法:" + age);
        }
        this.name = name;
        this.age = age;
    }
}

现在校验逻辑只有一份,三个入口全都受它保护。这种”参数少的转交给参数多的”写法叫构造链,是标准套路。

用 this() 有两条硬规矩。

第一,它必须是构造方法体的第一条语句。写在后面直接报错:

error: call to this must be first statement in constructor

第二,不能兜圈子。A 调 B、B 又调 A,编译器会识破:

error: recursive constructor invocation
Note

Java 25 把这条”必须第一条”的规则放宽了(JEP 513 灵活构造体,已转正)。现在允许在 this() 或 super() 之前写一小段前导语句,用来校验参数、算中间值。前提是这些语句不能引用正在构造的对象:不能读字段、不能调实例方法、不能把 this 传出去。对初学者来说,仍然建议直接把 this() 写在第一行,最省心也最通用。

构造方法总是从父类开始跑

前面那段注释里的 super() 不是随手写的。Java 规定:任何构造方法执行前,父类的构造必须先跑完。你不写,编译器就自动插一句无参的 super()。

class Animal {
    Animal() {
        System.out.println("Animal 构造执行");
    }
}

class Cat extends Animal {
    Cat() {
        // 这里被编译器悄悄插入了 super();
        System.out.println("Cat 构造执行");
    }
}

new Cat() 的输出顺序是先 Animal 后 Cat。道理和盖楼一样:地基没打好,二层无从谈起。父类字段先初始化完毕,子类构造才能放心使用它们。

这也解释了为什么 this() 和 super() 不能同时出现在一个构造里——它俩抢的是同一个位置,而且各自都会引发一次父类初始化。用了 this() 转交,父类构造由链条末端那个构造去触发。继承的完整规则在第 39 和 41 章展开。

this() 和 this.属性 不是一回事

两者都带 this,用途完全不同:

  • this(参数) —— 调用本类的另一个构造方法,只能出现在构造方法里,且最多一次。
  • this.属性 —— 访问当前对象的字段,任何实例方法里都能用。

记混了会写出 this.Student(...) 这种不存在的语法。下一章会把 this 的所有用法归拢讲一遍。

常见疑问

Q:构造方法能不能加 private?

A:能。私有构造意味着外部无法 new,只能通过类内部提供的静态方法拿对象。工具类常用这招把无参构造设成 private,防止别人误建实例。

Q:构造方法里能调用普通方法吗?

A:语法上可以,但要当心。构造还没跑完,对象处于半成品状态,此时调用的方法如果读了还没赋值的字段,拿到的是默认值。尤其别在构造里调用可能被子类重写的方法。

Q:一个类可以完全不写构造方法吗?

A:可以,编译器会补默认构造。但只要你需要”创建时必须传入某些数据”,就该显式写带参构造,让编译器帮你强制这个约定。

Tip

判断标准很简单:这个字段”没有它对象就不完整”,就放进构造方法参数里;可有可无的,留给 setter 后补。能用带参构造一步到位的,别 new 完再零散赋值。