- public class ReadOnlyClass {
- private String name = "hello";
- public String getName(){
- return name;
- }
- }
- public class ReadOnlyClass {
- private String name = "hello";
- public String getName(){
- return name;
- }
- }
public class ReadOnlyClass {private String name = "hello";public String getName(){return name;}}public class ReadOnlyClass {private String name = "hello";public String getName(){return name;}}
問題:能否將ReadOnlyClass 類的一個(gè)對象,把它的name屬性的值由hello改為world?如果能,請寫出實(shí)現(xiàn)代碼。如果不能請說明理由。
解答:可以。利用java的反射。
分析:任何一個(gè)類,我們可以得到它運(yùn)行時(shí)的Class實(shí)例,對于ReadOnlyClass 類,我們可以通過ReadOnlyClass .class得到它運(yùn)行時(shí)的Class實(shí)例,接著我們可以通過該類的Class實(shí)例去獲得這個(gè)name這個(gè)屬性所對應(yīng)的Field對象。我們知道對應(yīng)一個(gè)類的屬性都有一個(gè)和它相關(guān)的Field對象存在,對于構(gòu)造方法來說有一個(gè)Constructor對象存在,對于一個(gè)方法來說有一個(gè)對應(yīng)的Method對象存在。通過這些我們可以利用發(fā)射來給這些屬性動(dòng)態(tài)的賦值。首先我們來看jdk api中Class類的兩個(gè)方法的敘述:
public Field getField(String name)throws NoSuchFieldException,SecurityException
Returns a Field object that reflects the specified public member field of the class or interface represented by this Class object. The name parameter is a String specifying the simple name of the desired field.(翻譯:返回一個(gè) Field 對象,它反映此 Class 對象所表示的類或接口的指定publlic屬性字段。name 參數(shù)是一個(gè) String,用于指定所需字段的簡稱。)
The field to be reflected is determined by the algorithm that follows. Let C be the class represented by this object:(翻譯:要反映的字段由下面的算法確定。設(shè) C 為此對象所表示的類:)
If C declares a public field with the name specified, that is the field to be reflected.(翻譯:如果 C 聲明一個(gè)帶有指定名的公共字段,則它就是要反映的字段。)
If no field was found in step 1 above, this algorithm is applied recursively to each direct superinterface of C. The direct superinterfaces are searched in the order they were declared.(翻譯:如果在第 1 步中沒有找到任何字段,則該算法被遞歸地應(yīng)用于 C 的每一個(gè)直接超接口。直接超接口按其聲明順序進(jìn)行搜索)
If no field was found in steps 1 and 2 above, and C has a superclass S, then this algorithm is invoked recursively upon S. If C has no superclass, then a NoSuchFieldException is thrown.(翻譯:如果在第 1、2 兩步?jīng)]有找到任何字段,且 C 有一個(gè)超類 S,則在 S 上遞歸調(diào)用該算法。如果 C 沒有超類,則拋出 NoSuchFieldException。)
See The Java Language Specification, sections 8.2 and 8.3. (翻譯:請參閱《Java Language Specification》的第 8.2 和 8.3 節(jié)。 )
public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException
Returns a Field object that reflects the specified declared field of the class or interface represented by this Class object. The name parameter is a String that specifies the simple name of the desired field. Note that this method will not reflect the length field of an array class.(翻譯:返回一個(gè) Field 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明字段。name 參數(shù)是一個(gè) String,它指定所需字段的簡稱。注意,此方法不反映數(shù)組類的 length 字段。)
從上面的jdk api我們知道,要得到name屬性對應(yīng)的Field對象,我們只能調(diào)用Class的getDeclaredField方法,因?yàn)間etField方法只能得到一個(gè)類的public的屬性對應(yīng)的Field對象,而這里的name屬性是private的。我們通過Class的 getDeclaredField方法得到name屬性對應(yīng)的Field對象后,我們就可以調(diào)用Field對象的set(對象,屬性值)方法給name屬性賦值。由于name是private的,我們要想知道我們到底改變了name的值沒有,我們可以通過Field類的父類的 setAccessible(boolean flag)方法來壓制java語言的訪問限制。對于setAccessible方法,我們看jdk文檔:
public void setAccessible(boolean flag)throws SecurityException
Set the accessible flag for this object to the indicated boolean value. A value of true indicates that the reflected object should suppress Java language access checking when it is used. A value of false indicates that the reflected object should enforce Java language access checks.(翻譯:將此對象的 accessible 標(biāo)志設(shè)置為指示的布爾值。值為 true 則指示反射的對象在使用時(shí)應(yīng)該取消 Java 語言訪問檢查。值為 false 則指示反射的對象應(yīng)該實(shí)施 Java 語言訪問檢查。 )
First, if there is a security manager, its checkPermission method is called with a ReflectPermission("suppressAccessChecks") permission. (翻譯:首先,如果存在安全管理器,則在 ReflectPermission("suppressAccessChecks") 權(quán)限下調(diào)用 checkPermission 方法。)
A SecurityException is raised if flag is true but accessibility of this object may not be changed (for example, if this element object is a Constructor object for the class Class). (翻譯:如果 flag 為 true,并且不能更改此對象的可訪問性(例如,如果此元素對象是 Class 類的 Constructor 對象),則會(huì)引發(fā) SecurityException。)
A SecurityException is raised if this object is a Constructor object for the class java.lang.Class, and flag is true. (翻譯:如果此對象是 java.lang.Class 類的 Constructor 對象,并且 flag 為 true,則會(huì)引發(fā) SecurityException。)
從jdk文檔,我們可以通過setAccessible方法將其設(shè)置為true,這樣我們就可以去訪問name屬性了。
實(shí)現(xiàn)代碼如下:
Java代碼
- public class ReadOnlyClassByReflection {
- public static void main(String[] args)throws Exception {
- ReadOnlyClass pt = new ReadOnlyClass();
- Class<?> clazz = ReadOnlyClass.class;
- Field field = clazz.getDeclaredField("name");
- field.setAccessible(true);
- field.set(pt, "world");
- System.out.println(pt.getName());
- }
- }
- public class ReadOnlyClassByReflection {
- public static void main(String[] args)throws Exception {
- ReadOnlyClass pt = new ReadOnlyClass();
- Class<?> clazz = ReadOnlyClass.class;
- Field field = clazz.getDeclaredField("name");
- field.setAccessible(true);
- field.set(pt, "world");
- System.out.println(pt.getName());
- }
public class ReadOnlyClassByReflection {public static void main(String[] args)throws Exception {ReadOnlyClass pt = new ReadOnlyClass();Class<?> clazz = ReadOnlyClass.class;Field field = clazz.getDeclaredField("name");field.setAccessible(true);field.set(pt, "world");System.out.println(pt.getName());}}public class ReadOnlyClassByReflection {public static void main(String[] args)throws Exception {ReadOnlyClass pt = new ReadOnlyClass();Class<?> clazz = ReadOnlyClass.class;Field field = clazz.getDeclaredField("name");field.setAccessible(true);field.set(pt, "world");System.out.println(pt.getName());}
}總結(jié):對于一個(gè)類,它只有唯一的一個(gè)Class對象,它來標(biāo)識(shí)這個(gè)對象。這個(gè)Class對象就能夠獲得這個(gè)類的結(jié)構(gòu)上的特征。那么通過class對象就可以來獲得這個(gè)類相應(yīng)的構(gòu)造方法,屬性等。
獲得某一個(gè)類它的class對象有4種方式:
1、使用類的.class語法
2、通過類的對象的getClass()方法。getClass()方法在Object類里面定義的。
3、通過Class對象的forName()方法
4、對于包裝類,可以通過.TYPE語法方式
通過類的反射機(jī)制,我們可以去改變只讀的private的屬性的值。