1. Trang chủ
  2. » Công Nghệ Thông Tin

Động lực học lập trình Java, Phần 2: Giới thiệu sự phản chiếu Sử dụng thông tin lớp trong thời gian chạy để khởi động việc lập trình của bạn pps

20 492 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 20
Dung lượng 259,94 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Đối với mỗi một trong ba kiểu này của các thành phần lớp -- các hàm tạo constructor, các trường và các phương thức -- java.lang.Class cung cấp bốn cuộc gọi thể hiện sự phản chiếu riêng b

Trang 1

Động lực học lập trình Java, Phần 2: Giới thiệu sự phản chiếu

Sử dụng thông tin lớp trong thời gian chạy để khởi động việc lập trình của bạn

Dennis Sosnoski, Nhà tư vấn, Sosnoski Software Solutions, Inc

Tóm tắt: Sự phản chiếu cho phép truy cập mã của bạn tới thông tin bên trong đối

với các lớp được nạp vào JVM và cho phép bạn viết mã để làm việc với các lớp được lựa chọn trong quá trình thực hiện, không phải trong mã nguồn Điều này tạo cho sự phản chiếu một công cụ quan trọng để xây dựng các ứng dụng linh hoạt Nhưng xem ra nếu được sử dụng không thích hợp, sự phản chiếu có thể tốn kém Trong Phần 2 của loạt bài của mình về bản chất của nền tảng Java, nhà tư vấn phần mềm Dennis Sosnoski đưa ra một sự giới thiệu về cách sử dụng sự phản chiếu, cũng như xem xét một số các chi phí liên quan Bạn cũng sẽ tìm hiểu cách Java Reflection API (API phản chiếu Java) cho phép bạn kết nối vào các đối tượng trong thời gian chạy

Trong "Động lực học lập trình Java, Phần 1," tôi đã cung cấp cho bạn một sự giới thiệu về các lớp lập trình Java và nạp lớp Bài viết đó mô tả một số tư liệu thông tin rộng lớn theo định dạng lớp nhị phân Java Trong bài viết tháng này, tôi sẽ trình bày những điều cơ bản về việc sử dụng Java Reflection API để truy cập và sử dụng một số thông tin như vậy trong thời gian chạy Để giúp duy trì những điều này thú vị với các nhà phát triển, những người đã biết những điều cơ bản của sự phản chiếu, tôi sẽ trình bày một cái nhìn về cách so sánh hiệu năng phản chiếu với truy cập trực tiếp

Đừng bỏ lỡ phần còn lại của loạt bài này

Phần 1, "Các lớp Java và nạp lớp" (04.2003)

Phần 3, "Ứng dụng sự phản chiếu" (07.2003)

Phần 4, "Chuyển đổi lớp bằng Javassist" (09.2003)

Phần 5, "Việc chuyển các lớp đang hoạt động" (02.2004)

Phần 6, "Các thay đổi hướng-khía cạnh với Javassist" (03.2004)

Phần 7, "Kỹ thuật bytecode với BCEL" (04.2004)

Phần 8, "Thay thế sự phản chiếu bằng việc tạo mã" (06.2004)

Trang 2

Sử dụng sự phản chiếu khác với lập trình Java tiêu chuẩn ở chỗ nó làm việc với

siêu dữ liệu dữ liệu mô tả dữ liệu khác Kiểu siêu dữ liệu cụ thể được truy cập

bởi sự phản chiếu của ngôn ngữ Java là sự mô tả về các lớp và các đối tượng bên trong JVM Sự phản chiếu cho phép bạn truy cập trong thời gian chạy đến một loạt các thông tin lớp Thậm chí nó còn cho phép bạn đọc và viết các trường và các phương thức gọi của một lớp được chọn trong thời gian chạy

Sự phản chiếu là một công cụ mạnh Nó cho phép bạn xây dựng mã linh hoạt, mã này có thể được lắp ráp trong thời gian chạy mà không đòi hỏi các liên kết mã nguồn giữa các thành phần Nhưng một số khía cạnh của sự phản chiếu có thể khó

hiểu Trong bài này, tôi sẽ đi vào những lý do tại sao bạn có thể không muốn sử

dụng sự phản chiếu trong các chương trình của bạn, cũng như những lý do tại sao bạn muốn Sau khi bạn biết các sự thỏa hiệp, bạn có thể quyết định cho chính mình khi những lợi ích có giá trị hơn những hạn chế

