一、反射的引入
1.编译时知道类或对象的具体信息,此时直接对类和对象进行操作即可,无需反射
Student stu2 = new Student(); stu2.setAge(stu1.getAge()); System.out.println(stu2.getAge());
2.如果编译不知道类或对象的具体信息,此时应该如何做呢?使用反射来实现
比如类的名称放在XML文件中,属性和属性值放在XML文件中,需要在运行时读取XML文件,动态获取类的信息
Class clazz = Class.forName("com.uwo9.reflection.Student"); Object obj = clazz.newInstance();
3.反射的应用场合
在编译时根本无法知道该对象或类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息
4.反射的作用
通过反射可以使程序代码访问装载到JVM 中的类的内部信息
获取已装载类的属性信息
获取已装载类的方法
获取已装载类的构造方法信息
5.在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中
Class类:代表一个类
Constructor 类:代表类的构造方法
Field 类:代表类的成员变量(属性)
Method类:代表类的成员方法
二、反射技术的入口类
1.Class类是Java 反射机制的起源和入口
用于获取与类相关的各种信息
提供了获取类信息的相关方法
Class类继承自Object类
2.Class类是所有类的共同的图纸
每个类有自己的对象,好比图纸和实物的关系
每个类也可看做是一个对象,有共同的图纸Class,存放类的结构信息,能够通过相应方法取出相应信息
类的名字
属性
方法
构造方法
父类和接口
3.Class类常用方法
getFields() 获得类的public类型的属性。
getDeclaredFields()获得类的所有属性
getField(String name)获得类的指定属性
getMethods()获得类的public类型的方法
getMethod (String name,Class [] args)获得类的指定方法
getConstrutors()获得类的public类型的构造方法
getConstrutor(Class[] args)获得类的特定构造方法
newInstance()通过类的无参构造方法创建一个对象
getName()获得类的完整名字
getPackage()获取此类所属的包
getSuperclass()获得此类的父类对应的Class对象
4.获取类的Class信息
Class.forName()
类名.class
对象名.getClass()
对象名.getSuperClass()
包装类.TYPE
三、使用反射技术创建对象
方法1:通过Class的newInstance()方法
该方法要求该Class对象的对应类有无参构造方法
执行newInstance()实际上就是执行无参构造方法来创建该类的实例
Class clazz=Class.forName("com.uwo9.reflection.Student"); Object obj=clazz.newInstance(); //相当于执行语句: Student stu = new Student();
方法2:通过Constructor的newInstance()方法
先使用Class对象获取指定的Constructor对象
再调用Constructor对象的newInstance()创建Class对象对应类的对象
通过该方法可选择使用指定构造方法来创建对象
Class clazz = Class.forName("com.uwo9.reflection.Student"); Constructor cons = clazz.getConstructor(String.class,int.class, float.class ); Object obj = cons.newInstance( "lkl", 32, 56.5f ); //相当于执行语句: Student stu=new Student("lkl",32,56.5f); obj = clazz.getConstructor().newInstance(); //相当于执行语句: Student stu = new Student();
四、使用反射技术修改查询属性值
通过Class对象的getFields()或者getField()方法可以获得该类所包括的全部Field属性或指定Field属性。Field类提供了以下方法来访问属性
getXxx(Object obj):获取obj对象该Field的属性值。此处的Xxx对应8个基本数据类型,如果该属性类型是引用类型则直接使用get(Object obj)
setXxx(Object obj,Xxx val):将obj对象的该Field赋值val。此处的Xxx对应8个基本数据类型,如果该属性类型是引用类型则直接使用set(Object obj, Object val)
setAccessible(Boolean flag):若flag为true,则取消属性的访问权限控制,即使private属性也可以进行访问
Class clazz = Class.forName("com.uwo9.reflection.Student"); Object obj = clazz.newInstance(); // 调用getDeclaredField("name")方法取得name属性对应的Field对象。 Field f = clazz.getDeclaredField("name"); // 取消属性的访问权限控制,即使private属性也可以进行访问。 f.setAccessible(true); // 调用get()方法取得对应属性值。 System.out.println(f.get(obj)); // 调用set()方法给对应属性赋值。 f.set(obj, "lkl"); // 调用get()方法取得对应属性修改后的值。 System.out.println(f.get(obj));
五、使用反射技术动态调用方法
通过Class对象的getMethods() 方法可以获得该类所包括的全部方法, 返回值是Method[]
通过Class对象的getMethod()方法可以获得该类所包括的指定方法, 返回值是Method
每个Method对象对应一个方法,获得Method对象后,可以调用其invoke() 来调用对应方法
Object invoke(Object obj,Object [] args):obj代表当前方法所属的对象的名字,args代表当前方法的参数列表,返回值Object是当前方法的返回值,即执行当前方法的结果。
// 创建该类的一个对象 Class clazz = InvokeMethod.class; Object obj = clazz.newInstance(); // 调用该对象的add方法 Method amethod = clazz.getMethod("add", new Class[] { int.class,int.class }); Object result = amethod.invoke(obj, new Object[] { 5, 7 }); System.out.println(result); // 调用该对象的shout方法 Method smethod = clazz.getMethod("shout", new Class[] { String.class }); smethod.invoke(obj, new Object[] { "lkl" }); //相当于如下语句 // InvokeMethod im=new InvokeMethod(); // int sum=im.add(5, 7); // System.out.println(sum); // im.shout("lkl");
六、反射技术优缺点
反射提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类
反射是其它一些常用语言,如C、C++、Fortran 或者Pascal等都不具备的
Java反射技术应用领域很广,如软件测试、 EJB、JavaBean等
许多流行的开源框架例如Struts、Hibernate、Spring在实现过程中都采用了该技术
反射的缺点
性能问题
使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此Java反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用。
使用反射会模糊程序内部逻辑
程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
七、反射技术的典型应用
根据XML信息动态创建对象和调用方法
简化JDBC SELECT操作,直接返回List
八、其他
1.反射作用:
1.动态加载类信息,创建对象,设置属性,执行方法。
2.设置/获取私有属性。
3.获取Class对象时会将类的结构信息加载到JVM并且执行static代码块。
2.反射和new对象区别:
new属于静态编译,而反射属于动态编译
3.静态编译和动态编译区别:
静态编译就是在编译的时候把你所有的模块都编译进exe(可执行程序(executable program,EXE File))里去,当你启动这个exe的时候所有模块都加载进来了。你写小程序没问题,但程序一大,加载的过程(就是当你运行程序时初始化的过程)就比较费力了。。大多数ppc( PPC(Pay-Per-Click)搜索引擎)的硬件配置还是很一般的。
动态编译就不一样了,你编译的时候那些模块都没有编译进去,一般情况下你可以把那些模块都编译成DLL,这样你启动程序(初始化)的时候这些模块不会被加载,而是在运行的时候,用到那个模块就调用哪个模块。
(DLL是Dynamic Link Library的缩写,意为动态链接库。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用)
简单的打个比方,我写个阅读器,支持txt,pdf,udm三种格式,暂时把读txt,读pdf,读udm定义为三个功能模块。 使用静态编译:我想看个txt,点击应用程序图标以后三个功能都加载进来了,判断格式,使用读txt模块。(在这里,另外两个模块的作用就是占用系统资源) 使用动态编译:我想看个txt,点击应用程序,判断格式,只加载读txt模块,使用读txt模块。
显然,动态编译1速度快,2节省了系统资源,3利于今后拓展。
解释2 静态编译的话 exe文件运行的时候不会用到别的文件 动态编译 exe文件就要用到别的文件了
解释3 一个是租自行车,什么时候骑什么时候租,有新车就租新车,没新车就租旧车,有电动车就省点劲。 一个是自己买个自行车天天扛着,进电梯上地铁也扛着。
解释4 用静态编译,相当于你带着一个工具包到处跑(遇到有需要的地方不需要周围的环境提供相应的工具,自己用自己工具包的工具就行了,所以当环境发生变化可以尽可能的无视),当然,你本来不带任何东西走到哪是哪和工具包随身带的区别显然就是重量增加了,即程序的体积可能会比另一种方式来的大一点,看你的工具包有多大。