ตัวอย่างการสร้าง object โดยใช้ Class.newInstance()
จินตนาการว่าคุณได้รับมอบหมายให้สร้างวัตถุโดยใช้การสะท้อน เรามาเริ่มกันเลยดีไหม?
เราจะเริ่มต้นด้วยการเขียนโค้ดสำหรับคลาสที่เราต้องการยกตัวอย่าง:
public class Employee {
private String name;
private String lastName;
private int age;
{
age = -1;
name = "Rob";
surname = "Stark";
}
public Employee(String name, String surname, int age) {
this.name = name;
this.surname = surname;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return lastName;
}
public void setSurname(String surname) {
this.surname = surname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", surname='" + surname + '\'' +
", age=" + age +
'}';
}
}
นี่จะเป็นคลาสของเรา — ที่มีหลายฟิลด์, ตัวสร้างพร้อมพารามิเตอร์, ตัวรับและตัวตั้ง, เมธอด toString()และบล็อกการเริ่มต้น ทีนี้ มาดูส่วนที่สองกัน: การสร้างวัตถุโดยใช้การสะท้อน วิธีแรก ที่เราจะดูจะใช้Class.newInstance()
public class Main {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Employee employee = Employee.class.newInstance();
System.out.println("age is " + employee.getAge());
}
}
ยอดเยี่ยม! เรียกใช้โค้ดของเราและคอยดูอายุที่จะแสดง แต่เราได้รับข้อผิดพลาดเกี่ยวกับตัวสร้างเริ่มต้นที่ขาดหายไป ปรากฎว่าวิธีนี้ช่วยให้เราสร้างวัตถุโดยใช้ตัวสร้างเริ่มต้นเท่านั้น มาเพิ่มตัวสร้างเริ่มต้นสำหรับคลาสของเราและทดสอบรหัสอีกครั้ง
ข้อความผิดพลาด:

รหัสของตัวสร้างใหม่
public Employee() { }
หลังจากเพิ่มตัวสร้าง นี่คือผลลัพธ์:
ยอดเยี่ยม! เราพบว่าวิธีนี้ใช้ได้ผล ทีนี้มาดูใต้ฝากระโปรงกัน เมื่อเปิดเอกสารประกอบ เราพบว่าวิธีการของเราเลิกใช้ แล้ว :

นอกจากนี้ยังสามารถโยนInstantiationExceptionและIllegalAccessExceptionได้ อีกด้วย ดังนั้น เอกสารจึงแนะนำให้เราใช้วิธีอื่นใน การสร้างวัตถุ นั่นคือConstructor.newInstance() มาวิเคราะห์โดยละเอียดว่า คลาส Constructorทำงาน อย่างไร
เมธอด getConstructors และ getDeclaredConstructors
ในการทำงานกับ คลาส Constructorก่อนอื่นเราต้องได้รับอินสแตนซ์ เรามีสองวิธีสำหรับสิ่งนี้: getConstructorsและgetDeclaredConstructors
ตัวแรกส่งคืนอาร์เรย์ของตัวสร้างสาธารณะ และตัวที่สองส่งคืนอาร์เรย์ของตัวสร้างคลาสทั้งหมด
ให้ความเป็นส่วนตัวแก่ชั้นเรียนของเรา หรือสร้างตัวสร้างส่วนตัวเพื่อช่วยสาธิตวิธีการทำงานของเมธอดเหล่านี้
มาเพิ่มตัวสร้างส่วนตัว:
private Employee(String name, String surname) {
this.name = name;
this.lastName = lastName;
}
ดูโค้ด โปรดทราบว่าตัวสร้างตัวใดตัวหนึ่งเป็นแบบส่วนตัว:

