构造函数在Java中用于创建类的实例。构造函数与方法几乎相似,除了两点——它的名称与类名相同,并且没有返回类型。有时构造函数也被称为特殊方法,用于初始化对象。
Java中的构造函数
每当我们使用new
关键字创建一个类的实例时,构造函数被调用,并返回类的对象。由于构造函数只能将对象返回给类,因此它由Java运行时隐式执行,我们不应该为其添加返回类型。如果我们为构造函数添加了返回类型,那么它将成为类的一个方法。这是Java运行时区分普通方法和构造函数的方法。让我们假设在Employee
类中有以下代码。
public Employee() {
System.out.println("Employee Constructor");
}
public Employee Employee() {
System.out.println("Employee Method");
return new Employee();
}
这里第一个是构造函数,请注意没有返回类型和返回语句。第二个是普通方法,在这里我们再次调用第一个构造函数以获取Employee实例并返回它。建议不要将方法名与类名相同,因为这会造成混淆。
Java中的构造函数类型
在Java中有三种类型的构造函数。
- 默认构造函数
- 无参构造函数
- 带参数的构造函数
让我们用示例程序来了解所有这些构造函数类型。
Java中的默认构造函数
在类代码中并不总是需要提供构造函数实现。如果我们不提供构造函数,那么Java会为我们提供默认构造函数实现供我们使用。让我们看一个简单的程序,其中默认构造函数被使用,因为我们不会显式定义构造函数。
package com.journaldev.constructor;
public class Data {
public static void main(String[] args) {
Data d = new Data();
}
}
- 默认构造函数的唯一作用是初始化对象并将其返回给调用代码。
- 默认构造函数总是无参的,只有在没有现有构造函数定义时,Java编译器才会提供。
- 大多数情况下,我们都满足于默认构造函数本身,因为其他属性可以通过getter和setter方法访问和初始化。
无参构造函数
构造函数没有任何参数称为无参数构造函数。 这类似于重写默认构造函数,并用于执行一些预初始化工作,例如检查资源、网络连接、日志记录等。 让我们快速看一下 Java 中的无参数构造函数。
package com.journaldev.constructor;
public class Data {
//无参数构造函数
public Data() {
System.out.println("No-Args Constructor");
}
public static void main(String[] args) {
Data d = new Data();
}
}
现在当我们调用new Data()
时,我们的无参数构造函数将被调用。 下图说明了这种行为,请检查程序的控制台输出。
带参数的构造函数
带参数的构造函数称为参数化构造函数。 让我们看一个 Java 中参数化构造函数的示例。
package com.journaldev.constructor;
public class Data {
private String name;
public Data(String n) {
System.out.println("Parameterized Constructor");
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.journaldev.constructor;
public class Data {
private String name;
private int id;
//无参数构造函数
public Data() {
this.name = "Default 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="+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 中的私有构造函数
请注意,我们不能在构造函数中使用 abstract、final、static 和 synchronized 关键字。但是,我们可以使用访问修饰符来控制类对象的实例化。仍然可以使用 public
和 default
访问,但是将构造函数设置为私有有什么用呢?在这种情况下,任何其他类都无法创建该类的实例。嗯,构造函数被设置为私有是为了实现 单例设计模式。由于 Java 自动提供默认构造函数,我们必须显式创建一个构造函数并将其设置为私有。客户类提供了一个实用的静态方法来获取该类的实例。下面给出了 Data
类的私有构造函数示例。
// 私有构造函数
private Data() {
// 用于单例模式实现的空构造函数
// 可以包含在类的 getInstance() 方法中使用的代码
}
Java中的构造函数链
当一个构造函数调用同一类的另一个构造函数时,称为构造函数链。我们必须使用this
关键字调用类的另一个构造函数。有时它用于设置类变量的一些默认值。请注意,另一个构造函数调用应该是代码块中的第一条语句。此外,不应该有递归调用,否则会创建无限循环。让我们看一个在Java程序中使用构造函数链的例子。
package com.journaldev.constructor;
public class Employee {
private int id;
private String name;
public Employee() {
this("John Doe", 999);
System.out.println("Default Employee Created");
}
public Employee(int i) {
this("John Doe", i);
System.out.println("Employee Created with Default Name");
}
public Employee(String s, int i) {
this.id = i;
this.name = s;
System.out.println("Employee Created");
}
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 = "+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;
}
}
I have overridden the toString() method to print some useful information about Employee object. Below is the output produced by above program.
Employee Created
Default Employee Created
ID = 999, Name = John Doe
Employee Created
Employee Created with Default Name
ID = 10, Name = John Doe
Employee Created
ID = 20, Name = Pankaj
注意一个构造函数如何从另一个构造函数中调用,这就是构造函数链的过程。
Java超类构造函数
有时候一个类会从一个超类继承,在这种情况下,如果我们必须调用超类的构造函数,我们可以使用super
关键字来实现。让我们看一个使用超类构造函数的例子。请注意,超类构造函数调用应该是子类构造函数中的第一个语句。此外,在实例化子类构造函数时,Java首先初始化超类,然后是子类。因此,如果没有显式调用超类构造函数,则Java运行时将调用默认或无参数构造函数。让我们通过一些示例程序来理解这些概念。假设我们有两个类如下。
package com.journaldev.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.journaldev.constructor;
public class Student extends Person {
private String name;
public Student() {
System.out.println("Student Created");
}
public Student(int i, String n) {
super(i); // super class constructor called
this.name = n;
System.out.println("Student Created with name = " + n);
}
}
现在,如果我们像下面这样创建一个Student对象;
Student st = new Student();
那么输出是什么呢?上述代码的输出将是:
Person Created
Student Created
因此,调用去了Student类的无参数构造函数,因为在第一个语句中没有super调用,所以调用了Person类的无参数或默认构造函数。因此得到这样的输出。如果我们使用Student类的带参数构造函数,如Student st = new Student(34, "Pankaj");
,输出将是:
Person Created with Age = 34
Student Created with name = Pankaj
这里输出是清楚的,因为我们明确调用了超类构造函数,所以Java不需要从他们的一侧做任何额外的工作。
Java拷贝构造函数
Java复制构造函数以同一类的对象作为参数,并创建其副本。有时我们需要另一个对象的副本来进行一些处理。我们可以通过以下方式实现:
现在让我们看看如何编写复制构造函数。假设我们有一个类 Fruits
如下。
package com.journaldev.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.journaldev.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("Mango");
fl.add("Orange");
Fruits fr = new Fruits(fl);
System.out.println(fr.getFruitsList());
Fruits frCopy = fr;
frCopy.getFruitsList().add("Apple");
System.out.println(fr.getFruitsList());
frCopy = new Fruits(fr);
frCopy.getFruitsList().add("Banana");
System.out.println(fr.getFruitsList());
System.out.println(frCopy.getFruitsList());
}
}
上述程序的输出是:
[Mango, Orange]
[Mango, Orange, Apple]
[Mango, Orange, Apple]
[Mango, Orange, Apple, Banana]
请注意,使用复制构造函数时,原始对象和其副本彼此无关,对其中一个进行的任何修改都不会反映到另一个上。这就是Java中构造函数的全部内容。
Source:
https://www.digitalocean.com/community/tutorials/constructor-in-java