Lớp của những người mới bắt đầu

Điểm khởi đầu để sử dụng sự phản chiếu luôn luôn là một cá thể java.lang.Class Nếu bạn muốn làm việc với một lớp định sẵn, thì ngôn ngữ Java cung cấp một phím tắt dễ dàng để có được cá thể Class trực tiếp:

Class clas = MyClass.class;

Khi bạn sử dụng kỹ thuật này, tất cả các công việc liên quan đến việc nạp các lớp diễn ra ở hậu trường Tuy nhiên, nếu bạn cần phải đọc tên lớp trong thời gian chạy

từ một số nguồn bên ngoài, thì cách tiếp cận này không phải là sắp thực hiện Thay vào đó, bạn cần phải sử dụng một trình nạp lớp để tìm thông tin lớp Dưới đây là một cách để thực hiện điều đó:

// "name" is the class name to load

Class clas = null;

try {

clas = Class.forName(name);

Trang 3

} catch (ClassNotFoundException ex) {

// handle exception case

}

// use the loaded class

Nếu lớp đã được nạp, bạn sẽ tìm lại các thông tin Class hiện có Nếu lớp chưa được nạp, trình nạp lớp sẽ nạp nó bây giờ và trả về cá thể lớp vừa mới được xây dựng

Sự phản chiếu trên một lớp

Đối tượng Class mang đến cho bạn tất cả các kết nối cơ bản để truy cập phản chiếu đến siêu dữ liệu lớp Siêu dữ liệu này bao gồm các thông tin về chính lớp đó, chẳng hạn như gói và siêu lớp của lớp đó, cũng như các giao diện được lớp đó triển khai thực hiện Nó cũng bao gồm các chi tiết về các hàm tạo, các trường và các phương thức được lớp đó xác định Các mục sau cùng này là những thứ hầu như thường được sử dụng trong lập trình, vì vậy tôi sẽ đưa ra một số ví dụ về làm việc với chúng sau trong phần này

Hỏi chuyên gia: Dennis Sosnoski về các vấn đề JVM và bytecode

Đối với các ý kiến hay các câu hỏi về tài liệu được trình bày trong loạt bài này, cũng như bất cứ điều gì khác có liên quan đến Java bytecode, định dạng lớp nhị phân Java hoặc các vấn đề JVM chung, hãy truy cập vào diễn đàn thảo luận JVM

và Bytecode, do Dennis Sosnoski kiểm soát

Đối với mỗi một trong ba kiểu này của các thành phần lớp các hàm tạo

(constructor), các trường và các phương thức java.lang.Class cung cấp bốn cuộc gọi thể hiện sự phản chiếu riêng biệt để truy cập thông tin theo nhiều cách khác nhau Tất cả các cuộc gọi đi theo sau một dạng chuẩn Đây là một tập được sử dụng để tìm các hàm tạo:

Trang 4

 Constructor getConstructor(Class[] params) Tìm ra hàm tạo công khai bằng cách sử dụng các kiểu tham số cụ thể

 Constructor[] getConstructors() Tìm ra tất cả các hàm tạo công khai cho lớp đó

 Constructor getDeclaredConstructor(Class[] params) Tìm ra hàm tạo (bất

kể mức truy cập) bằng cách sử dụng các kiểu tham số cụ thể

 Constructor[] getDeclaredConstructors() Tìm ra tất cả các hàm tạo (bất

kể mức truy cập) cho lớp đó

Mỗi một trong các cuộc gọi này trả về một hoặc nhiều cá thể

java.lang.reflect.Constructor Lớp Constructor này định nghĩa một phương thức newInstance lấy một mảng các đối tượng làm đối số duy nhất của nó, sau đó trả về một cá thể vừa được xây dựng của lớp gốc Mảng các đối tượng là các giá trị tham

số sử dụng cho cuộc gọi hàm tạo Như là một ví dụ về cách làm việc này, giả sử bạn có một lớp TwoString với một hàm tạo lấy một cặp String, như thể hiện trong Liệt kê 1:

Liệt kê 1 Lớp được xây dựng từ cặp strings

public class TwoString {

private String m_s1, m_s2;

public TwoString(String s1, String s2) {

m_s1 = s1;

m_s2 = s2;

}

}