มาทดสอบวิธีการของเรากัน:
public class Main {
public static void main(String[] args) {
Class employeeClass = Employee.class;
System.out.println("getConstructors:");
printAllConstructors(employeeClass);
System.out.println("\n" +"getDeclaredConstructors:");
printDeclaredConstructors(employeeClass);
}
static void printDeclaredConstructors(Class<?> c){
for (Constructor<?> constructor : c.getDeclaredConstructors()) {
System.out.println(constructor);
}
}
static void printAllConstructors(Class<?> c){
for (Constructor<?> constructor : c.getConstructors()) {
System.out.println(constructor);
}
}
}
และเราได้ผลลัพธ์นี้:
สาธารณะ com.codegym.Employee(java.lang.String,java.lang.String,int)
public.com.codegym.Employee()
getDeclaredConstructors:
ส่วนตัว com.codegym.Employee(java.lang.String,java.lang .String)
สาธารณะ com.codegym.Employee(java.lang.String,java.lang.String,int)
สาธารณะ com.codegym.Employee()
โอเค นี่คือวิธีที่เราเข้าถึงวัตถุตัวสร้าง ตอนนี้เราสามารถพูดคุยเกี่ยวกับสิ่งที่สามารถทำได้
คลาส java.lang.reflect.Constructor และเมธอดที่สำคัญที่สุด
มาดูวิธีการที่สำคัญที่สุดและวิธีการทำงาน:
วิธี | คำอธิบาย |
---|---|
รับชื่อ () | ส่งคืนชื่อของตัวสร้างนี้เป็นสตริง |
getModifiers() | ส่งคืนตัวแก้ไขการเข้าถึง Java ที่เข้ารหัสเป็นตัวเลข |
getExceptionTypes() | ส่งคืนอาร์เรย์ของวัตถุคลาสที่แสดงถึงประเภทของข้อยกเว้นที่ประกาศโดยตัวสร้าง |
รับพารามิเตอร์ () | ส่งกลับอาร์เรย์ของ วัตถุ พารามิเตอร์ที่เป็นตัวแทนของพารามิเตอร์ทั้งหมด ส่งกลับอาร์เรย์ของความยาว 0 ถ้าตัวสร้างไม่มีพารามิเตอร์ |
getParameterTypes() | ส่งคืนอาร์เรย์ของวัตถุคลาสที่แสดงถึงประเภทพารามิเตอร์ที่เป็นทางการในลำดับการประกาศ |
getGenericParameterTypes() | ส่งคืนอาร์เรย์ของวัตถุ Type ที่แสดงถึงประเภทพารามิเตอร์ที่เป็นทางการในลำดับการประกาศ |
getName() & getModifiers()
มารวมอาร์เรย์ของเราไว้ในรายการเพื่อให้สะดวกในการทำงาน เราจะเขียนเมธอดgetNameและgetModifiers ด้วย:
static List<Constructor<?>> getAllConstructors(Class<?> c) {
return new ArrayList<>(Arrays.asList(c.getDeclaredConstructors()));
}
static List<String> getConstructorNames(List<Constructor<?>> constructors) {
List<String> result = new ArrayList<>();
for (Constructor<?> constructor : constructors) {
result.add(constructor.toString());
}
return result;
}
static List<String> getConstructorModifiers(List<Constructor<?>> constructors) {
List<String> result = new ArrayList<>();
for (Constructor<?> constructor : constructors) {
result.add(Modifier.toString(constructor.getModifiers()));
}
return result;
}
และ วิธีการ หลัก ของเรา ที่เราจะเรียกทุกอย่าง:
public static void main(String[] args) {
Class employeeClass = Employee.class;
var constructors = getAllConstructors(employeeClass);
var constructorNames = getConstructorNames(constructors);
var constructorModifiers = getConstructorModifiers(constructors);
System.out.println("Employee class:");
System.out.println("Constructors :");
System.out.println(constructorNames);
System.out.println("Modifiers :");
System.out.println(constructorModifiers);
}
และตอนนี้เราเห็นข้อมูลทั้งหมดที่เราต้องการแล้ว:
ตัวสร้าง:
[ส่วนตัว com.codegym.Employee(java.lang.String), สาธารณะ
com.codegym.Employee(java.lang.String,java.lang.String,int), สาธารณะ com.codegym.Employee() ]
ตัวดัดแปลง :
[ส่วนตัว, สาธารณะ, สาธารณะ]
getExceptionTypes()
วิธีนี้ช่วยให้เราได้รับอาร์เรย์ของข้อยกเว้นที่ตัวสร้างของเราอาจโยนทิ้ง ลองแก้ไขหนึ่งในคอนสตรัคเตอร์ของเราแล้วเขียนเมธอดใหม่
ที่นี่เราเปลี่ยนตัวสร้างปัจจุบันเล็กน้อย:
private Employee(String name, String surname) throws Exception {
this.name = name;
this.lastName = lastName;
}
และที่นี่เรามีวิธีการรับประเภทข้อยกเว้นและเพิ่มไปยังmain :
static List<Class<?>> getConstructorExceptionTypes(Constructor<?> c) {
return new ArrayList<>(Arrays.asList(c.getExceptionTypes()));
}
var constructorExceptionTypes = getConstructorExceptionTypes(constructors.get(0));
System.out.println("Exception types :");
System.out.println(constructorExceptionTypes);
ด้านบน เราเข้าถึงตัวสร้างแรกในรายการของเรา เราจะหารือเกี่ยวกับวิธีรับตัวสร้างที่เฉพาะเจาะจงในภายหลัง
และดูผลลัพธ์หลังจากที่เราเพิ่มการโยนข้อยกเว้น :
[คลาส java.lang.Exception]
และก่อนที่จะเพิ่มข้อยกเว้น:
[]
ทุกอย่างยอดเยี่ยม แต่เราจะทราบได้อย่างไรว่าตัวสร้างของเราต้องการพารามิเตอร์ใด ลองหาสิ่งนี้เช่นกัน
getParameters() & getParameterTypes() & getGenericParameterTypes()
มาเริ่มกันใหม่โดยการปรับแต่งคอนสตรัคเตอร์ส่วนตัวของเรา ตอนนี้จะมีลักษณะดังนี้:
private Employee(String name, String surname, List<String> list) {
this.name = name;
this.lastName = lastName;
}
และเรามีสามวิธีเพิ่มเติม: getParameters สำหรับ รับลำดับของพารามิเตอร์และประเภทของพารามิเตอร์, getParameterTypesสำหรับรับประเภทพารามิเตอร์ และgetGenericParameterTypesสำหรับรับประเภทที่รวมอยู่ในgenerics
static List<Parameter> getConstructorParameters(Constructor<?> c) {
return new ArrayList<>(Arrays.asList(c.getParameters()));
}
static List<Class<?>> getConstructorParameterTypes(Constructor<?> c) {
return new ArrayList<>(Arrays.asList(c.getParameterTypes()));
}
static List<Type> getConstructorParametersGenerics(Constructor<?> c) {
return new ArrayList<>(Arrays.asList(c.getGenericParameterTypes()));
}
และเราเพิ่มข้อมูลเพิ่มเติมให้กับ วิธี การหลัก ของเราที่ไม่น้อยอยู่แล้ว :
var constructorParameterTypes = getConstructorParameterTypes(constructors.get(0));
var constructorParameters = getConstructorParameters(constructors.get(0));
var constructorParametersGenerics = getConstructorParametersGenerics(constructors.get(0));
System.out.println("Constructor parameters :");
System.out.println(constructorParameters);
System.out.println("Parameter types :");
System.out.println(constructorParameterTypes);
System.out.println("Constructor parameter types :");
System.out.println(constructorParametersGenerics);
เมื่อดูที่ผลลัพธ์ เราจะเห็นข้อมูลโดยละเอียดเกี่ยวกับพารามิเตอร์ของตัวสร้างของเรา:
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
ประเภทพารามิเตอร์ :
[คลาส java.lang.String, คลาส java.lang.String, อินเทอร์เฟซ java.util.List]
ประเภทพารามิเตอร์ตัวสร้าง:
[คลาส java.lang.String, คลาส java.lang.String, java.util.List<java.lang.String>]
สิ่งนี้แสดงให้เห็นถึงความแตกต่างระหว่างแต่ละวิธีอย่างชัดเจน เราเห็นว่าเรามีตัวเลือกแยกต่างหากเพื่อรับข้อมูลเกี่ยวกับประเภทพารามิเตอร์ ประเภทที่รวมไว้ และทุกอย่างโดยทั่วไป สุดยอด! เมื่อเราคุ้นเคยกับ คลาส Constructorแล้ว เราสามารถกลับไปที่หัวข้อหลักของบทความของเรา นั่นคือการสร้างวัตถุ
การสร้างวัตถุโดยใช้ Constructor.newInstance()
วิธีที่สองในการสร้างวัตถุคือการเรียก เมธอด newInstanceบนตัวสร้าง ลองมาดูตัวอย่างการทำงานและดูว่าเราจะได้ตัวสร้างเฉพาะได้อย่างไร
หากคุณต้องการรับคอนสตรัคเตอร์เดียว คุณควรใช้ เมธอด getConstructor (อย่าสับสนกับgetConstructorsซึ่งจะคืนค่าอาร์เรย์ของคอนสตรัคเตอร์ทั้งหมด) เมธอดgetConstructorส่งคืนตัวสร้างเริ่มต้น
public static void main(String[] args) throws NoSuchMethodException {
Class employeeClass = Employee.class;
Constructor<?> employeeConstructor = employeeClass.getConstructor();
System.out.println(employeeConstructor);
}
และถ้าเราต้องการรับคอนสตรัคเตอร์เฉพาะ เราต้องส่งพารามิเตอร์ประเภทคอนสตรัคเตอร์ไปยังเมธอดนี้
อย่าลืมว่าเราสามารถรับตัวสร้างส่วนตัวของเราได้โดยใช้เมธอดgetDeclaredConstructor
Constructor<?> employeeConstructor2 = employeeClass.getDeclaredConstructor(String.class, String.class, List.class);
System.out.println(employeeConstructor2);
นี่คือวิธีที่เราจะได้รับตัวสร้างเฉพาะ ตอนนี้ลองสร้างวัตถุโดยใช้ตัวสร้างส่วนตัวและสาธารณะ
ตัวสร้างสาธารณะ:
Class employeeClass = Employee.class;
Constructor<?> employeeConstructor = employeeClass.getConstructor(String.class, String.class, int.class);
System.out.println(employeeConstructor);
Employee newInstance = (Employee) employeeConstructor.newInstance("Rob", "Stark", 10);
System.out.println(newInstance);
ผลลัพธ์คือวัตถุที่เราสามารถทำงานได้:
พนักงาน{ชื่อ='Rob' นามสกุล='Stark' อายุ=10}
ทุกอย่างใช้งานได้ดี! ตอนนี้เราจะลองกับตัวสร้างส่วนตัว:
Constructor<?> declaredConstructor = employeeClass.getDeclaredConstructor(String.class, String.class, List.class);
System.out.println(declaredConstructor);
Employee newInstance2 = (Employee) declaredConstructor.newInstance("Rob", "Stark", new ArrayList<>());
System.out.printf(newInstance2.toString());
ผลลัพธ์คือข้อผิดพลาดเกี่ยวกับความเป็นส่วนตัวของตัวสร้างของเรา:

Java ไม่สามารถสร้างวัตถุโดยใช้ตัวสร้างนี้ แต่จริง ๆ แล้วมีบางสิ่งที่มหัศจรรย์ที่เราสามารถทำได้ในเมธอดหลัก เราสามารถเข้าถึงระดับคอนสตรัคเตอร์ของเราได้ ทำให้สามารถสร้างอ็อบเจกต์ของคลาสของเราได้:
declaredConstructor.setAccessible(true);
ผลลัพธ์ของการสร้างวัตถุ
พนักงาน{name='Rob', นามสกุล='Stark', อายุ=-1}
เราไม่ได้กำหนดอายุในตัวสร้างของเรา ดังนั้นมันจึงยังคงเหมือนเดิมกับตอนที่เริ่มต้น
ยอดเยี่ยม มาสรุปกันเถอะ!
ประโยชน์ของการสร้างวัตถุโดยใช้ Constructor.newInstance()
ทั้งสองวิธีใช้ชื่อเดียวกัน แต่มีความแตกต่างระหว่างพวกเขา:
Class.newInstance() | Constructor.newInstance() |
---|---|
สามารถเรียกตัวสร้างno-arg เท่านั้น | สามารถเรียกใช้ตัวสร้างใดก็ได้โดยไม่คำนึงถึงจำนวนพารามิเตอร์ |
กำหนดให้มองเห็นตัวสร้างได้ | นอกจากนี้ยังสามารถเรียกตัวสร้างส่วนตัวได้ในบางกรณี |
ส่งข้อยกเว้นใด ๆ (ตรวจสอบหรือไม่) ที่ประกาศโดยตัวสร้าง | ล้อมรอบข้อยกเว้นที่เกิดขึ้นด้วยInvocationTargetException เสมอ |
ด้วยเหตุผลเหล่านี้Constructor.newInstance()จึงเป็นที่นิยมมากกว่าClass.newInstance()และเป็นวิธีที่ใช้โดยเฟรมเวิร์กและ API ต่างๆ เช่น Spring, Guava, Zookeeper, Jackson, Servlet เป็นต้น
GO TO FULL VERSION