Java中的构造函数用于创建类的实例。构造函数几乎与方法相似,除了两件事-它的名称与类名相同,并且它没有返回类型。有时构造函数也被称为初始化对象的特殊方法。
Java中的构造函数
每当使用new
关键字创建类的实例时,都会调用构造函数并返回类的对象。因为构造函数只能将对象返回给类,所以它是由Java运行时隐式完成的,我们不应该向它添加返回类型。如果我们将返回类型添加到构造函数中,那么它将成为类的方法。这是Java运行时区分普通方法和构造函数的方式。让我们假设在Employee
类中有以下代码。
1public Employee() {
2 System.out.println("Employee Constructor");
3}
4
5public Employee Employee() {
6 System.out.println("Employee Method");
7 return new Employee();
8}
这里第一个是构造函数,注意没有返回类型和返回语句。第二个是一个普通的方法,我们再次调用第一个构造函数来获取Employee实例并返回它。建议不要使用与类名相同的方法名,因为这样会造成混淆。
Java中的构造函数类型
Java中有三种类型的构造函数。
1.默认构造函数 2.无参数构造函数 3.参数化构造函数
让我们通过示例程序来研究所有这些构造函数类型。
Java中的默认构造函数
不需要总是在类代码中提供构造函数实现。如果我们不提供构造函数,那么Java将提供默认的构造函数实现供我们使用。让我们来看一个简单的程序,其中使用了默认构造函数,因为我们不会显式定义构造函数。
1package com.journaldev.constructor;
2
3public class Data {
4
5 public static void main(String[] args) {
6 Data d = new Data();
7 }
8}
1.默认的构造函数唯一角色是初始化对象并将其返回给调用代码。 2.默认构造函数始终不带参数,只有在没有定义现有构造函数的情况下才由Java编译器提供。 3.大多数情况下,我们可以使用默认构造函数本身,因为可以通过getter setter方法访问和初始化其他属性。
无参数构造函数
不带任何参数的构造函数称为无参数构造函数。它类似于覆盖默认构造函数,用于执行一些预初始化工作,如检查资源、网络连接、日志记录等。让我们快速了解一下Java中的无参数构造函数。
1package com.journaldev.constructor;
2
3public class Data {
4 //no-args constructor
5 public Data() {
6 System.out.println("No-Args Constructor");
7 }
8 public static void main(String[] args) {
9 Data d = new Data();
10 }
11}
现在,当我们调用new data()
时,我们的无参数构造函数将被调用。下图显示了此行为,请检查程序的控制台输出。中没有args构造函数
参数化构造函数
带参数的构造函数称为参数构造函数。让我们看一看Java中的参数化构造函数的例子。
1package com.journaldev.constructor;
2
3public class Data {
4
5 private String name;
6
7 public Data(String n) {
8 System.out.println("Parameterized Constructor");
9 this.name = n;
10 }
11
12 public String getName() {
13 return name;
14 }
15
16 public static void main(String[] args) {
17 Data d = new Data("Java");
18 System.out.println(d.getName());
19 }
20
21}
的JAVA类构造函数
Java中的构造函数重载
当我们有多个构造函数时,这就是Java中的构造函数重载。让我们来看一个Java程序中构造函数重载的例子。
1package com.journaldev.constructor;
2
3public class Data {
4
5 private String name;
6 private int id;
7
8 //no-args constructor
9 public Data() {
10 this.name = "Default Name";
11 }
12 //one parameter constructor
13 public Data(String n) {
14 this.name = n;
15 }
16 //two parameter constructor
17 public Data(String n, int i) {
18 this.name = n;
19 this.id = i;
20 }
21
22 public String getName() {
23 return name;
24 }
25
26 public int getId() {
27 return id;
28 }
29
30 @Override
31 public String toString() {
32 return "ID="+id+", Name="+name;
33 }
34 public static void main(String[] args) {
35 Data d = new Data();
36 System.out.println(d);
37
38 d = new Data("Java");
39 System.out.println(d);
40
41 d = new Data("Pankaj", 25);
42 System.out.println(d);
43
44 }
45
46}
Java中的私有构造函数
请注意,我们不能将abstract,最终关键字、static关键字和同步关键字与构造函数一起使用。但是,我们可以使用访问修饰符来控制类对象的实例化。使用Public
和default
访问仍然可以,但是将构造函数设为私有有什么用呢?在这种情况下,任何其他类都不能创建类的实例。如果我们想要实现Singleton Design pattern.],则构造函数被设置为私有的由于Java自动提供默认构造函数,因此我们必须显式创建一个构造函数并将其保持为私有。客户端类提供了一个实用程序静态方法来获取类的实例。下面给出了一个Data
类的私有构造函数示例。
1// private constructor
2private Data() {
3 //empty constructor for singleton pattern implementation
4 //can have code to be used inside the getInstance() method of class
5}
Java中的构造函数链接
当一个构造函数调用同一类的另一个构造函数时,称为构造函数链。我们必须使用this
关键字来调用该类的另一个构造函数。有时它被用来设置类变量的一些缺省值。请注意,另一个构造函数调用应该是代码块中的第一个语句。此外,不应该有会创建无限循环的递归调用。让我们来看一个Java程序中构造函数链接的例子。
1package com.journaldev.constructor;
2
3public class Employee {
4
5 private int id;
6 private String name;
7
8 public Employee() {
9 this("John Doe", 999);
10 System.out.println("Default Employee Created");
11 }
12
13 public Employee(int i) {
14 this("John Doe", i);
15 System.out.println("Employee Created with Default Name");
16 }
17 public Employee(String s, int i) {
18 this.id = i;
19 this.name = s;
20 System.out.println("Employee Created");
21 }
22 public static void main(String[] args) {
23
24 Employee emp = new Employee();
25 System.out.println(emp);
26 Employee emp1 = new Employee(10);
27 System.out.println(emp1);
28 Employee emp2 = new Employee("Pankaj", 20);
29 System.out.println(emp2);
30 }
31
32 @Override
33 public String toString() {
34 return "ID = "+id+", Name = "+name;
35 }
36 public int getId() {
37 return id;
38 }
39
40 public void setId(int id) {
41 this.id = id;
42 }
43
44 public String getName() {
45 return name;
46 }
47
48 public void setName(String name) {
49 this.name = name;
50 }
51
52}
我重写了toString()方法以打印有关Employee对象的一些有用信息。以下是上述程序产生的输出。
1Employee Created
2Default Employee Created
3ID = 999, Name = John Doe
4Employee Created
5Employee Created with Default Name
6ID = 10, Name = John Doe
7Employee Created
8ID = 20, Name = Pankaj
注意一个构造函数是如何从另一个构造函数调用的,这称为构造函数链接过程。
Java超级构造函数
有时一个类是从一个超类继承而来的,在这种情况下,如果我们必须调用超类构造函数,那么我们可以使用Super
关键字来实现。让我们来看一个使用超类构造函数的示例。请注意,超级构造函数调用应该是子类构造函数中的第一条语句。同样,在实例化子类构造函数时,Java首先初始化超类,然后初始化子类。因此,如果没有显式调用超类构造函数,则Java运行时将调用默认的或无参数的构造函数。让我们通过一些示例程序来理解这些概念。让我们假设我们有两个类,如下所示。
1package com.journaldev.constructor;
2
3public class Person {
4
5 private int age;
6
7 public Person() {
8 System.out.println("Person Created");
9 }
10
11 public Person(int i) {
12 this.age = i;
13 System.out.println("Person Created with Age = " + i);
14 }
15
16}
1package com.journaldev.constructor;
2
3public class Student extends Person {
4
5 private String name;
6
7 public Student() {
8 System.out.println("Student Created");
9 }
10
11 public Student(int i, String n) {
12 super(i); // super class constructor called
13 this.name = n;
14 System.out.println("Student Created with name = " + n);
15 }
16
17}
现在,如果我们创建一个Student对象,如下所示;
1Student st = new Student();
产出会是多少呢?上述代码的输出将为:
1Person Created
2Student Created
因此,调用转到Student类的no-args构造函数,因为在第一个语句中没有超级调用,即调用Person类的no-args或默认构造函数。因此,输出。如果我们使用Student类的参数化构造函数作为Student st = new Student(34,);
,输出将是:
1Person Created with Age = 34
2Student Created with name = Pankaj
这里的输出很清楚,因为我们显式地调用了超类构造函数,所以Java不需要从它们那端做任何额外的工作。
Java Copy构造函数
Java复制构造函数将同一类的对象作为参数,并创建其副本。有时,我们需要另一个对象的副本来进行一些处理。我们可以通过以下方式做到这一点:
1.实施cloning 2.提供了copyof the Object]的实用方法。 3.拥有复制构造函数
现在,让我们看看如何编写复制构造函数。假设我们有一个类‘Fruits’,如下所示。
1package com.journaldev.constructor;
2
3import java.util.ArrayList;
4import java.util.List;
5
6public class Fruits {
7
8 private List<String> fruitsList;
9
10 public List<String> getFruitsList() {
11 return fruitsList;
12 }
13
14 public void setFruitsList(List<String> fruitsList) {
15 this.fruitsList = fruitsList;
16 }
17
18 public Fruits(List<String> fl) {
19 this.fruitsList = fl;
20 }
21
22 public Fruits(Fruits fr) {
23 List<String> fl = new ArrayList<>();
24 for (String f : fr.getFruitsList()) {
25 fl.add(f);
26 }
27 this.fruitsList = fl;
28 }
29}
请注意,Fruits(Fruits fr)
正在执行深度复制以返回对象的副本。让我们看看一个测试程序,以理解为什么使用复制构造函数来复制对象更好。
1package com.journaldev.constructor;
2
3import java.util.ArrayList;
4import java.util.List;
5
6public class CopyConstructorTest {
7
8 public static void main(String[] args) {
9 List<String> fl = new ArrayList<>();
10 fl.add("Mango");
11 fl.add("Orange");
12
13 Fruits fr = new Fruits(fl);
14
15 System.out.println(fr.getFruitsList());
16
17 Fruits frCopy = fr;
18 frCopy.getFruitsList().add("Apple");
19
20 System.out.println(fr.getFruitsList());
21
22 frCopy = new Fruits(fr);
23 frCopy.getFruitsList().add("Banana");
24 System.out.println(fr.getFruitsList());
25 System.out.println(frCopy.getFruitsList());
26
27 }
28
29}
上述程序的输出为:
1[Mango, Orange]
2[Mango, Orange, Apple]
3[Mango, Orange, Apple]
4[Mango, Orange, Apple, Banana]
请注意,当使用复制构造函数时,原始对象及其副本彼此无关,其中一个对象中的任何修改都不会反映到另一个对象中。这就是Java中的构造函数。