Mã được hiển thị trong Liệt kê 2 tìm ra hàm tạo và sử dụng nó để tạo một cá thể của lớp TwoString khi sử dụng Strings "a" và "b":

Trang 5

Liệt kê 2 Cuộc gọi sự phản chiếu cho hàm tạo

Class[] types = new Class[] { String.class, String.class };

Constructor cons = TwoString.class.getConstructor(types);

Object[] args = new Object[] { "a", "b" };

TwoString ts = (TwoString)cons.newInstance(args);

Mã trong Liệt kê 2 bỏ qua một số kiểu có thể của các ngoại lệ đã kiểm tra được các phương thức phản chiếu khác nhau đưa ra Các ngoại lệ này được trình bày chi tiết trong các mô tả Javadoc API, vậy để cho ngắn gọn, tôi để chúng ở ngoài các ví

dụ này

Trong khi tôi đang nói chủ đề về các hàm tạo, ngôn ngữ lập trình Java cũng định nghĩa một phương thức phím tắt đặc biệt mà bạn có thể sử dụng để tạo một cá thể

của một lớp bằng một hàm tạo no-argument (hoặc mặc định) Phím tắt này được

nhúng vào trong định nghĩa Class riêng của nó như sau:

Object newInstance() Xây dựng cá thể mới khi sử dụng hàm tạo mặc định

Mặc dù cách tiếp cận này chỉ cho phép bạn sử dụng một hàm tạo cụ thể, nó tạo một phím tắt rất tiện lợi nếu đó là một thứ bạn muốn Kỹ thuật này đặc biệt có ích khi làm việc với JavaBeans, JavaBeans được dùng để xác định một hàm tạo công khai, không có đối số (no-argument)

Các trường của sự phản chiếu

Các cuộc gọi phản chiếu Class (lớp) nhằm truy cập thông tin về trường là tương tự như các cuộc gọi được dùng để truy cập các hàm tạo, với tên trường được sử dụng thay cho một mảng của các kiểu tham số:

 Field getField(String name) Tìm ra trường công khai có tên

 Field[] getFields() Tìm ra tất cả các trường công khai của lớp đó

Trang 6

 Field getDeclaredField(String name) Tìm ra trường có tên được lớp đó khai báo

 Field[] getDeclaredFields() Tìm ra tất cả các trường được lớp đó khai báo

Mặc dù có sự tương đồng với các cuộc gọi hàm tạo, cũng có một sự khác biệt quan trọng khi nói đến các trường: hai trường đầu tiên trả về các thông tin cho các trường công khai để có thể truy cập chúng thông qua lớp đó ngay cả những lớp được thừa kế từ một lớp ông bà Hai trường cuối trả về các thông tin cho các trường được lớp đó khai báo trực tiếp không phân biệt các kiểu truy cập của trường

Các cá thể java.lang.reflect.Field được các cuộc gọi trả về định nghĩa các phương thức getXXX và setXXX cho tất cả các kiểu nguyên thủy, cũng như các phương thức get và set chung làm việc với các tham chiếu đối tượng Nó cho bạn quyết định sử dụng một phương thức thích hợp dựa trên kiểu trường thực tế, mặc dù các phương thức getXXX sẽ xử lý tự động các biến đổi mở rộng (như khi sử dụng phương thức getInt để lấy ra một giá trị byte)

Liệt kê 3 cho thấy một ví dụ về việc sử dụng các phương thức phản chiếu trường, dưới dạng một phương thức để tăng một trường int của một đối tượng theo tên:

Liệt kê 3 Tăng một trường bằng sự phản chiếu

public int incrementField(String name, Object obj) throws {

Field field = obj.getClass().getDeclaredField(name);

int value = field.getInt(obj) + 1;

field.setInt(obj, value);

return value;

}

Trang 7

Phương thức này bắt đầu hiển thị một số tính linh hoạt có thể với sự phản chiếu Thay vì làm việc với một lớp cụ thể, incrementField sử dụng phương thức

getClass của đối tượng được chuyển qua để tìm thông tin lớp, sau đó trực tiếp tìm trường có tên trong lớp đó

Các phương thức của sự phản chiếu

Sự phản chiếu Class gọi truy cập thông tin của phương thức rất giống với những

