Java反射獲取private屬性和方法(子類,父類,祖先….)
先來看一個例子:String可變還是不可變?
大家都應該知道,或者聽過,String類是不可變的,爲什麼呢?因爲String其實使用一個
private final char [] value;
來保存字符的;final,private,明顯就不讓你改了啊。但是,大家請看:
String a="abc";
Field valueFieldString=String.class.getDeclaredField("value");
valueFieldString.setAccessible(true);
char[]value=(char[])valueFieldString.get(a);
value[2]='@';
String b="abc";
//a.intern();
System.out.println(a);
System.out.println(b);
System.out.println(a==b);
System.out.println("abc"==b);
System.out.println("ab@"==a);
System.out.println(a.equals("ab@"));
System.out.println(a.equals("abc"));
System.out.println("abc".equals("ab@"));
輸出(猜猜看,和你想的一不一樣呢?):
ab@
ab@
true
true
false//(如果把intern那個註釋去掉的話,就是true)
true
true
true
如果,你都答對了,恭喜你,那請直接跳到 反射用法.
否則:請看下面分析
先摘一段官方intern()的註釋:
* Returns a canonical representation for the string object.
* <p>
* A pool of strings, initially empty, is maintained privately by the
* class {@code String}.
* <p>
* When the intern method is invoked, if the pool already contains a
* string equal to this {@code String} object as determined by
* the {@link #equals(Object)} method, then the string from the pool is
* returned. Otherwise, this {@code String} object is added to the
* pool and a reference to this {@code String} object is returned.
3個點:
* 1.常量池(JVM中就是方法區)是由 String 這個class來私有的管理的。*
* 2.調用intern()時,會查看常量池是否存在這個String對象,這是通過equals()方法來判斷的。*
* 3.如果存在,則返回常量池中那個String對象,否則,加入到常量池,並返回當前對象。*
ps : equals()方法,首先是看obj1==obj2,也就是引用地址是否相等,如果等則返回true,然後是逐個字符比較。
ok,有了以上知識。其實到這裏你自己也可以分析出來了。
我們來分析一下
首先,String a="abc";
這句話在內存(常量池)中放入了一個String對象:“abc”(內存地址假設爲0x123,由char [] value = [‘a’,’b’,’c’]保存),
然後第2~5行,我們先不管語法,只要知道是把“abc”這個字符串的第三個字符’c’替換爲’@’,
此時char [] value = [‘a’,’b’,’c’] 變爲了char [] value = [‘a’,’b’,’@’],String這個class對象管理的還是“abc”這個對象。
接下來 String b="abc"
,這句話會從常量池中尋找“abc”,此時會發現常量池中“abc”是存在的,於是,b就指向了“abc”,b其實就是“abc”的位置的一個引用,a也是一個“abc”位置的引用。
那麼接下來就是順理成章了:
System.out.println(a);//a指向"abc"這個對象,而這個對象,內部存儲卻是{'a','b','@'}
System.out.println(b);//同上
System.out.println(a==b);//都指向0x123
System.out.println("abc"==b);//都是0x123
System.out.println("ab@"==a);//"ab@"這個對象在常量池中沒有,會先在常量中生成,地址顯然不是0x123,所以輸出false,但是,當調用了a.intern()後,會調用String的equals()去常量池中新建一個對象"ab@',並返回這個位置。
System.out.println(a.equals("ab@"));
System.out.println(a.equals("abc"));
System.out.println("abc".equals("ab@"));
如果仔細想想,其實還是有疑問,在這裏就不展開了,不然偏題了,因爲我對底層也不懂,intern是c++實現的,具體看這篇:
反射
反射,RTTI,Class對象,這些就不說了,看一段代碼:
static void getClassFieldAndMethod(Class cur_class) {
String class_name = cur_class.getName();
Field[] obj_fields = cur_class.getDeclaredFields();
for (Field field : obj_fields) {
field.setAccessible(true);
System.out.println(class_name + ":" + field.getName());
}
Method[] methods = cur_class.getDeclaredMethods();
for (Method method : methods) {
method.setAccessible(true);
System.out.println(class_name + ":" + method.getName());
}
if (cur_class.getSuperclass() != null) {
getClassFieldAndMethod(cur_class.getSuperclass());
}
}
這段代碼輸入是一個Class的對象,遞歸輸出這個類的父類,祖父類直到Object類的所有方法和域。
再看一段:
static void getObjField(Object obj) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException{
Class cur_class = obj.getClass();
getClassFieldAndMethod(cur_class);
Field vfield=cur_class.getDeclaredField("value");
vfield.setAccessible(true);
char[]value=(char[])vfield.get(obj);
System.out.println(Arrays.toString(value));
}
ok,這段代碼輸入任意一個對象,調用上面那段代碼的方法getClassFieldAndMethod(),這樣你就知道這個對象都有些什麼域,什麼方法了,再通過域的名稱,就可以獲取當前這個對象的域的值,進而可以修改了!!
是不是很簡單,從此以後誰都不能限制你在Java程序裏面爲所欲爲了!