Java基礎篇-反射

編程中直接用到反射的地方很少坑填,不過研究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驛站 輸入 反射 獲取

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市洲尊,隨后出現的幾起案子远豺,更是在濱河造成了極大的恐慌,老刑警劉巖坞嘀,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件躯护,死亡現場離奇詭異,居然都是意外死亡丽涩,警方通過查閱死者的電腦和手機棺滞,發(fā)現死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來矢渊,“玉大人继准,你說我怎么就攤上這事“校” “怎么了移必?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長毡鉴。 經常有香客問我避凝,道長舞萄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任管削,我火速辦了婚禮倒脓,結果婚禮上,老公的妹妹穿的比我還像新娘含思。我一直安慰自己崎弃,他們只是感情好,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布含潘。 她就那樣靜靜地躺著饲做,像睡著了一般。 火紅的嫁衣襯著肌膚如雪遏弱。 梳的紋絲不亂的頭發(fā)上盆均,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天,我揣著相機與錄音漱逸,去河邊找鬼泪姨。 笑死,一個胖子當著我的面吹牛饰抒,可吹牛的內容都是我干的肮砾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼袋坑,長吁一口氣:“原來是場噩夢啊……” “哼仗处!你這毒婦竟也來了?” 一聲冷哼從身側響起枣宫,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤婆誓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后也颤,有當地人在樹林里發(fā)現了一具尸體旷档,經...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年歇拆,在試婚紗的時候發(fā)現自己被綠了鞋屈。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡故觅,死狀恐怖厂庇,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情输吏,我是刑警寧澤权旷,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響拄氯,放射性物質發(fā)生泄漏躲查。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一译柏、第九天 我趴在偏房一處隱蔽的房頂上張望镣煮。 院中可真熱鬧,春花似錦鄙麦、人聲如沸典唇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽介衔。三九已至,卻和暖如春骂因,著一層夾襖步出監(jiān)牢的瞬間炎咖,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工寒波, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留乘盼,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓影所,卻偏偏與公主長得像蹦肴,于是被迫代替她去往敵國和親僚碎。 傳聞我的和親對象是個殘疾皇子猴娩,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355