sự phản chiếu được sử dụng cho các hàm tạo và các trường:

 Method getMethod(String name, Class[] params) Tìm ra phương thức công khai có tên bằng cách sử dụng các kiểu tham số cụ thể

 Method[] getMethods() Tìm ra tất cả các phương thức công khai của lớp

 Method getDeclaredMethod(String name, Class[] params) Tìm ra

phương thức công khai có tên được lớp đó khai báo bằng cách sử dụng các kiểu tham số cụ thể

 Method[] getDeclaredMethods() Tìm ra tất cả các phương thức được lớp

đó khai báo

Như với các cuộc gọi trường, hai phương thức đầu tiên trả về thông tin cho các phương thức công khai có thể được truy cập thông qua lớp đó, ngay cả các lớp đó được thừa kế từ một lớp ông bà Hai phương thức cuối trả về thông tin cho các phương thức được lớp đó khai báo trực tiếp, mà không liên quan đến kiểu truy cập của phương thức này

Các cá thể java.lang.reflect.Method được các cuộc gọi trả về định nghĩa một

phương thức invoke (gọi) mà bạn có thể sử dụng để gọi phương thức đó trên một

cá thể của lớp định nghĩa Phương thức invoke này lấy hai đối số cung cấp cá thể lớp và một mảng các giá trị tham số cho cuộc gọi này

Liệt kê 4 đưa ví dụ về trường tiến thêm một bước, khi hiển thị một ví dụ về sự phản chiếu của phương thức đang hành động Phương thức này làm tăng một thuộc tính int JavaBean được xác định bằng phương thức get và set Ví dụ, nếu đối tượng đã xác định phương thức getCount và setCount cho một giá trị count (đếm)

số nguyên, thì bạn có thể vượt qua "count" như tham số name trong một cuộc gọi đến phương thức này để tăng giá trị đó

Liệt kê 4 Làm tăng một thuộc tính JavaBean bằng sự phản chiếu

Trang 8

public int incrementProperty(String name, Object obj) {

String prop = Character.toUpperCase(name.charAt(0)) +

name.substring(1);

String mname = "get" + prop;

Class[] types = new Class[] {};

Method method = obj.getClass().getMethod(mname, types);

Object result = method.invoke(obj, new Object[0]);

int value = ((Integer)result).intValue() + 1;

mname = "set" + prop;

types = new Class[] { int.class };

method = obj.getClass().getMethod(mname, types);

method.invoke(obj, new Object[] { new Integer(value) });

return value;

}

Để thực hiện theo các quy ước JavaBeans, tôi biến đổi chữ cái đầu của thuộc tính tên thành chữ hoa, sau đó dựa vào get để xây dựng tên phương thức đọc và set để xây dựng tên phương thức viết Các phương thức đọc JavaBeans chỉ trả về giá trị

và viết các phương thức lấy giá trị làm tham số duy nhất, vì vậy tôi chỉ định các kiểu tham số cho các phương thức cho phù hợp Cuối cùng, quy ước đòi hỏi các phương thức là công khai, vậy tôi sử dụng dạng tra cứu thông tin để tìm các phương thức công khai có khả năng gọi được trên lớp đó

Ví dụ này là một ví dụ đầu tiên mà tôi đã chuyển qua các giá trị nguyên thủy khi

