什么是反思?Java安全可以从反序列化漏洞入手,反序列化漏洞可以从反射入手。反射是大多数语言的重要组成部分。对象可以通过反射获得自己的类,类可以通过反射获得所有方法(包括私有方法)。可以调用获得的方法。简而言之,通过“反射”,我们可以为Java这样的静态语言添加动态特性。可能看了这两句话,你还是不知道什么是反射。现在为了方便大家理解,我先给大家提出一个需求,并通过这个需求来引发反思。需求如下:根据配置文件re.properties中的指定信息,创建对象并调用方法。classfullpath=com.lxflxf.Catmethod=hi学习框架的时候要求很多,就是在不修改源码的情况下,通过外部文件配置来控制程序。我们可以使用现有技术来做吗?让我们手写吧。先创建一个配置文件,写入上面的内容,然后创建一个类,写入下面的内容:publicclassCat{privateStringname="kitten";publicvoidhi(){System.out.println("hi"+name);}}传统的方法是我们可以先new一个对象,然后调用它的方法。写法如下:Catcat=newCat();cat.hi();通过传统方式,确实可以调用hi()方法,但这和我们的需求不一样。这里需要我们根据配置文件re.properties指定信息来完成。至此,有同学说我们可以通过IO流读取配置文件的信息。好吧,让我们用代码来写吧。使用Properties读写配置文件。案例代码如下:Propertiesproperties=newProperties();properties.load(newFileInputStream("src//re.properties"));Stringclassfullpath=properties.get("classfullpath").toString();StringmethodName=properties.get("方法").toString();System.out.println("类全路径"+类全路径);System.out.println("方法名="+方法名);运行,发现内容读取成功。那么我们需要创建对象,如何创建对象呢?有同学说,我们直接createnewclassfullpath不就可以了吗?嗯,这个主意不错,下次别想了。别忘了,我们现在的classfullpath是string类型的,怎么去new。所以现有的技术做不到这一点。那么这里就介绍一下我们要重点关注的重点——反射机制。为了更好的理解反射,这里先做一个小案例,再进行解释。第一步是加载类,返回Class类型的对象cls。类cls=Class.forName(classfullpath);第二步,通过cls获取你加载的类com.lxflxf.Cat的对象实例。对象o=cls.newInstance();有的同学可能会问,你怎么知道这里拿到的是com.lxflxf.Cat,我们打印出来看看,System.out.println(o.getClass())输出结果如下:第三步,通过cls获取你加载的类com.lxflxf.Cat的methodName的方法对象,我们可以把这个方法看做是反射中的一个对象。方法method1=cls.getMethod(methodName);最后通过method1调用方法,即通过方法对象调用方法。方法1.调用(o);在这里我们也可以发现反射和传统方法的区别。传统的方法是对象。method(),而在反射中,就是method。调用(对象)。那我们运行一下,看看能不能把方法里的内容输出出来,如下:说到这里,大家脑海里应该都有反射的概念了。其实反射机制还有一个好处,就是可以在不修改源代码的情况下,通过外部文件配置来控制程序。比如这里,我在Cat类下写了另外一个方法cry()方法,代码如下:publicvoidcry(){System.out.println(name+"...喵喵喵");如果我们使用传统的方法。要调用这个方法,我们是不是要修改代码,比如cat.cry();这样,通过反射,我们只需要修改配置文件即可。在配置文件re.properties中,把method=hi改成method=cry,就可以了。运行后发现调用成功并输出了内容,在不改代码的情况下改了配置文件,就完成了对藕的理解。反射机制上面通过一个小案例简单了解了反射,现在系统的讲一下。**反射机制允许程序在执行过程中通过ReflectionAPI获取任何类的内部信息(如成员变量、构造函数、成员方法等),并可以操作对象的属性和方法。**类加载后,会在堆中生成一个Class类型的对象(一个类只有一个Class对象),其中包含了该类的完整结构信息。通过这个对象获取类的结构。为了方便理解,这里贴一张Java反射机制的原理示意图。如下:那么现在做一个小总结,Java的反射机制可以完成:在运行时判断任意一个对象属于哪个类。在运行时构造任何类的对象。在运行时获取任何类的成员变量和方法。在运行时调用任何对象的成员变量和方法。生成动态代理。与反射相关的主要类如下:java.long.Class:表示一个类,Class对象表示某个类加载后堆中的对象。java.lang.reflect.Method:表示类的方法。java.lang.reflect.Field:表示类的成员变量。java.lang.reflect.Constructor:表示类的构造方法。在上面的案例代码中,我们使用了与Method和Class相关的方法。下面我们来演示一下通过Field获取成员变量。代码如下:Fieldname=cls.getField("name");System.out.println(name.get(o));发现成功获取了成员变量的值:Class类分析接下来我们来梳理一下Class类的特点。先看Class类图:我们发现它的父类还是Object。那么第二点,Class类对象并不是新建的,而是系统创建的。这里怎么理解?大家还记得我们上面画的示意图吗?Class类通过loadClass()方法加载,生成某个类对应的Class类对象。现在给大家演示一下。编写如下案例代码:Class>aClass=Class.forName("com.lxflxf.Cat");然后在这段代码前面打断点调试。成功进入ClassLoader类,到达loadClass()方法。如下:接下来,第三点就是某个类的Class对象在内存中只有一份,因为该类只加载了一次。现在写一个小案例来验证这件事,通过hashCode判断,写下下面几行代码:Class>cls1=Class.forName("com.lxflxf.Cat");Class>cls2=Class.forName("com.lxflxf.Cat");System.out.println(cls1.hashCode());System.out.println(cls2.hashCode());执行结果如下图,数值相同。最后,关于Class对象有两点。首先,每个类实例都会记住它是从哪个类实例生成的。其次,Class对象可以通过一系列的API,完全获取一个类的完整结构。Class类的常用方法这里通过写一个小案例,给大家讲一下Class类的常用方法。首先,使用以下代码创建一个新的Car类:publicclassCar{publicStringbrand;公开价格;publicStringcolor;}然后我要获取Car类对应的Class对象,这里使用了forName()方法:StringclassAllPath="com.lxflxf.Car";//获取Car类对应的Class对象类cls=Class.forName(classAllPath);我们可以输出:System.out.println(cls);System.out.println(cls.getClass());第一个输出是cls对象,哪个类是Class对象,第二个输出是cls运行类型,如下图:如果我想获取包名,可以使用getPackageName()方法或者System.out.println(cls.getPackageName()),输出com.lxflxf。如果要获取类名,可以使用getName()方法。还有一个很重要的方法,就是创建一个对象实例:newInstance(),例如:Objecto=cls.newInstance();,这里还需要注意,在JDK1.9及以上,newInstance()不再使用。也可以通过getField()获取属性。还有其他的写法,这里就不一一举例了。罗列一张表如下:前面说了那么多,有哪些类型有Class对象呢?列表如下:外部类、成员内部类、静态内部类、局部内部类、匿名内部类接口:接口数组枚举:枚举注解:注解基本数据类型voidcase代码如下:Class
