CFSDN nhấn mạnh vào giá trị tạo ra nguồn mở và chúng tôi cam kết xây dựng nền tảng chia sẻ tài nguyên để mọi nhân viên CNTT có thể tìm thấy thế giới tuyệt vời của bạn tại đây.
Bài viết trên blog CFSDN này nói sơ qua về công nghệ phản chiếu Java, được tác giả sưu tầm và biên soạn. Nếu các bạn quan tâm tới bài viết này thì nhớ like nhé.
1. Phản ánh là gì?
Phản chiếu là một trong những tính năng của Java, cho phép một chương trình Java đang chạy có được thông tin riêng của nó và thao tác các thuộc tính bên trong của một lớp hoặc đối tượng.
Lời giải thích chính thức của Oracle về sự phản ánh là:
Reflection cho phép mã Java khám phá thông tin về các trường, phương thức và hàm tạo của các lớp được tải và sử dụng các trường, phương thức và hàm tạo được phản ánh để vận hành trên các đối tác cơ bản của chúng, trong phạm vi giới hạn bảo mật. API hỗ trợ các ứng dụng cần truy cập vào các thành viên công khai của đối tượng mục tiêu (dựa trên lớp thời gian chạy của đối tượng) hoặc các thành viên được khai báo bởi một lớp nhất định. Nó cũng cho phép các chương trình ngăn chặn quyền kiểm soát truy cập phản chiếu mặc định.
Nói tóm lại, thông qua sự phản ánh, chúng ta có thể thu được thông tin về từng loại và thành viên của một chương trình hoặc tập hợp trong thời gian chạy. Các loại đối tượng chung trong chương trình được xác định tại thời điểm biên dịch, nhưng cơ chế phản ánh Java có thể tạo động các đối tượng và gọi các thuộc tính của chúng không xác định được tại thời điểm biên dịch. Vì vậy, chúng ta có thể tạo các đối tượng trực tiếp thông qua cơ chế phản chiếu, ngay cả khi loại đối tượng này không xác định được tại thời điểm biên dịch.
2. Công dụng chính của sự phản ánh
Nhiều người nghĩ rằng sự phản chiếu không được sử dụng rộng rãi trong các ứng dụng phát triển Java thực tế, nhưng thực tế không phải vậy. Khi chúng ta sử dụng IDE (như Eclipse, IDEA), khi chúng ta vào một đối tượng hoặc lớp và muốn gọi các thuộc tính hoặc phương thức của nó, ngay khi chúng ta nhấn dấu chấm, trình biên dịch sẽ tự động liệt kê các thuộc tính hoặc phương thức của nó. được sử dụng.
Công dụng quan trọng nhất của sự phản ánh là phát triển các khuôn khổ chung khác nhau. Nhiều khung công tác (chẳng hạn như Spring) được định cấu hình (chẳng hạn như định cấu hình các Bean thông qua các tệp XML). Để đảm bảo tính linh hoạt của khung, chúng có thể cần tải các đối tượng hoặc lớp khác nhau và gọi các phương thức khác nhau tùy theo tệp cấu hình. trường hợp, bạn phải sử dụng Để phản ánh, các đối tượng cần tải sẽ được tải động khi chạy.
Ví dụ: khi phát triển bằng framework Struts 2, chúng ta thường cấu hình Action trong struts.xml, chẳng hạn như:
- <hoạt động tên="đăng nhập"
- lớp="org.xxx.SimpleLoginAction"
- phương pháp="thực hiện">
-
/cửa hàng/cửa hàng-
chỉ số
.jsp
-
tên
="lỗi">đăng nhập.jsp
-
hoạt động>
Tệp cấu hình và Hành động thiết lập mối quan hệ ánh xạ. Khi lớp Xem đưa ra một yêu cầu, yêu cầu đó sẽ bị chặn bởi StrutsPrepareAndExecuteFilter, sau đó StrutsPrepareAndExecuteFilter sẽ tự động tạo một phiên bản Hành động. Ví dụ: nếu chúng ta yêu cầu login.action thì StrutsPrepareAndExecuteFilter sẽ phân tích tệp struts.xml, truy xuất hành động có tên đăng nhập trong hành động, tạo một cá thể SimpleLoginAction dựa trên thuộc tính lớp và sử dụng phương thức gọi để gọi phương thức thực thi. Quá trình này không thể tách rời khỏi sự phản ánh.
Đối với các nhà phát triển khung, sự phản chiếu tuy nhỏ nhưng rất quan trọng. Đối với các nhà phát triển bình thường, nếu không đi sâu vào phát triển framework, họ sẽ ít sử dụng sự phản ánh hơn. Tuy nhiên, việc hiểu được cơ chế cơ bản của framework sẽ giúp làm phong phú thêm ý tưởng lập trình của họ và cũng rất có lợi.
3. Sử dụng cơ bản sự phản ánh
3.1. Lấy đối tượng lớp thông qua sự phản chiếu
Có ba cách để có được đối tượng thông qua sự phản ánh.
3.1.1 Nhận được bởi Class.forName().
- công cộng tĩnh Lớp học
forName(Chuỗi className)
- ném ClassNotFoundException {
- Lớp học
người gọi = Reflection.getCallerClass();
- trở lại forName0(lớpTên, ĐÚNG VẬY, ClassLoader.getClassLoader(người gọi), người gọi);
- }
Ví dụ: phương pháp này thường được sử dụng trong quá trình phát triển JDBC để tải trình điều khiển cơ sở dữ liệu.
- Lớp.forName("Tên gói. Tên lớp");
3.1.2. Lấy tên lớp.class.
Ví dụ:
- Lớp học
intClass = số nguyên.lớp học;
- Lớp học
lớp số nguyên = Số nguyên.KIỂU;
-
- Lớp #RelfectEntity là một ví dụ cho bài viết này
- Lớp relfectEntity2 = RelfectEntity.class;
3.1.3. Lấy đối tượng thông qua getClass().
- StringBuilder str = new StringBuilder("123");
-
- Lớp strClass = str.getClass();
Cả ba phương thức đều có thể được sử dụng để thu được các đối tượng lớp. Trong quá trình phát triển khung, phương thức đầu tiên thường được sử dụng nhiều hơn.
3.2 Lấy thông tin về hàm tạo của lớp
Để có được cách sử dụng hàm tạo của lớp, bạn chủ yếu lấy một thể hiện của lớp Constructor thông qua phương thức getConstructor của lớp Class và lớp Constructor có một phương thức newInstance có thể tạo một thể hiện đối tượng.
- công cộng T newInstance(Đối tượng ... initargs)
3.3 Lấy một thể hiện của một lớp
Có hai cách chính để tạo ra các đối tượng thông qua sự phản chiếu.
Sử dụng phương thức newInstance() của đối tượng Class để tạo một thể hiện của lớp tương ứng của đối tượng Class.
- Lớp c = String.class;
-
- Đối tượng str = c.newInstance();
Đầu tiên lấy đối tượng Constructor được chỉ định thông qua đối tượng Class, sau đó gọi phương thức newInstance() của đối tượng Constructor để tạo một thể hiện.
- //Lấy đối tượng Class tương ứng với String
- Lớp học
c = String. class;
- //Lấy hàm tạo của lớp String với tham số String
- Trình xây dựng constructor = c.getConstructor(String.class);
- //Tạo một thể hiện dựa trên hàm tạo
- Đối tượng obj = constructor.newInstance("23333");
- Hệ thống.ngoài.println(đối tượng);
Phương thức này xây dựng một thể hiện của một lớp bằng cách sử dụng một hàm tạo được chỉ định.
3.4. Lấy biến lớp
Lớp thực thể:
- /**
- * Lớp cơ sở
- */
- công cộng lớp BaseClass {
-
- công cộng Chuỗi publicBaseVar1;
-
- công cộng Chuỗi publicBaseVar2;
- }
-
- /**
- * Phân lớp
- */
- công cộng lớp ChildClass mở rộng BaseClass{
-
- công cộng Chuỗi publicOneVar1;
-
- công cộng Chuỗi publicOneVar2;
-
- Chuỗi riêng tư privateOneVar1;
-
- Chuỗi riêng tư privateOneVar2;
-
- công cộng Chuỗi printOneMsg() {
- trở lại OneVar1 riêng tư;
- }
- }
Bài kiểm tra:
- công cộng lớp VarTest {
-
- công cộng tĩnh void main(String[] args) {
- //1. Lấy và xuất tên của lớp
- Lớp mClass = ChildClass.class;
- Hệ thống.ngoài.println("Tên lớp:" + mClass.getName());
- Hệ thống.ngoài.println("----Nhận tất cả các biến truy cập công khai (bao gồm cả những biến được khai báo bởi lớp này và những biến được kế thừa từ lớp cha)----");
-
- //2. Nhận tất cả công cộng Các biến quyền truy cập (bao gồm các biến được khai báo bởi lớp này và các biến được kế thừa từ lớp cha)
- Trường[] các trường = mClass.getFields();
-
- //Truyền các biến và xuất thông tin biến
- vì (Trường trường : các trường) {
- // Nhận quyền truy cập và đầu ra
- số nguyên bộ sửa đổi = field.getModifiers();
- Hệ thống.ngoài.print(Modifier.toString(các trình sửa đổi) + " ");
-
- //Loại biến đầu ra và tên biến
- Hệ thống.ngoài.println(trường.getType().getName() + " " + field.getName());
- }
- Hệ thống.ngoài.println("----Lấy tất cả các biến được khai báo trong lớp này----");
-
- //3. Lấy tất cả các biến được khai báo trong lớp này
- Trường[] tất cả các trường = mClass.getDeclaredFields();
- vì (Trường trường: allFields) {
- // Nhận quyền truy cập và đầu ra
- số nguyên bộ sửa đổi = field.getModifiers();
- Hệ thống.ngoài.print(Modifier.toString(các trình sửa đổi) + " ");
-
- //Loại biến đầu ra và tên biến
- Hệ thống.ngoài.println(trường.getType().getName() + " " + field.getName());
- }
- }
- }
Kết quả đầu ra:
- Tên lớp: com.example.java.reflect.ChildClass
-
- công cộng java.lang.String publicOneVar1
- công cộng java.lang.String publicOneVar2
- công cộng java.lang.String publicBaseVar1
- công cộng java.lang.String publicBaseVar2
-
- công cộng java.lang.String publicOneVar1
- công cộng java.lang.String publicOneVar2
- java.lang.String riêng tư privateOneVar1
- java.lang.String riêng tư privateOneVar2
3.5 Sửa đổi các biến lớp
Sửa đổi các lớp con.
- /**
- * Phân lớp
- */
- công cộng lớp ChildClass mở rộng BaseClass{
-
- công cộng Chuỗi publicOneVar1;
-
- công cộng Chuỗi publicOneVar2;
-
- Chuỗi riêng tư privateOneVar1;
-
- Chuỗi riêng tư privateOneVar2;
-
- công cộng Chuỗi printOneMsg() {
- trở lại OneVar1 riêng tư;
- }
- }
Bài kiểm tra:
- công cộng lớp VarModfiyTest {
-
- công cộng tĩnh void main(String[] args) ném ra ngoại lệ {
- //1. Lấy và xuất tên của lớp
- Lớp mClass = ChildClass.class;
- Hệ thống.ngoài.println("Tên lớp:" + mClass.getName());
- Hệ thống.ngoài.println("----Lấy biến riêng tư PrivateOneVar1 trong lớp ChildClass----");
-
- //2. Lấy biến riêng tư PrivateOneVar1 trong lớp ChildClass
- Trường privateField = mClass.getDeclaredField("privateOneVar1");
-
- //3. Thao tác với các biến riêng tư
- nếu (privateField != vô giá trị) {
- // Truy cập vào các biến riêng tư
- privateField.setAccessible(ĐÚNG VẬY);
-
- // khởi tạo đối tượng
- ChildClass obj = (ChildClass) mClass.newInstance();
-
- // Sửa đổi các biến riêng tư và đầu ra để thử nghiệm
- Hệ thống.ngoài.println("biến PrivateOneVar1, giá trị trước khi sửa đổi: " + obj.printOneMsg());
-
- //gọi bộ(đối tượng, giá trị) Sửa đổi giá trị của một biến
- //privateField là biến riêng tư thu được
- //obj Đối tượng được thao tác
- //"Xin chào thế giới" là giá trị được sửa đổi thành
- Trường riêng tư.bộ(đối tượng, "Xin chào thế giới");
- Hệ thống.ngoài.println("biến PrivateOneVar1, giá trị được sửa đổi: " + obj.printOneMsg());
- }
- }
- }
Kết quả đầu ra:
- Tên lớp: com.example.java.reflect.ChildClass
-
- Biến riêngOneVar1, giá trị trước khi sửa đổi: vô giá trị
- Biến riêngOneVar1, giá trị được sửa đổi: hello world
3.6 Lấy tất cả các phương thức của một lớp
Sửa đổi lớp thực thể.
- /**
- * Lớp cơ sở
- */
- công cộng lớp BaseClass {
-
- công cộng Chuỗi publicBaseVar1;
-
- công cộng Chuỗi publicBaseVar2;
-
- khoảng trống riêng tưprivatePrintBaseMsg(String var) {
- Hệ thống.ngoài.println("Lớp cơ sở - phương thức riêng, biến:" + đã);
- }
-
- công cộng void publicPrintBaseMsg(String var) {
- Hệ thống.ngoài.println("Lớp cơ sở - phương thức công khai, biến:" + đã);
- }
- }
-
- /**
- * Phân lớp
- */
- công cộng lớp ChildClass mở rộng BaseClass{
-
- công cộng Chuỗi publicOneVar1;
-
- công cộng Chuỗi publicOneVar2;
-
- Chuỗi riêng tư privateOneVar1;
-
- Chuỗi riêng tư privateOneVar2;
-
- công cộng Chuỗi printOneMsg() {
- trở lại OneVar1 riêng tư;
- }
-
- khoảng trống riêng tư PrivatePrintOneMsg(String var) {
- Hệ thống.ngoài.println("Lớp con - phương thức riêng, biến:" + đã);
- }
-
- công cộng void publicPrintOneMsg(String var) {
- Hệ thống.ngoài.println("Lớp con - phương thức công khai, biến:" + đã);
- }
- }
Bài kiểm tra:
- công cộng lớp MethodTest {
-
- công cộng tĩnh void main(String[] args) {
- //1. Lấy và xuất tên của lớp
- Lớp mClass = ChildClass.class;
- Hệ thống.ngoài.println("Tên lớp:" + mClass.getName());
- Hệ thống.ngoài.println("----Các phương pháp để có được tất cả các quyền truy cập công khai, bao gồm cả những quyền do chính bạn khai báo và những quyền được kế thừa từ các lớp cha---");
-
- //2 lấy tất cả công cộng Truy cập các phương thức cấp phép, bao gồm các phương thức do chính bạn khai báo và các phương thức được kế thừa từ các lớp cha
- Phương thức[] mMethods = mClass.getMethods();
- vì (Phương pháp phương pháp: mMethods) {
- //Nhận và xuất các quyền truy cập của phương thức (Modifiers: modifier)
- số nguyên bộ sửa đổi = phương thức.getModifiers();
- Hệ thống.ngoài.print(Modifier.toString(các trình sửa đổi) + " ");
-
- //Lấy và xuất kiểu giá trị trả về của phương thức
- Lớp returnType = Method.getReturnType();
- Hệ thống.ngoài.print(returnType.getName() + " " + phương thức.getName() + "( ");
-
- // Lấy và xuất tất cả các tham số của phương thức
- Tham số[] tham số = phương thức.getParameters();
- vì (Tham số tham số: các tham số) {
- Hệ thống.ngoài.print(tham số.getType().getName() + " " + tham số.getName() + ",");
- }
-
- //Nhận và xuất ngoại lệ do phương thức đưa ra
- Lớp[] exceptionTypes = phương thức.getExceptionTypes();
- nếu (exceptionTypes.length == 0){
- Hệ thống.ngoài.println(" )");
- } khác {
- vì (Lớp c: exceptionTypes) {
- Hệ thống.ngoài.println(" ) ném " + c. getName());
- }
- }
- }
- Hệ thống.ngoài.println("----Nhận tất cả các phương thức của lớp này---");
- //3. Lấy tất cả các phương thức của lớp này
- Phương thức[] allMethods = mClass.getDeclaredMethods();
- vì (Phương pháp phương pháp: allMethods) {
- //Nhận và xuất các quyền truy cập của phương thức (Modifiers: modifier)
- số nguyên bộ sửa đổi = phương thức.getModifiers();
- Hệ thống.ngoài.print(Modifier.toString(các trình sửa đổi) + " ");
-
- //Lấy và xuất kiểu giá trị trả về của phương thức
- Lớp returnType = Method.getReturnType();
- Hệ thống.ngoài.print(returnType.getName() + " " + phương thức.getName() + "( ");
-
- // Lấy và xuất tất cả các tham số của phương thức
- Tham số[] tham số = phương thức.getParameters();
- vì (Tham số tham số: các tham số) {
- Hệ thống.ngoài.print(tham số.getType().getName() + " " + tham số.getName() + ",");
- }
-
- //Nhận và xuất ngoại lệ do phương thức đưa ra
- Lớp[] exceptionTypes = phương thức.getExceptionTypes();
- nếu (exceptionTypes.length == 0){
- Hệ thống.ngoài.println(" )");
- } khác {
- vì (Lớp c: exceptionTypes) {
- Hệ thống.ngoài.println(" ) ném " + c. getName());
- }
- }
- }
- }
- }
Đầu ra:
- Tên lớp: com.example.java.reflect.ChildClass
-
- công cộng java.lang.String printOneMsg()
- công cộng void publicPrintOneMsg( java.lang.String arg0, )
- công cộng void publicPrintBaseMsg( java.lang.String arg0, )
- công cộng void wait( arg0 dài cuối cùng,số nguyên arg1, ) ném java.lang.InterruptedException
- công cộng cuối cùng void wait( long arg0, ) gốc ném java.lang.InterruptedException
- công cộng void wait() cuối cùng ném java.lang.InterruptedException
- công cộng boolean bằng( java.lang.Object arg0, )
- công cộng java.lang.String thànhChuỗi()
- công cộng tự nhiên số nguyên Mã băm ( )
- công cộng java.lang.Class gốc cuối cùng getClass()
- công cộng cuối cùng bản địa void notify()
- công cộng cuối cùng bản địa void notifyAll()
-
- công cộng java.lang.String printOneMsg()
- riêng tư void privatePrintOneMsg( java.lang.String arg0, )
- công cộng void publicPrintOneMsg( java.lang.String arg0, )
Tại sao có nhiều đầu ra như vậy?
Bởi vì tất cả các lớp đều kế thừa lớp đối tượng theo mặc định, nên bạn sẽ tìm thấy một số phương thức công khai khi mở lớp đối tượng, để chúng được in ra cùng nhau.
3.7. Phương thức gọi
- công cộng lớp MethodInvokeTest {
-
- công cộng tĩnh void main(String[] args) ném ra ngoại lệ {
- // 1. Lấy và xuất tên lớp
- Lớp mClass = ChildClass.class;
- Hệ thống.ngoài.println("Tên lớp:" + mClass.getName());
- Hệ thống.ngoài.println("----Lấy phương thức riêng tư PrivatePrintOneMsg của lớp ChildClass---");
-
- // 2. Lấy phương thức riêng tư tương ứng
- // Tham số đầu tiên là tên của phương thức riêng cần lấy
- // Cái thứ hai là loại tham số cần lấy. Tham số là Class..., nếu không có tham số thì là.vô giá trị
- //Các tham số của phương thức cũng có thể được viết như thế này: new Class[]{String.class}
- Phương thức privateMethod = mClass.getDeclaredMethod("privatePrintOneMsg", String. class);
-
- // 3. Bắt đầu phương thức hoạt động
- nếu (privateMethod != vô giá trị) {
- // Truy cập vào các phương thức riêng tư
- // Chỉ cần có quyền truy cập, không sửa đổi quyền thực tế
- PrivateMethod.setAccessible(ĐÚNG VẬY);
-
- // khởi tạo đối tượng
- ChildClass obj = (ChildClass) mClass.newInstance();
-
- // Sử dụng sự phản chiếu gọi để gọi các phương thức riêng tư
- // obj Đối tượng được thao tác
- // Các tham số sau được truyền dưới dạng tham số thực tế
- privateMethod.invoke(obj, "Xin chào thế giới");
- }
- }
- }
Kết quả đầu ra:
- Tên lớp: com.example.java.reflect.ChildClass
-
-
-
- Lớp con - phương thức riêng, biến: hello world
4. Tóm tắt
Vì sự phản chiếu tiêu tốn một lượng tài nguyên hệ thống bổ sung nhất định, nên nếu bạn không cần tạo động một đối tượng thì không cần sử dụng sự phản chiếu. Ngoài ra, việc kiểm tra quyền có thể bị bỏ qua khi gọi các phương thức thông qua phản chiếu, do đó có khả năng phá vỡ tính năng đóng gói và gây ra các vấn đề bảo mật.
5. Bài viết tham khảo
sczyh30: Phân tích chuyên sâu về phản ánh Java.
Burt: Phản ánh Java từ cơ bản đến nâng cao.
Liên kết gốc: https://mp.weixin.qq.com/s/CxRmB8xko2nou_4tZxCJdg.
Cuối cùng, bài viết thảo luận ngắn gọn về công nghệ phản chiếu Java này kết thúc tại đây. Nếu bạn muốn biết thêm về một cuộc thảo luận ngắn gọn về công nghệ phản chiếu Java, vui lòng tìm kiếm các bài viết về CFSDN hoặc tiếp tục duyệt các bài viết liên quan. tương lai! .
Tôi là một lập trình viên xuất sắc, rất giỏi!