sử dụng sự phản chiếu, vậy chúng ta hãy xem xét cách làm này Nguyên tắc cơ bản rất đơn giản: bất cứ khi nào bạn cần phải chuyển qua một giá trị nguyên thủy, chỉ cần thay thế một cá thể của lớp trình bao (wrapper) tương ứng (được định

Trang 9

nghĩa trong gói java.lang cho kiểu nguyên thủy đó Điều này áp dụng cho cả các cuộc gọi và cả các trả về Vì vậy, khi tôi gọi phương thức get trong ví dụ của tôi, tôi chờ đợi kết quả là một trình bao java.lang.Integer cho giá trị thuộc tính int thực

sự

Phản chiếu các mảng

Các mảng là các đối tượng trong ngôn ngữ lập trình Java Giống như tất cả các đối tượng, chúng có các lớp Nếu bạn có một mảng, bạn có thể nhận được lớp của mảng đó khi sử dụng phương thức getClass chuẩn, cũng giống như với bất kỳ đối

tượng khác Tuy nhiên, việc nhận được lớp đó mà không có một cá thể hiện có làm

việc khác với các kiểu đối tượng khác Ngay cả sau khi bạn có một lớp mảng không có nhiều lớp bạn có thể làm việc trực tiếp với nó, việc truy cập hàm tạo được sự phản chiếu cho các lớp thông thường đưa ra không làm việc với các mảng

và các mảng không có bất kỳ các trường nào dễ truy cập Các phương thức

java.lang.Object cơ bản chỉ được định nghĩa cho đối tượng mảng

Việc xử lý đặc biệt của các mảng sử dụng một tập hợp các phương thức tĩnh được lớp java.lang.reflect.Array cung cấp Các phương thức trong lớp này cho phép bạn tạo các mảng mới, nhận được chiều dài của một đối tượng mảng và đọc và viết các giá trị có chỉ mục của một đối tượng mảng

Liệt kê 5 cho thấy một phương thức hữu ích để thay đổi kích thước một mảng hiện

có một cách hiệu quả Nó sử dụng sự phản chiếu để tạo một mảng mới cùng kiểu, sau đó sao chép tất cả các dữ liệu suốt từ mảng cũ trước khi trả về mảng mới

Liệt kê 5 Phát triển một mảng bằng sự phản chiếu

public Object growArray(Object array, int size) {

Class type = array.getClass().getComponentType();

Object grown = Array.newInstance(type, size);

System.arraycopy(array, 0, grown, 0,

Math.min(Array.getLength(array), size));

return grown;

Trang 10

}

An ninh và sự phản chiếu

An ninh có thể là một vấn đề phức tạp khi đối phó với sự phản chiếu Mã kiểu-khung công tác thường sử dụng sự phản chiếu và với điều này bạn có thể muốn khung công tác có truy cập đầy đủ tới mã của bạn mà không cần quan tâm về các hạn chế truy cập thông thường Tuy vậy việc truy cập không kiểm soát được có thể tạo những nguy cơ an ninh chính trong các trường hợp khác, chẳng hạn như khi

mã được thi hành trong một môi trường được chia sẻ bởi mã không đáng tin cậy

Do các nhu cầu xung đột nhau, nên ngôn ngữ lập trình Java định nghĩa một cách tiếp cận đa cấp để xử lý an ninh phản chiếu Các chế độ cơ bản là bắt tuân theo các hạn chế như nhau trên sự phản chiếu như đã áp dụng cho việc truy cập mã nguồn:

 Truy cập từ bất cứ ở đâu tới các thành phần công khai của lớp

 Không truy cập bên ngoài lớp riêng của nó tới các thành phần riêng

 Truy cập có giới hạn tới các thành phần được bảo vệ và các thành phần gói (truy cập mặc định)

Tuy nhiên có một cách đơn giản xung quanh các hạn chế này thỉnh thoảng có Tất cả các lớp Constructor, Field, và Method mà tôi đã sử dụng trong các ví dụ trước đó mở rộng một lớp cơ sở chung lớp java.lang.reflect.AccessibleObject Lớp này định nghĩa một phương thức setAccessible cho phép bạn bật hoặc tắt kiểm tra truy cập cho một cá thể của một trong những lớp này Việc bắt giữ duy nhất (catch) là nếu có một trình quản lý an ninh, nó sẽ kiểm tra xem mã tắt kiểm tra truy cập có cho phép làm như vậy không Nếu không cho phép, trình quản lý an ninh đưa ra một lỗi ngoại lệ

Liệt kê 6 giải thích một chương trình có sử dụng sự phản chiếu trên một cá thể của lớp TwoString của Liệt kê 1 để hiển thị điều này đang hoạt động:

Liệt kê 6 An ninh phản chiếu đang hoạt động

Ngày đăng: 07/08/2014, 10:22

HÌNH ẢNH LIÊN QUAN

Hình 1. Các thời gian truy cập trường - Động lực học lập trình Java, Phần 2: Giới thiệu sự phản chiếu Sử dụng thông tin lớp trong thời gian chạy để khởi động việc lập trình của bạn pps
Hình 1. Các thời gian truy cập trường (Trang 14)
Hình 2. Các thời gian gọi phương thức - Động lực học lập trình Java, Phần 2: Giới thiệu sự phản chiếu Sử dụng thông tin lớp trong thời gian chạy để khởi động việc lập trình của bạn pps
Hình 2. Các thời gian gọi phương thức (Trang 17)

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w