Java构造器完全指南:从基础到高级的构造方法解析
在Java中,构造函数用于创建类的实例。构造函数与方法几乎相似,只有两个不同之处——它的名字与类名相同,并且没有返回类型。有时构造函数也被称为特殊方法来初始化对象。
Java中的构造函数
每当我们使用new关键字创建一个类的实例时,构造函数就会被调用并返回该类的对象。由于构造函数只能返回类的对象,这是由Java运行时隐式完成的,我们不应该为其添加返回类型。如果我们给构造函数添加返回类型,那么它将成为该类的一个方法。这是Java运行时区分普通方法和构造函数的方式。让我们假设在Employee类中有以下代码。
public Employee() {
System.out.println("员工构造函数");
}
public Employee Employee() {
System.out.println("员工方法");
return new Employee();
}
这里的第一个是构造函数,注意没有返回类型和返回语句。第二个是一个普通方法,我们再次调用第一个构造函数来获取Employee实例并返回它。不建议使用与类名相同的方法名,因为这会造成混淆。
Java中的构造函数类型
Java中有三种类型的构造函数。
- 默认构造函数
- 无参构造函数
- 带参数的构造函数
让我们用示例程序来研究所有这些构造函数类型。
在Java中的默认构造函数
在类代码中并不总是需要提供构造函数的实现。如果我们没有提供构造函数,Java 将为我们提供默认的构造函数实现供我们使用。让我们看一个简单的程序,在这个程序中,由于我们不会显式定义构造函数,所以使用了默认构造函数。
package com.Ethan.constructor;
public class Data {
public static void main(String[] args) {
Data d = new Data();
}
}
- 默认构造函数的作用只是初始化对象并将其返回给调用代码。
- 默认构造函数总是没有参数的,并且只在没有已定义的构造函数时由Java编译器提供。
- 大多数情况下,我们只需要默认构造函数,因为其他属性可以通过getter和setter方法访问和初始化。
无参数构造函数
没有任何参数的构造器被称为无参构造器。它类似于覆盖默认构造器,用于执行一些预初始化的操作,如检查资源、网络连接、日志记录等。让我们简要地了解一下Java中的无参构造器。
package com.Ethan.constructor;
public class Data {
//无参构造器
public Data() {
System.out.println("无参构造器");
}
public static void main(String[] args) {
Data d = new Data();
}
}

参数化构造函数
带有参数的构造函数被称为参数化构造函数。让我们来看一个Java中的参数化构造函数的例子。
package com.Ethan.constructor;
public class Data {
private String name;
public Data(String n) {
System.out.println("参数化构造函数");
this.name = n;
}
public String getName() {
return name;
}
public static void main(String[] args) {
Data d = new Data("Java");
System.out.println(d.getName());
}
}

