編程中直接用到反射的地方很少坑填,不過研究Spring、mybatis等框架會發(fā)現弛姜,反射是實現這些框架的基礎脐瑰。讓我們一塊看下反射和它的應用吧。
1廷臼、什么是反射
2苍在、class加載有哪些方法
3绝页、通過反射可以獲取到類的那些東西
一、反射的定義
反射機制是指在程序的運行狀態(tài)中寂恬,可以構造任意類的對象续誉,了解任意對象所屬的類,了解任意類的成員變量和方法初肉,可以調用任意對象的屬性和方法酷鸦。
即,對類和實例化對象可以動態(tài)進行以下操作:
1牙咏、通過類構造實例化對象
2臼隔、通過類讀取類包含的屬性方法
3、通過實例化對象獲取類的信息
4妄壶、通過實例化對象操作對象的所有屬性和方法
這么方便的操作摔握,為什么在日常編碼中很少直接使用呢,反射有如下問題:
1丁寄、性能差氨淌,反射是一種解釋性操作,直接通知JVM進行的操作伊磺,無法進行代碼優(yōu)化盛正,比直接使用類和實例化對象要慢的多。
2屑埋、安全問題蛮艰,通過反射可以執(zhí)行實例化對象所有的屬性方法,包括private的方法雀彼,會引起一些安全錯誤
3壤蚜、可讀維護性差,使用反射相對直接調用徊哑,不能直觀的看到調用的屬性方法袜刷,出問題了,代碼的可讀莺丑、調試和維護性會很差
4著蟹、抽象性,對象改變了梢莽,通過反射邏輯可能無法感知萧豆,引起一些莫名的錯誤
二、Class的獲取
反射的第一步需要獲取Class對象昏名,怎么獲取呢
首先構建一個pojo對象Person作為例子
@Slf4j
public class Person {
static {
log.info("I am person");
}
public Person(String name){
this.name=name;
}
public Person(){}
public int id;
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Mg
public void showMessage() {
log.info("this person message");
}
}
1涮雷、實例化對象獲取Class對象
Personperson=newPerson();Classcl1=person.getClass();
2、類獲取Class對象
Classcl2=Person.class;
3轻局、使用class.forName根據類的全名獲取
Classcl3=Class.forName("com.mg.empty.demo.mg20200531.Person");
4洪鸭、使用類加載器獲取
Class cl4 = ClassLoader.getSystemClassLoader().loadClass("com.mg.empty.demo.mg20200531.Person");
附:forName和loadClass的區(qū)別
類的加載過程包括以下步驟:加載样刷、連接(連接過程暫不細分)、初始化
forName默認是進行到初始化览爵,loadClass進行到連接這一步
即置鼻,loadClass只把類加載到了jvm中,forName加載并完成了初始化
這個怎么來證明一下呢
在Person這個類中有一塊代碼
static{System.out.println("i am person");}
這個是在初始化階段執(zhí)行的蜓竹,試下兩個方法是否有這個日志打印
如果forName不想走初始化過程箕母,可以使用public static Class forName(String name, boolean initialize, ClassLoader loader) 這個方法加載,將initialize設置為false即可
三俱济、反射的使用
1嘶是、構造函數的獲取和使用
首先獲取構造函數,代碼如下
Classcl=Person.class;
StringpStr="";
log.info("------------獲取所有構造函數----------------");
Constructor[]cs=cl.getConstructors();
for(Constructorc:cs){
Parameter[]ps=c.getParameters();
pStr="";
for(Parameterp:ps){
pStr+=p.getType().getSimpleName()+" "+p.getName()+",";
}
log.info(String.format("%s (%s)",c.getName(),pStr));
}
輸出結果為
com.mg.empty.demo.Person(Stringname,)
com.mg.empty.demo.Person()
獲取指定構造函數
log.info("------------獲取指定構造函數----------------");
Constructor sc = cl.getConstructor(String.class);
Parameter[] ps = sc.getParameters();
pStr = "";
for (Parameter p : ps) {
pStr += p.getType().getSimpleName() + " " + p.getName() + ",";
}
log.info(String.format("%s (%s)", sc.getName(), pStr));
使用構造函數實例化對象
log.info("------------使用構造函數----------------");
Constructor sc1 = cl.getConstructor(String.class);
Person person1 = (Person) sc1.newInstance("mg");
log.info(person1.getName());
2姨蝴、屬性的獲取和使用
獲取屬性的方法有兩個getFields只能獲取public修飾的屬性,如下
log.info("------------獲取public屬性----------------");
Field[] fs = cl.getFields();
for (Field f : fs) {
log.info(String.format("%s %s", f.getType().getSimpleName(), f.getName()));
}
獲取所有屬性的方法是getDeclaredFields
log.info("------------獲取所有屬性----------------");
Field[] fs = cl.getDeclaredFields();
for(Field f : fs)
{
log.info(String.format("%s %s",f.getType().getSimpleName(),f.getName()));
}
獲取指定名稱的屬性
log.info("------------獲取指定屬性----------------");
Field field = cl.getDeclaredField("name");
log.info(String.format("%s %s",field.getType().getSimpleName(),field.getName()));
獲取和修改屬性值
log.info("------------獲取和修改屬性值----------------");
Person person1 = new Person();
person1.setName("mg");
Field field = cl.getDeclaredField("name");
field.setAccessible(true);
log.info(field.get(person1).toString());
field.set(person1,"mg2020");
log.info(field.get(person1).toString());
注意 private的屬性需要設置下field.setAccessible(true);否則拋異常肺缕。
3左医、方法的獲取和使用
獲取包括父類的所有方法
log.info("------------獲取包括父類的所有方法----------------");
Method[] ms = cl.getMethods();
for (Method m : ms) {
Parameter[] ps = m.getParameters();
pStr = "";
for (Parameter p : ps) {
pStr += p.getType().getSimpleName() + " " + p.getName() + ",";
}
log.info(String.format("%s (%s)", m.getName(), pStr));
}
獲取本類中定義的方法
log.info("------------獲取所有方法----------------");
Method[] ms = cl.getDeclaredMethods();
for (Method m : ms) {
Parameter[] ps = m.getParameters();
pStr = "";
for (Parameter p : ps) {
pStr += p.getType().getSimpleName() + " " + p.getName() + ",";
}
log.info(String.format("%s (%s)", m.getName(), pStr));
}
獲取指定的方法
log.info("------------獲取指定方法----------------");
Method method = cl.getMethod("setName",String.class);
Parameter[] ps = method.getParameters();
pStr = "";
for (Parameter p : ps) {
pStr += p.getType().getSimpleName() + " " + p.getName() + ",";
}
log.info(String.format("%s (%s)", method.getName(), pStr));
使用方法
{
log.info("------------使用方法----------------");
Person person1 = new Person();
Method method = cl.getMethod("setName",String.class);
method.invoke(person1,"mg");
log.info(person1.getName());
}
4、注解的獲取和使用
先定義一個注解
{
log.info("------------使用方法----------------");
Person person1 = new Person();
Method method = cl.getMethod("setName",String.class);
method.invoke(person1,"mg");
log.info(person1.getName());
}
接下來獲取注解屬性
log.info("------------獲取注解----------------");
Method method = cl.getDeclaredMethod("showMessage");
Mg annotation = method.getAnnotation(Mg.class);
log.info(annotation.name());
反射到這里就結束了同木,雖然反射是一個很簡單的技術浮梢,并且日常編碼中不經常使用,不過在框架中是很重要的存在彤路,所以還是需要熟練掌握的秕硝。
文中代碼提取 關注公眾號 MG驛站 輸入 反射 獲取