北京治疗白癜风的医院 http://yyk.39.net/bj/zhuanke/89ac7.html作者
ewenll
责编
夕颜
出品
CSDN博客
Java代码在计算机中经历的阶段:三个阶段Person.java经过javac编译,变为Person.class文件(字节码文件),字节码文件中,主要有类的属性、构造函数、方法,当然还有类的其他信息,这个阶段称为源码阶段,通过类加载器进入内存,在内存中生成一个Class对象,这个阶段为Class类对象阶段,一个类的Class对象中存储了类的全部信息,使用这个类对象的阶段称为Runtime运行时阶段Java识别类和对象信息的两种方式一种是传统的RTTI(Run-TimeTypeIdentification),它假定我们在编译时已经知道了所有的类型信息;另一种是反射机制,它允许我们在运行时发现和使用类的信息。使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)什么是反射?Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象.。反射就是把Java类中的各种成分映射成一个个的Java对象。获取Class对象的方式类名.class:该方法最为安全可靠,程序性能更高。这说明任何一个类都有一个隐含的静态成员变量class。这种方法只适合在编译前就知道操作的Class,多用于传递参数。Class.forName(“类的全路径名”):当知道某类的全路径名时,可以使用此方法获取Class类对象。用的最多,但可能抛出ClassNotFoundException异常,多用于加载配置文件。对象名.getClass():getClass()是Object中的方法,java中所有类都直接或间接继承了Object类,所以都有getClass()方法。
//ewen包下的Person类publicclassPerson{privateStringname;privateintage;publicStringa;protectedStringb;Stringc;privateStringd;//构造函数、get、set方法省略。publicvoidsleep(){System.out.println("sleep....");}}
publicclassReflectTest{publicstaticvoidmain(String[]args)throwsException{//获取Person类的Class对象//通过类名.class获取Classclazz1=Person.class;//通过Class.forName("类的全路径名")Classclazz2=Class.forName("ewen.Person");//通过对象名.getClass()方法获得Personperson=newPerson();Classclazz3=person.getClass();System.out.println(clazz1==clazz2);//trueSystem.out.println(clazz2==clazz3);//true}}一个类只有一个Class实例,无论通过哪种方式获取,得到的都是同一个Class对象。Class对象的功能1)获取类的成员变量Field[]getFields():获取所有public修饰的成员变量,返回一个含有所有public修饰的成员变量对象的数组;FieldgetField(Stringname):获取指定名称的public修饰的成员变量,返回一个成员变量对象。Field[]getDeclaredFields():获取所有的成员变量,不考虑修饰符。FieldgetDeclaredField(Stringname):获取指定名称的成员变量对象,不考虑修饰符。
//获取所有pubic修饰的成员变量Field[]fields=clazz1.getFields();for(Fieldfieldields){System.out.println(field);//publicjava.lang.Stringewen.Person.a,只有a是public修饰的}//获取指定的成员变量Fieldfield=clazz1.getField("a");System.out.println("成员变量a:"+field);/publicjava.lang.Stringewen.Person.a//使用getField()方法获取非public修饰的方法,报NoSuchFieldException/*Fieldfield2=clazz1.getField("b");System.out.println("protected修饰的b"+field2);*///获取所有成员变量Field[]fields2=clazz1.getDeclaredFields();for(Fieldfield1ields2){System.out.println(field1);//所有成员变量}//获取指定成员变量,不考虑修饰符Fieldfield3=clazz1.getDeclaredField("d");System.out.println(field3);/publicjava.lang.Stringewen.Person.d2)获取类的构造方法Constructor?[]getConstructors():获取所有public修饰的构造方法,返回一个含有所有public修饰的构造函数对象的数组。ConstructorgetConstructor(类?…parameterTypes):获取含有指定参数public修饰的构造函数。Constructor?[]getDeclaredConstructors():获取所有构造函数,不考虑修饰符。ConstructorgetDeclaredConstructor(类?…parameterTypes):获取所有构造函数,不考虑修饰符,参数是构造器中参数类型对应的Class对象。
//获取构造函数Constructorcon=clazz1.getDeclaredConstructor(String.class,int.class);System.out.println(con);//privateewen.Person(java.lang.String,int)3)获取类中的方法Method[]getMethods():获取所有public修饰的方法,返回一个含有所有public修饰的方法对象的数组。MethodgetMethod(Stringname,类?…parameterTypes):获取public修饰的含有指定参数的方法。Method[]getDeclaredMethods():获取所有方法,不考虑修饰符MethodgetDeclaredMethod(Stringname,类?…parameterTypes):获取指定方法,不考虑修饰符。4)获取类的全路径名:StringgetName()通过反射操作类中的成员变量、构造函数、方法1)Field:成员变量对象操作:设置成员变量值:field.set(类对象名,value)
//获取指定的成员变量Fieldfield=clazz1.getField("a");System.out.println("成员变量a:"+field);Personperson2=newPerson();//设置值field.set(person2,"aaa");System.out.println("a的值:"+person2.getA());//a的值:aaa获取成员变量值:field.get(类对象名)
//获取指定的成员变量Fieldfield=clazz1.getField("a");System.out.println("成员变量a:"+field);Personperson2=newPerson();//获取person2中的a的值Objectvalue=field.get(person2);System.out.println("a的值:"+value);//a的值为null,String类型的成员变量默认为null。忽略访问权限修饰符的安全检查:field.setAccessible(true)
//获取指定成员变量,不考虑修饰符Fieldfield3=clazz1.getDeclaredField("d");System.out.println(field3);//设置值Personperson1=newPerson();/*忽略访问权限修饰符的安全检查,d是private修饰的,如果不忽略访问权限修饰符的安全检查,会报IllegalAccessException异常。*/field3.setAccessible(true);field3.set(person1,"");System.out.println("d的值:"+person1.getD());//d的值:2)Constructor:构造方法对象创建对象:TnewInstance(Object…initargs),里面为可变参数,变量的值。
/获取构造函数Constructorcon1=clazz1.getConstructor(String.class);//创建对象Objectperson3=con1.newInstance("张三");如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法。如果构造函数是private修饰的,也可以调用setAccessible(true)来忽略访问权限的安全检查。
Objectperson4=clazz1.newInstance();3)Method:方法对象执行方法:Objectinvoke(类对象名,Object…args),参数必须得有类对象,变量参数根据方法参数来定如果方法不是public修饰的,调用method.setAccessible(true)来忽略访问权限的安全检查。
//获取方法对象Methodmethod=clazz1.getMethod("sleep");//执行方法method.invoke(person1);Method调用invoke()的时候,存在许多细节:invoke()方法中第一个参数Object实质上是Method所依附的Class对应的类的实例,如果这个方法是一个静态方法,那么obj为null,后面的可变参数Object对应的自然就是参数。invoke()返回的对象是Object,所以实际上执行的时候要进行强制转换。在对Method调用invoke()的时候,如果方法本身会抛出异常,那么这个异常就会经过包装,由Method统一抛InvocationTargetException。而通过InvocationTargetException.getCause()可以获取真正的异常。获取方法名称StringgetName()案例需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法实现:1.配置文件2.反射步骤:1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中2.在程序中加载读取配置文件3.使用反射技术来加载类文件进内存4.创建对象5.执行方法
pro.properties文件:pro.className=ewen.Personpro.methodName=sleeppublicclassUseReflect{publicstaticvoidmain(String[]args)throwsException{//加载配置文件/*创建Properties对象。Properties是Java中jdk自带的一个对象我们可以直接将后缀为properties的文件变为Properties对象,然后通过Porperties对象中的方法对.properties文件对象进行操作*/Propertiespro=newProperties();//获得配置文件(pro.properties)的字节流InputStreamis=UseReflect.class.getClassLoader().getResourceAsStream("pro.properties");//从输入流中读取属性列表(键和元素对)。pro.load(is);//获取配置文件中定义的数据StringclassName=pro.getProperty("pro.className");StringmethodName=pro.getProperty("pro.methodName");//获取Class对象Classclazz=Class.forName(className);//获取类对象Objectobj=clazz.newInstance();//获取方法对象Methodmethod=clazz.getMethod("sleep");//执行方法method.invoke(obj);}}只需更改配置文件pro.properties文件就可以,在不改变代码的前提下,执行任意类的任意方法。反射的优缺点优点反射机制极大的提高了程序的灵活性和扩展性,降低模块的耦合性,提高自身的适应能力。通过反射机制可以让程序创建和控制任何类的对象,无需提前硬编码目标类。使用反射机制能够在运行时构造一个类的对象、判断一个类所具有的成员变量和方法、调用一个对象的方法。反射机制是构建框架技术的基础所在,使用反射可以避免将代码写死在框架中。正是反射有以上的特征,所以它能动态编译和创建对象,极大的激发了编程语言的灵活性,强化了多态的特性,进一步提升了面向对象编程的抽象能力,因而受到编程界的青睐。缺点尽管反射机制带来了极大的灵活性及方便性,但反射也有缺点。反射机制的功能非常强大,但不能滥用。在能不使用反射完成时,尽量不要使用,原因有以下几点:1.性能问题。Java反射机制中包含了一些动态类型,所以Java虚拟机不能够对这些动态代码进行优化。因此,反射操作的效率要比正常操作效率低很多。我们应该避免在对性能要求很高的程序或经常被执行的代码中使用反射。而且,如何使用反射决定了性能的高低。如果它作为程序中较少运行的部分,性能将不会成为一个问题。2.安全限制使用反射通常需要程序的运行没有安全方面的限制。如果一个程序对安全性提出要求,则最好不要使用反射。3.程序健壮性反射允许代码执行一些通常不被允许的操作,所以使用反射有可能会导致意想不到的后果。反射代码破坏了Java程序结构的抽象性,所以当程序运行的平台发生变化的时候,由于抽象的逻辑结构不能被识别,代码产生的效果与之前会产生差异。版权声明:本文为CSDN博主「ewenll」的原创文章,遵循CC4.0BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接: