偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Java安全 | 反射看這一篇就夠了

開發(fā) 前端
我們可以將Java這種靜態(tài)語?附加上動(dòng)態(tài)特性,換句話說,就是反射機(jī)制是Java實(shí)現(xiàn)動(dòng)態(tài)語言的關(guān)鍵,也就是通過反射實(shí)現(xiàn)類動(dòng)態(tài)加載。怎么理解呢,就是在運(yùn)行時(shí)加載需要的類,如果運(yùn)行時(shí)不用該類,則不報(bào)錯(cuò),降低了依賴性。

什么是反射?

Java安全可以從反序列化漏洞說起,反序列化漏洞又可以從反射說起。反射是大多數(shù)語言里都必不可少的組成部分,對(duì)象可以通過反射獲取他的類,類可以通過反射拿到所有?法(包括私有),拿到的?法可以調(diào)?,總之通過“反射”,我們可以將Java這種靜態(tài)語?附加上動(dòng)態(tài)特性??赡苷f完這一兩句話大家還是不知道反射是個(gè)啥玩意,現(xiàn)在為了讓大家容易理解,先為大家提出一個(gè)需求,通過這個(gè)需要來引出反射。需求如下:

根據(jù)配置文件re.properties指定信息,創(chuàng)建對(duì)象并調(diào)用方法。

classfullpath=com.lxflxf.Cat

method=hi

這樣的需求在學(xué)習(xí)框架時(shí)很多,即在通過外部文件配置,在不修改源碼的情況下,來控制程序。

我們使用現(xiàn)有技術(shù)可以做到嗎?咱們可以動(dòng)手寫一下。

首先創(chuàng)建配置文件,寫入上述內(nèi)容,然后創(chuàng)建一個(gè)類,寫入如下內(nèi)容:

public class Cat {

private String name = "小貓";

public void hi(){
System.out.println("hi" + name);
}
}

傳統(tǒng)的方法是不是我們可以先new一個(gè)對(duì)象,然后再調(diào)用它的方法。寫法如下:

Cat cat = new Cat();
cat.hi();

通過傳統(tǒng)方法,確實(shí)可以調(diào)用hi()方法,但是這和我們的需求不一樣,這里我們是要根據(jù)配置文件re.properties指定信息來完成。到了這里,有同學(xué)就說了,咱們可以通過IO流的方式來讀取配置文件的信息。好,咱們用代碼來寫一下。

使用Properties來讀寫配置文件。案例代碼如下:

Properties properties = new Properties();
properties.load(new FileInputStream("src//re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String methodName = properties.get("method").toString();
System.out.println("classfullpath" + classfullpath);
System.out.println("methodName=" + methodName);

運(yùn)行一下,發(fā)現(xiàn)成功讀取到內(nèi)容。

然后需要?jiǎng)?chuàng)建對(duì)象,怎么創(chuàng)建對(duì)象呢?有同學(xué)就說了,咱們可以直接new classfullpath,這樣不就好了嘛?嗯,想法不錯(cuò),下回不要想了。不要忘記了,我們現(xiàn)在的classfullpath可是字符串類型,怎么能去new呢。所以現(xiàn)有技術(shù)是做不到這個(gè)事情的。那么這里就要引入我們要將的重點(diǎn)——反射機(jī)制。

為了能更好的理解反射,這里先寫一個(gè)小案例,然后在去解釋。

第一步、加載類,返回Class類型的對(duì)象cls。

Class cls = Class.forName(classfullpath);

第二步、通過cls得到你加載的類 com.lxflxf.Cat 的對(duì)象實(shí)例。

Object o = cls.newInstance();

可能有同學(xué)會(huì)問,你怎么知道這里拿到的是com.lxflxf.Cat呢,我們可以打印一下來看看,System.out.println(o.getClass())輸出結(jié)果如下:

第三步、通過cls得到你加載的類 com.lxflxf.Cat 的 methodName 的方法對(duì)象,我們可以在反射中,把方法視為對(duì)象。

Method method1 = cls.getMethod(methodName);

最后、通過method1調(diào)用方法、也就是通過方法對(duì)象來實(shí)現(xiàn)調(diào)用方法。

method1.invoke(o);

在這里我們也能發(fā)現(xiàn)反射和傳統(tǒng)方法的區(qū)別了,傳統(tǒng)方法是對(duì)象.方法(),反射中呢,是方法.invoke(對(duì)象)。那我們運(yùn)行一下,看看能否輸出方法里的內(nèi)容呢,如下:

說到這里大家腦海里應(yīng)該也有了反射的概念。其實(shí)反射機(jī)制還有一個(gè)優(yōu)點(diǎn),那就是可以通過外部文件配置,在不修改源碼的情況下,來控制程序。比如這里,我在Cat類下面再寫一個(gè)方法,cry()方法,代碼如下:

public void cry(){
System.out.println(name + "......喵喵喵");
}

如果我們使用傳統(tǒng)方法,要調(diào)用這個(gè)方法,是不是就要修改代碼了,比如cat.cry();這樣的,那通過反射,我們只需要修改配置文件就可以了,在配置文件re.properties中,將method=hi改為method=cry,就可以了。

運(yùn)行,發(fā)現(xiàn)成功調(diào)用并輸出了內(nèi)容,實(shí)現(xiàn)了改配置文件,不改代碼,完成了解藕。

反射機(jī)制

上文中,通過一個(gè)小案例來簡(jiǎn)單的了解了一下反射,現(xiàn)在來系統(tǒng)的說一下。**反射機(jī)制允許程序在執(zhí)行期借助于ReflectionAPI取得任何類的內(nèi)部信息(比如成員變量、構(gòu)造器、成員方法等等),并能操作對(duì)象的屬性及方法。**加載完類后,在堆中就產(chǎn)生了一個(gè)Class類型的對(duì)象(一個(gè)類只有一個(gè)Class對(duì)象),這個(gè)對(duì)象包含了類的完整結(jié)構(gòu)信息。通過這個(gè)對(duì)象得到類的結(jié)構(gòu)。為了便于理解,在這里為大家畫一下Java反射機(jī)制原理示意圖。如下:

然后現(xiàn)在做一個(gè)小小的總結(jié),Java反射機(jī)制可以完成:

  • 在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類。
  • 在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象。
  • 在運(yùn)行時(shí)得到任意一個(gè)類所具有的成員變量和方法。
  • 在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的成員變量和方法
  • 生成動(dòng)態(tài)代理。

反射相關(guān)的主要類如下:

  • Java.long.Class:代表一個(gè)類,Class對(duì)象表示某個(gè)類加載后在堆中的對(duì)象。
  • Java.lang.reflect.Method:代表類的方法。
  • Java.lang.reflect.Field:代表類的成員變量
  • Java.lang.reflect.Constructor:代表類的構(gòu)造方法。

上文的案例代碼中,我們使用了Method和Class相關(guān)的方法,現(xiàn)在演示一下,通過Field來拿到成員變量,代碼如下:

Field name = cls.getField("name");
System.out.println(name.get(o));

發(fā)現(xiàn)成功拿到了成員變量的值:

Class類分析

接下來對(duì)Class類特點(diǎn)進(jìn)行一下梳理。先看看Class類圖:

我們發(fā)現(xiàn)它的父類仍然是Object。

然后第二點(diǎn)是,Class類對(duì)象不是new出來的,而是系統(tǒng)創(chuàng)建的。這里怎么理解呢,還記得上面咱們畫的原理圖嗎?Class類是由loadClass()方法完成類加載,生成了某個(gè)類對(duì)應(yīng)的Class類對(duì)象?,F(xiàn)在為大家演示一下。寫如下案例代碼:

Class<?> aClass = Class.forName("com.lxflxf.Cat");

然后這這句代碼的前面下一個(gè)斷點(diǎn),進(jìn)行調(diào)試。成功進(jìn)入ClassLoader類中,到了loadClass()方法。如下:

接下來說第三點(diǎn),對(duì)于某個(gè)類的Class類對(duì)象,在內(nèi)存中只有一份,因?yàn)轭愔患虞d一次?,F(xiàn)在寫一個(gè)小案例來驗(yàn)證一下這個(gè)事情,通過ha shCode來判斷,寫如下幾行代碼:

Class<?> cls1 = Class.forName("com.lxflxf.Cat");
Class<?> cls2 = Class.forName("com.lxflxf.Cat");
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());

執(zhí)行結(jié)果如下圖,值相同。

最后關(guān)于Class類對(duì)象還有兩點(diǎn)說一下,一是每個(gè)類的實(shí)例都會(huì)記得自己是由哪個(gè)Class實(shí)例所生成,二是Class對(duì)象可以完整地得到一個(gè)類的完整結(jié)構(gòu),通過一系列的API。

Class類常用方法

這里通過寫小案例的方式,為大家說說Class類常用方法,首先新建一個(gè)Car類,代碼如下:

public class Car {
public String brand;
public int price;
public String color;
}

然后我要獲取到Car類對(duì)應(yīng)的Class對(duì)象,這里用到的就是forName()方法:

String classAllPath = "com.lxflxf.Car";
//獲取到Car類對(duì)應(yīng)的Class對(duì)象
Class cls = Class.forName(classAllPath);

我們可以輸出一下:

System.out.println(cls);
System.out.println(cls.getClass());

第一個(gè)輸出的是cls對(duì)象,是哪個(gè)類的Class對(duì)象,第二個(gè)輸出的是cls運(yùn)行類型,如下圖:

如果我想要得到包名,可以通過getPackageName()方法,可以通過System.out.println(cls.getPackageName()),輸出內(nèi)容為com.lxflxf。如果想得到類名,可以通過getName()方法。還有一個(gè)很重要的方法,那就是創(chuàng)建對(duì)象實(shí)例:newInstance(),案例如:Object o = cls.newInstance();,這里也需要注意一點(diǎn),在JDK1.9往上,不再使用newInstance()。還可以通過getField()獲取到屬性。還有一寫其他方法,這里就不一一舉例了。列了一個(gè)表格,如下:

前面說了這么多,那哪些類型有Class對(duì)象呢?如下列表:

外部類,成員內(nèi)部類,靜態(tài)內(nèi)部類,局部?jī)?nèi)部類,匿名內(nèi)部類

interface:接口

數(shù)組

enum: 枚舉

annotation: 注解

基本數(shù)據(jù)類型

void

案例代碼如下:

Class<String> cls1 = String.class;  //外部類
Class<Serializable> cls2 = Serializable.class; //接口
Class<Integer[]> cls3 = Integer[].class; //數(shù)組
Class<Deprecated> cls4 = Deprecated.class; //注解
System.out.println(cls1);
System.out.println(cls2);
System.out.println(cls3);
System.out.println(cls4);

輸出結(jié)果如下:

動(dòng)態(tài)加載

在文章最開始,就說了一下,通過“反射”,我們可以將Java這種靜態(tài)語?附加上動(dòng)態(tài)特性,換句話說,就是反射機(jī)制是Java實(shí)現(xiàn)動(dòng)態(tài)語言的關(guān)鍵,也就是通過反射實(shí)現(xiàn)類動(dòng)態(tài)加載。怎么理解呢,就是在運(yùn)行時(shí)加載需要的類,如果運(yùn)行時(shí)不用該類,則不報(bào)錯(cuò),降低了依賴性。

舉個(gè)例子吧!

新建一個(gè)Java文件,命名為ClassLoad,寫入如下代碼:

Scanner scanner = new Scanner(System.in);
System.out.println("請(qǐng)輸入數(shù)字");
String key = scanner.next();
switch (key){
case "1":
System.out.println("我等于1");
case "2":
Class<?> cls = Class.forName("Person");
Object o = cls.newInstance();
Method m = cls.getMethod("hi");
m.invoke(o);
System.out.println("ok!");
break;

}

這里,我沒有寫Person類,但是程序編譯的時(shí)候是不會(huì)報(bào)錯(cuò)的。也就是說,等到程序執(zhí)行到case "2",里面時(shí)才會(huì)發(fā)生報(bào)錯(cuò),也就是上文中提到的**在運(yùn)行時(shí)加載需要的類,如果運(yùn)行時(shí)不用該類,則不報(bào)錯(cuò),這就是動(dòng)態(tài)加載。**我們現(xiàn)在來運(yùn)行看一眼。先輸入1程序正常,然后輸入2報(bào)錯(cuò)。

現(xiàn)在是不是理解了動(dòng)態(tài)加載了呢。

類加載

可能還有一些同學(xué)想要了解,比如,類加載過程到底是怎么樣的呢?其實(shí)類加載大體分為三個(gè)階段(加載階段(Loading)、鏈接階段(驗(yàn)證、準(zhǔn)備、解析)、初始化階段(initalization)),這里畫一張圖來便于理解。

具體的這個(gè)內(nèi)容咱們后續(xù)再說,現(xiàn)在這里就不做探討了。

責(zé)任編輯:武曉燕 來源: FreeBuf.COM
相關(guān)推薦

2022-08-01 11:33:09

用戶分析標(biāo)簽策略

2021-04-08 07:37:39

隊(duì)列數(shù)據(jù)結(jié)構(gòu)算法

2023-09-11 08:13:03

分布式跟蹤工具

2020-07-03 08:21:57

Java集合框架

2020-02-18 16:20:03

Redis ANSI C語言日志型

2023-02-10 09:04:27

2022-06-20 09:01:23

Git插件項(xiàng)目

2020-07-06 08:06:00

Java模塊系統(tǒng)

2017-03-11 22:19:09

深度學(xué)習(xí)

2023-11-18 09:30:42

模型AI

2019-05-14 09:31:16

架構(gòu)整潔軟件編程范式

2023-10-17 08:15:28

API前后端分離

2018-05-22 08:24:50

PythonPyMongoMongoDB

2024-09-23 08:00:00

消息隊(duì)列MQ分布式系統(tǒng)

2019-04-02 10:51:29

瀏覽器緩存前端

2017-03-13 09:50:46

Python裝飾器

2019-12-31 09:56:16

Linux 系統(tǒng) 數(shù)據(jù)

2019-09-05 08:14:44

Puppet部署結(jié)構(gòu)

2019-04-10 10:43:15

Redis內(nèi)存淘汰策略

2022-05-19 08:28:19

索引數(shù)據(jù)庫
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)