![translation](https://cdn.durumis.com/common/trans.png)
นี่คือโพสต์ที่แปลด้วย AI
[Java] แนวคิด Reflection และวิธีการใช้งาน
- ภาษาที่เขียน: ภาษาเกาหลี
- •
-
ประเทศอ้างอิง: ทุกประเทศ
- •
- เทคโนโลยีสารสนเทศ
เลือกภาษา
สรุปโดย AI ของ durumis
- Reflection เป็น API ที่ช่วยให้เข้าถึงข้อมูลเกี่ยวกับคลาสในขณะที่โปรแกรมกำลังทำงานและสามารถจัดการกับคลาสนั้นได้ตามต้องการ
- Reflection ช่วยให้สามารถสร้างอินสแตนซ์ของคลาสและเข้าถึงฟิลด์และเมธอดโดยไม่คำนึงถึงตัวควบคุมการเข้าถึง ซึ่งมีประโยชน์อย่างยิ่งในการจัดการการพึ่งพาแบบไดนามิกในขั้นตอนการพัฒนาขนาดใหญ่ เช่น ในกรอบงาน
- อย่างไรก็ตาม ควรใช้ Reflection เฉพาะในกรณีที่จำเป็นเท่านั้น เนื่องจากอาจส่งผลต่อการปิดบังข้อมูลและทำให้ประสิทธิภาพลดลง
การสะท้อนคืออะไร?
การสะท้อนคือ API ที่ช่วยให้คุณสามารถสร้างอินสแตนซ์ของคลาสที่ต้องการได้โดยใช้วัตถุประเภทคลาสที่โหลดในพื้นที่ฮีป และช่วยให้คุณสามารถเข้าถึงฟิลด์และเมธอดของอินสแตนซ์ได้โดยไม่คำนึงถึงตัวควบคุมการเข้าถึง
ที่นี่ คลาสที่โหลดหมายถึง JVM คลาสโหลดเดอร์ได้ทำการโหลดไฟล์คลาสแล้ว และสร้างวัตถุประเภทคลาสที่เก็บข้อมูลของคลาสนั้นไว้ในพื้นที่ฮีป โปรดทราบว่าสิ่งนี้แตกต่างจากวัตถุที่สร้างขึ้นโดยใช้คีย์เวิร์ด new หากคุณไม่เข้าใจวัตถุประเภทคลาส คุณสามารถตรวจสอบเอกสาร JDK สำหรับวัตถุ java.lang.class ได้
วิธีการใช้งาน
ก่อนที่จะใช้การสะท้อน คุณต้องรับวัตถุประเภทคลาสที่โหลดในพื้นที่ฮีป มี 3 วิธี
- รับจากคลาส.class
- รับจากอินสแตนซ์.getClass()
- รับจาก Class.forName("ชื่อคลาส")
public class Member {
private String name;
protected int age;
public String hobby;
public Member() {
}
public Member(String name, int age, String hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}
public void speak(String message) {
System.out.println(message);
}
private void secret() {
System.out.println("รหัสผ่านคือ 1234");
}
@Override
public String toString() {
return "Member{" +
"name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
'}';
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
Class memberClass = Member.class;
System.out.println(System.identityHashCode(memberClass));
Member member = new Member("เจย์ออน", 23, "พัฒนา DARA");
Class extends Member> memberClass2 = member.getClass();
System.out.println(System.identityHashCode(memberClass2));
Class> memberClass3 = Class.forName("{ชื่อแพ็คเกจ}.Member");
System.out.println(System.identityHashCode(memberClass3));
}
}
// ผลลัพธ์การดำเนินการ
1740000325
1740000325
คุณสามารถเห็นได้ว่าอินสแตนซ์ประเภทคลาสที่ได้รับจาก 3 วิธีนั้นเหมือนกัน ไม่ว่าคุณจะใช้วิธีใด ค่าแฮชจะเหมือนกัน ดังนั้นคุณสามารถเลือกใช้ตามสถานการณ์ได้
ตอนนี้ คุณสามารถสร้างอินสแตนซ์ของคลาสนั้นได้โดยใช้ประเภทคลาสที่ได้รับ และสามารถเข้าถึงฟิลด์และเมธอดของอินสแตนซ์ได้โดยไม่คำนึงถึงตัวควบคุมการเข้าถึง ก่อนอื่น ลองสร้างอินสแตนซ์ของคลาสนั้น
public class Main {
public static void main(String[] args) throws Exception {
// พิมพ์คอนสตรัคเตอร์ทั้งหมดของ Member
Member member = new Member();
Class extends Member> memberClass = member.getClass();
Arrays.stream(memberClass.getConstructors()).forEach(System.out::println);
// สร้างอินสแตนซ์โดยใช้คอนสตรัคเตอร์เริ่มต้นของ Member
Constructor extends Member> constructor = memberClass.getConstructor();
Member member2 = constructor.newInstance();
System.out.println("member2 = " + member2);
// สร้างอินสแตนซ์โดยใช้คอนสตรัคเตอร์อื่นของ Member
Constructor extends Member> fullConstructor =
memberClass.getConstructor(String.class, int.class, String.class);
Member member3 = fullConstructor.newInstance("เจย์ออน", 23, "พัฒนา DARA");
System.out.println("member3 = " + member3);
}
}
// ผลลัพธ์การดำเนินการ
public Member()
public Member(java.lang.String,int,java.lang.String)
member2 = Member{name='null', age=0, hobby='null'}
คุณสามารถรับคอนสตรัคเตอร์ได้โดยใช้ getConstructor() และสร้างอินสแตนซ์ของ Member ได้โดยใช้ newInstance()
สุดท้าย ลองเข้าถึงฟิลด์และเมธอดของอินสแตนซ์โดยไม่คำนึงถึงตัวควบคุมการเข้าถึง
public class Main {
public static void main(String[] args) throws Exception {
Member member = new Member("เจย์ออน", 23, "พัฒนา DARA");
Class extends Member> memberClass = member.getClass();
// เข้าถึงฟิลด์
Field[] fields = memberClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.get(member));
}
fields[0].set(member, "เจย์ออน2");
System.out.println(member);
// เข้าถึงเมธอด
Method speakMethod = memberClass.getDeclaredMethod("speak", String.class);
speakMethod.invoke(member, "ทดสอบการสะท้อน");
Method secretMethod = memberClass.getDeclaredMethod("secret");
secretMethod.setAccessible(true);
secretMethod.invoke(member);
}
}
// ผลลัพธ์การดำเนินการ
เจย์ออน
23
พัฒนา DARA
Member{name='เจย์ออน2', age=23, hobby='พัฒนา DARA'}
ทดสอบการสะท้อน
คุณสามารถรับตัวแปรอินสแตนซ์ทั้งหมดของคลาสได้โดยใช้ getDeclaredFileds() และสามารถรับค่าฟิลด์ได้โดยใช้ get() และสามารถแก้ไขค่าฟิลด์ได้โดยใช้ set() โปรดทราบว่าเมื่อเข้าถึงฟิลด์ที่มีตัวควบคุมการเข้าถึง private คุณต้องส่ง true เป็นอาร์กิวเมนต์ไปยัง setAccessible()
คุณยังสามารถรับเมธอดได้โดยใช้ getDeclaredMethod() ด้วย ในกรณีนี้ คุณต้องส่งชื่อเมธอดและประเภทของพารามิเตอร์เป็นอาร์กิวเมนต์ เช่นเดียวกัน คุณต้องตั้งค่าอาร์กิวเมนต์ของ setAccessible() เป็น true เมื่อเข้าถึงเมธอดที่มีตัวควบคุมการเข้าถึง private สุดท้าย คุณสามารถเรียกใช้เมธอดที่ได้รับจาก API การสะท้อนโดยใช้เมธอด invoke()
ข้อดีข้อเสีย
- ข้อดี
- มีการยืดหยุ่นในการสร้างอินสแตนซ์ของคลาสในขณะรันไทม์ เข้าถึงฟิลด์และเมธอดโดยไม่คำนึงถึงตัวควบคุมการเข้าถึง และดำเนินการที่จำเป็น
- ข้อเสีย
- ละเมิดการห่อหุ้ม
- ไม่สามารถตรวจสอบประเภทที่เกี่ยวข้องได้ในขณะคอมไพล์ เนื่องจากอินสแตนซ์ถูกสร้างขึ้นในขณะรันไทม์
- ยากที่จะติดตามการไหลของการดำเนินการที่เฉพาะเจาะจง เนื่องจากอินสแตนซ์ถูกสร้างขึ้นในขณะรันไทม์
- ประสิทธิภาพของการเข้าถึงฟิลด์และเมธอดโดยใช้การสะท้อนนั้นช้ากว่าการเข้าถึงโดยตรง (ไม่ได้ช้าในทุกสถานการณ์)
เหตุผลในการใช้งาน
คุณสามารถเข้าถึงข้อมูลคลาสและปรับแต่งคลาสได้ตามต้องการในขณะรันไทม์ผ่าน API การสะท้อน แม้แต่ฟิลด์หรือเมธอดที่ประกาศโดยใช้ตัวควบคุมการเข้าถึง private ก็สามารถปรับแต่งได้ ดูเหมือนว่าจะเป็นเทคนิคที่ไม่ควรใช้เนื่องจากละเมิดการห่อหุ้มซึ่งเป็นสิ่งสำคัญในแบบจำลองการออกแบบเชิงวัตถุ
ในขั้นตอนคอนโซลขนาดเล็ก นักพัฒนาสามารถระบุวัตถุและการพึ่งพาที่ใช้ในโปรแกรมได้ทั้งหมดในขณะคอมไพล์ อย่างไรก็ตาม ในขั้นตอนการพัฒนาขนาดใหญ่ เช่น เฟรมเวิร์ก การระบุวัตถุและการพึ่งพาจำนวนมากนั้นยาก ในกรณีนี้ การใช้การสะท้อนช่วยให้คุณสามารถสร้างคลาสแบบไดนามิกและสร้างความสัมพันธ์แบบพึ่งพาได้
เช่น ใน Spring Bean Factory คุณสามารถเห็นได้ว่า Bean Factory จะสร้างและจัดการคลาสที่มีการแนบ @Controller, @Service, @Repository เป็นต้น แม้ว่านักพัฒนาจะไม่ได้แจ้งคลาสนั้นไปยัง Bean Factory แต่สิ่งนี้เป็นไปได้เนื่องจากการสะท้อน ในขณะรันไทม์ หากพบคลาสที่มีการแนบการอธิบายประกอบนั้น การสะท้อนจะใช้เพื่อสร้างอินสแตนซ์ของคลาสนั้น ใส่ฟิลด์ที่จำเป็น และเก็บไว้ใน Bean Factory
แน่นอน เนื่องจากละเมิดการห่อหุ้ม จึงควรใช้การสะท้อนเฉพาะในกรณีที่จำเป็นเท่านั้น