java上乘武功入門--反射
先來(lái)看一段魔法吧
public class Test {
private static void changeStrValue(String str, char[] value) {
// 只要執(zhí)行魔法代碼就可以達(dá)到下面的效果
// 施展魔法的代碼稍后揭秘
}
public static void main(String[] args) {
changeStrValue("abc", new char[]{'d','e','f'});
String abc = "abc";
System.out.println("abc");
System.out.println(abc);
System.out.println("abc".equals(abc));
}
}

二當(dāng)家的第一次看到這個(gè)執(zhí)行結(jié)果覺(jué)得很有意思。明明應(yīng)該是"abc"怎么就變成了"def"呢?
反射機(jī)制是個(gè)什么玩意兒?
Java的反射(reflection)機(jī)制是指在程序的運(yùn)行狀態(tài)中,可以構(gòu)造任意一個(gè)類的對(duì)象,可以了解任意一個(gè)對(duì)象所屬的類,可以了解任意一個(gè)類的成員變量和方法,可以調(diào)用任意一個(gè)對(duì)象的屬性和方法。這種動(dòng)態(tài)獲取程序信息以及動(dòng)態(tài)調(diào)用對(duì)象的功能稱為Java語(yǔ)言的反射機(jī)制。反射被視為動(dòng)態(tài)語(yǔ)言的關(guān)鍵。
以上就是百科的解釋??赡苡悬c(diǎn)抽象,接著看二當(dāng)家的給你秀起來(lái)解釋一下。
構(gòu)造任意一個(gè)類的對(duì)象
一般情況下,我們?nèi)绻胍獎(jiǎng)?chuàng)建一個(gè)類的對(duì)象,應(yīng)該要用到new關(guān)鍵字。但是像spring這樣的框架,我們只需要配置類名,就可以得到類的實(shí)例。他是怎么做到的呢?
import java.util.List;
public class Test {
/**
* 根據(jù)類名取得類實(shí)例
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
* @param className
* @param <T>
* @return
* @throws InstantiationException
* @throws IllegalAccessException
* @throws ClassNotFoundException
*/
public static <T> T getInstance(String className) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Class<T> clazz = (Class<T>) Class.forName(className);
return clazz.newInstance();
}
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
List<String> list = getInstance("java.util.ArrayList");
list.add("abc");
list.add("def");
for (String v : list) {
System.out.println(v);
}
}
}
類名可以在程序運(yùn)行中從配置文件獲取,甚至是從網(wǎng)絡(luò)獲取,然后動(dòng)態(tài)創(chuàng)建一個(gè)類的實(shí)例。
了解任意一個(gè)對(duì)象所屬的類
import java.util.ArrayList;
public class Test {
/**
* 打印對(duì)象的類名
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
* @param o
*/
public static void printClass(Object o) {
System.out.printf(o.getClass().getName());
}
public static void main(String[] args) {
printClass(new ArrayList<>());
}
}

了解任意一個(gè)類的成員變量和方法
我們一般要使用一個(gè)類,先要知道有什么方法和屬性,先了解,后使用。但是像spring那樣的框架為什么可以為我們自動(dòng)注入呢?他怎么知道我們一個(gè)對(duì)象里有什么屬性呢?
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test {
/**
* 打印類的屬性
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
* @param clazz
*/
public static void printFields(Class clazz) {
System.out.println(clazz.getName() + "包含如下屬性:");
for (Field f : clazz.getDeclaredFields()) {
System.out.println(f);
}
}
/**
* 打印類的方法
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
* @param clazz
*/
public static void printMethods(Class clazz) {
System.out.println(clazz.getName() + "包含如下方法:");
for (Method m : clazz.getDeclaredMethods()) {
System.out.println(m);
}
}
public static void main(String[] args) {
printFields(MyClass.class);
printMethods(MyClass.class);
}
}
class MyClass {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

調(diào)用任意一個(gè)對(duì)象的屬性和方法
像spring這樣的框架,即使一個(gè)屬性是私有屬性并且沒(méi)有set方法,一樣可以注入。
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test {
/**
* 調(diào)用一個(gè)對(duì)象的方法
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
* @param o
* @param methodName
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public static void callMethod(Object o, String methodName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method m = o.getClass().getDeclaredMethod(methodName);
m.setAccessible(true);
m.invoke(o);
}
/**
* 修改一個(gè)對(duì)象的屬性
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
* @param o
* @param fieldName
* @param value
* @throws IllegalAccessException
*/
public static void changeFieldValue(Object o, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
Field f = o.getClass().getDeclaredField(fieldName);
f.setAccessible(true);
f.set(o, value);
}
public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, NoSuchFieldException {
MyClass o = new MyClass();
// 修改任意屬性,即使是私有的
changeFieldValue(o, "name", "二當(dāng)家的白帽子");
// 調(diào)用任意方法,即使是私有的
callMethod(o, "printName");
}
}
class MyClass {
// 私有屬性,只可以調(diào)用set方法修改
private String name;
private void printName() {
// 私有方法,只有本類自己的實(shí)例可以調(diào)用
System.out.println("My name is " + name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

魔法揭秘
是時(shí)候揭秘魔法的真面目了,沒(méi)錯(cuò),也是利用了反射。
import java.lang.reflect.Field;
public class Test {
/**
* 修改字符串內(nèi)部的值
* @author 二當(dāng)家的白帽子 https://le-yi.blog.csdn.net/
* @param str
* @param value
*/
private static void changeStrValue(String str, char[] value) {
try {
Field f = str.getClass().getDeclaredField("value");
f.setAccessible(true);
f.set(str, value);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
changeStrValue("abc", new char[]{'d','e','f'});
// 這里的"abc"字符串和上面調(diào)用changeStrValue的參數(shù)"abc"會(huì)指向同一塊內(nèi)存
String abc = "abc";
System.out.println("abc");
System.out.println(abc);
System.out.println("abc".equals(abc));
}
}
要理解這段代碼,除了反射機(jī)制還需要了解java對(duì)于字符串的處理。"字符串常量池"已經(jīng)超出本文的范圍,是另一個(gè)話題,本文就不多說(shuō)了。
原本字符串內(nèi)容是"abc",我們正常情況下無(wú)法修改這個(gè)內(nèi)容,因?yàn)镾tring是不變類。但是反射大法卻可以打破一切禁忌。
總結(jié)
一般的程序可能用不到寫反射的代碼。但是像spring這樣的框架,如果沒(méi)有反射,我真的想不出如何實(shí)現(xiàn)呢。哪怕永遠(yuǎn)不需要用反射,了解機(jī)制對(duì)我們都有著莫大的好處。
本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
淺談Arrays.asList() 和ArrayList類型區(qū)別
下面小編就為大家?guī)?lái)一篇Arrays.asList() 和ArrayList類型區(qū)別。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10
SpringBoot使用自定義注解實(shí)現(xiàn)權(quán)限攔截的示例
詳解用Eclipse如何創(chuàng)建Web項(xiàng)目
Java簡(jiǎn)單使用EasyExcel操作讀寫excel的步驟與要點(diǎn)

