Java 中的构造函数
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 {
//no-args constructor
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;
//no-args constructor
public Data() {
this.name = "Default Name";
}
//one parameter constructor
public Data(String n) {
this.name = n;
}
//two parameter constructor
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 中的私有构造函数
Note that we can’t use abstract, final, static and synchronized keywords with constructors. However we can use access modifiers to control the instantiation of class object. Using public
and default
access is still fine, but what is the use of making a constructor private? In that case any other class won’t be able to create the instance of the class. Well, a constructor is made private in case we want to implement singleton design pattern. Since java automatically provides default constructor, we have to explicitly create a constructor and keep it private. Client classes are provided with a utility static method to get the instance of the class. An example of private constructor for Data
class is given below.
// private constructor
private Data() {
//empty constructor for singleton pattern implementation
//can have code to be used inside the getInstance() method of class
}
Constructor Chaining in Java
When a constructor calls another constructor of the same class, it’s called constructor chaining. We have to use this
keyword to call another constructor of the class. Sometimes it’s used to set some default values of the class variables. Note that another constructor call should be the first statement in the code block. Also, there should not be recursive calls that will create an infinite loop. Let’s see an example of constructor chaining in java program.
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
Notice how one constructor is being called from another constructor, that’s called constructor chaining process.
Java Super Constructor
Sometimes a class is inherited from a superclass, in that case, if we have to call superclass constructor then we can do it using super
keyword. Let’s have a look at an example of using super class constructor. Note that super constructor call should be the first statement in the child class constructor. Also when instantiating child class constructor, java first initializes the super class and then child class. So if the super class constructor is not explicitly called then default or no-args constructor is called by java runtime. Let’s understand these concepts through some example program. Let’s assume we have two classes like below.
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);
}
}
Now if we create a Student object like below;
Student st = new Student();
What will be the output produced? The output of the above code will be:
Person Created
Student Created
So the call went to the no-args constructor of Student class since there was no super call in the first statement the no-args or default constructor of Person class is called. Hence the output. What if we are using parameterized constructor of Student class as Student st = new Student(34, "Pankaj");
, the output will be:
Person Created with Age = 34
Student Created with name = Pankaj
Here the output is clear because we are explicitly calling superclass constructor, so Java doesn’t need to do any extra work from their side.
Java Copy Constructor
Java copy constructor takes the object of the same class as an argument and creates a copy of it. Sometimes we need a copy of another object to do some processing. We can do this by following ways:
Now let’s see how to write a copy constructor. Suppose we have a class Fruits
like below.
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;
}
}
Notice that Fruits(Fruits fr)
is performing a deep copy to return the copy of the object. Let’s look at a test program to understand why it’s better to have copy constructor to copy an object.
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());
}
}
The output of the above program is:
[Mango, Orange]
[Mango, Orange, Apple]
[Mango, Orange, Apple]
[Mango, Orange, Apple, Banana]
Notice that when copy constructor is used, both the original object and its copy are unrelated to each other and any modifications in one of them will not reflect into other. That’s all for the constructor in java.