
Constructor trong Java
Bất cứ khi nào chúng ta sử dụng từ khóa new để tạo một thể hiện của một lớp, constructor được gọi và đối tượng của lớp được trả về. Vì constructor chỉ có thể trả về đối tượng cho lớp, nó được thực hiện ngầm bởi Java runtime và chúng ta không nên thêm kiểu trả về cho nó.
Nếu chúng ta thêm một kiểu trả về vào một phương thức Constructor, thì nó sẽ trở thành một phương thức của lớp. Đây là cách Java runtime phân biệt giữa phương thức bình thường và phương thức Constructor. Giả sử chúng ta có mã sau trong lớp Employee.
public Employee() {
System.out.println("Employee
Constructor");
}
public Employee Employee() {
System.out.println("Employee
Method");
return new
Employee();
}
Ở đây cái đầu tiên là một constructor, hãy lưu ý rằng không có kiểu trả về và không có câu lệnh trả về. Phương thức thứ hai là một phương thức bình thường, trong đó chúng ta lại gọi phương thức khởi tạo đầu tiên để lấy cá thể Employee và trả về nó. Bạn không nên đặt tên phương thức giống với tên lớp vì nó tạo ra sự nhầm lẫn.
Có ba loại phương thức khởi tạo trong java.
👉 Default Constructor
Hãy xem xét tất cả các loại phương thức khởi tạo này với các chương trình ví dụ.
Default Constructor trong Java
Không bắt buộc phải luôn cung cấp một triển khai phương thức Constructor trong mã lớp. Nếu chúng tôi không cung cấp một constructor, thì java sẽ cung cấp triển khai Default constructor để chúng tôi sử dụng. Hãy xem xét một chương trình đơn giản mà Default constructor đang được sử dụng vì chúng ta sẽ không xác định rõ ràng một constructor.
package com.hn.constructor;
public class Data {
public static void
main(String[] args) {
Data d = new
Data();
}
}
👉 Default constructor chỉ có vai trò là khởi tạo đối tượng và trả nó về mã gọi.
👉 Default constructor luôn không có đối số và chỉ được cung cấp bởi trình biên dịch java khi không có constructor nào được xác định.
👉 Hầu hết thời gian chúng ta đều ổn với chính phương thức Default constructor vì các thuộc tính khác có thể được truy cập và khởi tạo thông qua các phương thức getter setter.
No-Args Constructor
Constructor không có bất kỳ đối số nào được gọi là no-args constructor. Nó giống như ghi đè default constructor và được sử dụng để thực hiện một số công việc tiền khởi tạo như kiểm tra tài nguyên, kết nối mạng, ghi nhật ký, v.v. Hãy xem nhanh no-args constructor trong java.
package com.hn.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();
}
}
Bây giờ khi chúng ta sẽ gọi new Data(), thì phương thức no-args constructor của chúng ta sẽ được gọi. Hình ảnh dưới đây minh họa hành vi này, hãy kiểm tra đầu ra giao diện điều khiển của chương trình.
Parameterized Controller
Constructor với các đối số được gọi là Parameterized Constructor( constructor tham số hóa). Hãy xem ví dụ về Parameterized Constructor trong java.
package com.hn.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());
}
}
Constructor overloading trong Java
Khi chúng ta có nhiều hơn một constructor, thì đó là constructor overloading trong java. Hãy xem một ví dụ về phương thức Constructor Overloading trong chương trình java.
package com.hn.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("HN", 25);
System.out.println(d);
}
}
Lưu ý rằng chúng ta không thể sử dụng từ khóa abstract, final, tĩnh và synchronized với các constructor. Tuy nhiên, chúng ta có thể sử dụng access modifiers để điều khiển việc khởi tạo đối tượng lớp. Sử dụng public và default truy cập vẫn ổn, nhưng việc đặt một phương thức khởi tạo ở chế độ private thì có ích lợi gì? Trong trường hợp đó, bất kỳ lớp nào khác sẽ không thể tạo cá thể của lớp.
Một phương thức khởi tạo được đặt ở chế độ riêng tư trong trường hợp chúng ta muốn triển khai singleton design pattern . Vì java tự động cung cấp default constructor, chúng ta phải tạo một cách rõ ràng một constructor và giữ nó ở chế độ private. Các lớp máy khách được cung cấp một phương thức static tiện ích để lấy thể hiện của lớp. Dưới đây là một ví dụ về phương thức private constructor cho lớp Data.
// private constructor
private Data() {
//empty constructor for
singleton pattern implementation
//can have code to be used inside the
getInstance() method of class
}
Khi một constructor gọi một constructor khác của cùng một lớp, nó được gọi là constructor chaining. Chúng ta phải sử dụng từ khóa this để gọi một phương thức constructor khác của lớp. Đôi khi nó được sử dụng để đặt một số giá trị mặc định của các biến lớp.
Lưu ý rằng một lệnh gọi constructor khác phải là câu lệnh đầu tiên trong khối mã. Ngoài ra, không nên gọi đệ quy sẽ tạo ra một vòng lặp vô hạn. Hãy xem một ví dụ về constructor chaining trong chương trình java.
package com.hn.constructor;
public class Employee {
private int id;
private String name;
public Employee() {
this("Nhan vien", 999);
System.out.println("Default
Employee Created");
}
public Employee(int i) {
this("Nhan vien", 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("HN", 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;
}
}
Tôi đã ghi đè phương thức toString() để in một số thông tin hữu ích về đối tượng Employee.
Chú ý cách một constructor đang được gọi từ một constructor khác, đó được gọi là quá trình constructor chaining.
Đôi khi một lớp được kế thừa từ lớp cha, trong trường hợp đó, nếu chúng ta phải gọi constructor lớp cha thì chúng ta có thể thực hiện việc đó bằng cách sử dụng từ khóa super. Hãy xem một ví dụ về việc sử dụng super constructor.
Lưu ý rằng lệnh gọi super constructor phải là câu lệnh đầu tiên trong constructor lớp con. Ngoài ra, khi khởi tạo phương thức constructor lớp con, java đầu tiên khởi tạo lớp super và sau đó là lớp con. Vì vậy, nếu phương thức super constructor không được gọi rõ ràng thì phương thức default hoặc no-args constructor được gọi bằng java runtime. Chúng ta hãy hiểu những khái niệm này thông qua một số chương trình ví dụ.
Giả sử chúng ta có hai lớp như dưới đây.
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);
}
}
Bây giờ nếu chúng ta tạo một đối tượng Student như bên dưới:
Student st = new Student();
Đầu ra của đoạn mã trên sẽ là:
Person Created
Student Created
Vì vậy, lời gọi đã chuyển đến constructor no-args của lớp Student vì không có lệnh gọi super nào trong câu lệnh đầu tiên mà hàm no-args hoặc default constructor của lớp Person được gọi. Do đó đầu ra. Điều gì sẽ xảy ra nếu chúng ta đang sử dụng parameterized constructor tham số hóa của lớp Student Student st = new Student(34, "Pankaj");, kết quả đầu ra sẽ là:
Person Created with Age = 34
Student Created with name = Pankaj
Ở đây đầu ra là rõ ràng vì chúng ta đang gọi super constructor một cách rõ ràng, vì vậy Java không cần phải thực hiện thêm bất kỳ công việc nào từ phía của chúng.
Java Copy Constructor
Phương thức Java Copy Constructor lấy đối tượng của cùng một lớp làm đối số và tạo một bản sao của nó. Đôi khi chúng ta cần một bản sao của một đối tượng khác để thực hiện một số xử lý. Chúng ta có thể làm điều này bằng những cách sau:
👉 thực hiện cloning
👉 cung cấp một phương thức tiện ích cho deep copy của đối tượng.
👉 Có một copy constructor
Bây giờ chúng ta hãy xem cách viết một copy constructor. Giả sử chúng ta có một lớp Fruits như dưới đây.
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;
}
}
Lưu ý rằng Fruits(Fruits fr) đang thực hiện một deep copy để trả lại bản sao của đối tượng. Chúng ta hãy xem xét một chương trình thử nghiệm để hiểu tại sao tốt hơn nên có copy constructor để sao chép một đối tượng.
public class ConstructorTest {
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 fr = fr;
fr.getFruitsList().add("Apple");
System.out.println(fr.getFruitsList());
fr = new Fruits(fr);
fr.getFruitsList().add("Banana");
System.out.println(fr.getFruitsList());
System.out.println(fr.getFruitsList());
}
}
Kết quả của chương trình trên là:
Lưu ý rằng khi sử dụng copy constructor, cả đối tượng gốc và bản sao của nó đều không liên quan đến nhau và bất kỳ sửa đổi nào ở một trong số chúng sẽ không phản ánh thành đối tượng khác.
Đó là tất cả đối với constructor trong java.
0 Nhận xét