Java中的构造器重载
当我们拥有多个构造函数时,这就是Java中的构造函数重载。让我们来看一个关于Java程序中构造函数重载的示例。
package com.Ethan.constructor;
public class Data {
private String name;
private int id;
//无参构造器
public Data() {
this.name = "默认名称";
}
//单参数构造器
public Data(String n) {
this.name = n;
}
//双参数构造器
public Data(String n, int i) {
this.name = n;
this.id = i;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
@Override
public String toString() {
return "ID="+id+", 名称="+name;
}
public static void main(String[] args) {
Data d = new Data();
System.out.println(d);
d = new Data("Java");
System.out.println(d);
d = new Data("Pankaj", 25);
System.out.println(d);
}
}
在Java中的私有构造函数
请注意,在构造函数中我们不能使用抽象、最终、静态和同步关键字。但是我们可以使用访问修饰符来控制类对象的实例化。使用公共和默认访问仍然是可以的,但是使构造函数私有有什么用呢?在这种情况下,任何其他类都将无法创建该类的实例。嗯,如果我们想要实现单例设计模式,就需要将构造函数设为私有。由于Java自动提供默认构造函数,我们必须显式地创建一个构造函数并将其设为私有。客户类将提供一个实用的静态方法来获取该类的实例。以下是Data类的私有构造函数示例。
// 私有构造函数
private Data() {
//用于实现单例模式的空构造函数
//可以包含在类的getInstance()方法中使用的代码
}
在Java中的构造器链
当构造函数调用同一个类的另一个构造函数时,这称为构造函数链。我们必须使用this关键字来调用类的另一个构造函数。有时,它用于设置类变量的一些默认值。注意,另一个构造函数调用应该是代码块中的第一个语句。此外,不能进行递归调用,否则会创建无限循环。让我们看一个在Java程序中使用构造函数链的例子。
package com.Ethan.constructor;
public class Employee {
private int id;
private String name;
public Employee() {
this("约翰·多伊", 999);
System.out.println("创建默认员工");
}
public Employee(int i) {
this("约翰·多伊", i);
System.out.println("使用默认名称创建员工");
}
public Employee(String s, int i) {
this.id = i;
this.name = s;
System.out.println("员工已创建");
}
public static void main(String[] args) {
Employee emp = new Employee();
System.out.println(emp);
Employee emp1 = new Employee(10);
System.out.println(emp1);
Employee emp2 = new Employee("Pankaj", 20);
System.out.println(emp2);
}
@Override
public String toString() {
return "ID = "+id+", 姓名 = "+name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
我已经重写了toString()方法,以打印有关员工对象的一些有用信息。以下是以上程序生成的输出结果。
员工已创建
创建默认员工
ID = 999, 姓名 = 约翰·多伊
员工已创建
使用默认名称创建员工
ID = 10, 姓名 = 约翰·多伊
员工已创建
ID = 20, 姓名 = Pankaj
请注意,一个构造函数被另一个构造函数调用,这被称为构造函数链处理过程。
Java超类构造函数
有时候,一个类会从一个超类继承,此时如果我们需要调用超类的构造函数,我们可以使用super关键字来实现。让我们来看一个使用超类构造函数的例子。注意,super构造函数调用应该是子类构造函数中的第一条语句。另外,在实例化子类构造函数时,Java会先初始化超类,然后再初始化子类。因此,如果没有显式调用超类的构造函数,则Java运行时会调用默认的无参构造函数。让我们通过一些示例程序来理解这些概念。假设我们有如下两个类:
package com.Ethan.constructor;
public class Person {
private int age;
public Person() {
System.out.println("Person Created");
}
public Person(int i) {
this.age = i;
System.out.println("Person Created with Age = " + i);
}
}
package com.Ethan.constructor;
public class Student extends Person {
private String name;
public Student() {
System.out.println("Student Created");
}
public Student(int i, String n) {
super(i); // 调用超类构造函数
this.name = n;
System.out.println("Student Created with name = " + n);
}
}
现在如果我们像下面这样创建一个学生对象:
Student st = new Student();
以上代码将产生什么输出?以上代码的输出将为:
Person Created
Student Created
所以,在第一句代码中没有super调用,因此调用了Student类的无参数构造函数,从而调用了Person类的无参数或默认构造函数。因此,输出结果如上所示。如果我们使用Student类的有参数构造函数,例如Student st = new Student(34, "张三");
,则输出结果将为:
Person Created with Age = 34
Student Created with name = 张三
这里输出是明确的,因为我们明确地调用了超类的构造函数,所以Java不需要做任何额外的工作。
Java的复制构造函数
Java拷贝构造函数接受同一类的对象作为参数,并创建其副本。有时候我们需要复制另一个对象来进行一些处理。我们可以通过以下方法来实现:
- 实现克隆技术
- 为对象提供一个深拷贝的实用方法
- 具备拷贝构造器
现在让我们来看一下如何编写一个拷贝构造函数。假设我们有一个类名为Fruits的类,具体如下所示:
package com.Ethan.constructor;
import java.util.ArrayList;
import java.util.List;
public class Fruits {
private List<String> fruitsList;
public List<String> getFruitsList() {
return fruitsList;
}
public void setFruitsList(List<String> fruitsList) {
this.fruitsList = fruitsList;
}
public Fruits(List<String> fl) {
this.fruitsList = fl;
}
public Fruits(Fruits fr) {
List<String> fl = new ArrayList<>();
for (String f : fr.getFruitsList()) {
fl.add(f);
}
this.fruitsList = fl;
}
}
请注意,Fruits(Fruits fr)构造函数正在执行深拷贝以返回对象的副本。让我们看一个测试程序,以了解为什么最好使用拷贝构造函数来拷贝一个对象:
package com.Ethan.constructor;
import java.util.ArrayList;
import java.util.List;
public class CopyConstructorTest {
public static void main(String[] args) {
List<String> fl = new ArrayList<>();
fl.add("芒果");
fl.add("橙子");
Fruits fr = new Fruits(fl);
System.out.println(fr.getFruitsList());
Fruits frCopy = fr;
frCopy.getFruitsList().add("苹果");
System.out.println(fr.getFruitsList());
frCopy = new Fruits(fr);
frCopy.getFruitsList().add("香蕉");
System.out.println(fr.getFruitsList());
System.out.println(frCopy.getFruitsList());
}
}
以上程序的输出是:
[芒果, 橙子]
[芒果, 橙子, 苹果]
[芒果, 橙子, 苹果]
[芒果, 橙子, 苹果, 香蕉]
请注意,当使用复制构造函数时,原始对象和其副本是互不相关的,对其中一个对象所做的任何修改不会反映到另一个对象上。这就是Java中构造函数的全部内容。