反射是java中一个不太被大多数人关注的一个模块,尤其对于很多初学者,甚至不知道何为反射,但是现在在java中越来越多的企业在笔试,面试的时候提到反射的知识,因为反射机制在web上的确有着不可替代的重要作用。究竟什么是反射呢?今天我就带领大家初步理解java中的反射机制,JAVA反射机制是在运行状态中,能够动态获取的信息以及动态调用对象的方法的功能的一种机制。 通俗点说就是:Java程序可以加载一个运行时才得知名称的class(称为运行时类),获悉其完整构造(但不包括methods定义),并生成其对象实体、对其fields设值或唤起其methods。再通俗讲,其实就是一个字符串,当然这个字符串是一个从包到类的一个地址名称,通过反射机制能够生成一个运行时对象,然后可以通过这个对象操作类中的属性和方法,当然这样讲可能也有些片面了。
首先构造一个字符串,“com.angel.prac”是包名,"Earth"是我自己写的一个类(Earth类在上面有具体的实现),通过Class类下的forName就可以将这个含有确切是哪个类的字符串返回一个确切的运行时类对象,然后通过newInstance方法就可以产生一个新的对象,这个对象是从c来的,newInstance这个方法的返回值是Object,此刻我们将要强转为Earth类型的,从而能够运用Earth类下的属性,方法。现在实际上我们已经拿到了这个运行时类对象了,就可以利用这个对象去操纵Earth类下的属性,方法了。
反射一个方法,splite这个方法是处理字符串的一个方法,意为分割,在本例中就是说只要遇到一个!号那么我就分割成一部分,存到一个数组中,最后返回一个字符串数组。用这个运行时类的对象去getMethod,就可以去获得一个程序员指定名字的方法了,在这里,我用了一系列处理字符串的API方法,在这里,就不多多细说了,大家可以参考API中文文档。在这里有一个非常重要的方法那就是invoke,意思是调用,就是说将执行earth这个新对象下的程序员想调用的方法,这个方法是返回在Method的对象中的。然后返回一个Object对象。
反射一个方法,例子中已经注释了,getDeclaredField和getField的区别,我们可以通过getDeclaredField访问类中的私有属性,返回一个Field,要想对数据进行存取,必须将setAccessible设置为true,这时候,你就告诉JVM了,我要访问这个属性,我有对它操作的权利。非默认构造器中的获取在这就不重复了。
内省,这是个什么东东呢?不妨我们去问一下google大神,看看google是如何说的:
好了,到这里呢,反射和内省讲的就差不多了,不明白?不要紧,什么也不要说,先照着我的代码打三遍,打完了再看一遍这篇文章,我相信你会有很大的收获。可能有很多的人问:我费这么大的劲,就为了传个参数何必要这么复杂呢?在以后的学习和工作中, Java 的反射以及内省应用到程序设计中去可以大大的提供程序的智能化和可扩展性,以后的web开发中我们只要操纵XML的配置文件就可以了,而不必再去修改源代码,这是不是很爽呢?当然也大大地提高了代码的可维护性。
上面的解释可能太过于抽象,大家可能还不能明白究竟什么是实实在在的反射,没关系,只要大家细心研究下面的实例,一定会让大家将反射机制的知识变成自己的东西。
下面是我自己写的一个程序,它包括了通过反射机制反射一个类,反射一个方法,反射一个属性,以及甚至可以反射一个数组,当然还有内省的实现,内省并不属于反射,要告诉大家的是,我的每个实现都是分为在默认构造器和在非默认构造器下不同的实现过程。
package com.angel.prac;
public class Earth {
private String name;
private double volume;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getVolume() {
return volume;
}
public void setVolume(double volume) {
this.volume = volume;
}
}
package com.angel.prac;
public class Mars {
private String name;
private float volume;
public Mars(){}
public Mars(String name, float volume) {
this.name = name;
this.volume = volume;
}
public Mars(String name){
this.name = name;
}
public String getName() {
return name;
}
public float getVolume() {
return volume;
}
}
反射一个类:
package com.angel.prac;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Reflaction {
public void reflectClass() throws Exception{
//反射一个类:
//默认构造器的反射类:
String s = "com.angel.prac.Earth";
Class <?> c = Class.forName(s);
Earth earth = (Earth)c.newInstance();
earth.setName("Angel");
earth.setVolume(521.1314);
System.out.println("The Earth's name is "+ earth.getName() + "-----" +
"The volume is "+ earth.getVolume());
//非默认构造器的反射类:
String s0 = "com.angel.prac.Mars";
Class <?> c0 = Class.forName(s0);
// java.lang.Class.getConstructor(Class<?>... arg0)
Constructor <?> constructor = c0.getConstructor(String.class,float.class);
Mars mars = (Mars)constructor.newInstance("Mars",1314.521f);
System.out.println("The Mars' name is " + mars.getName() + "----- "+"The volume is "
+ mars.getVolume());
}
首先构造一个字符串,“com.angel.prac”是包名,"Earth"是我自己写的一个类(Earth类在上面有具体的实现),通过Class类下的forName就可以将这个含有确切是哪个类的字符串返回一个确切的运行时类对象,然后通过newInstance方法就可以产生一个新的对象,这个对象是从c来的,newInstance这个方法的返回值是Object,此刻我们将要强转为Earth类型的,从而能够运用Earth类下的属性,方法。现在实际上我们已经拿到了这个运行时类对象了,就可以利用这个对象去操纵Earth类下的属性,方法了。
现在讲讲在非默认的情况下,这个Mars类中含有非默认构造器,并且参数的传递是通过这个非默认构造器进行的,而不是Earth类中通过Getter和Setter的封装set到类中的。对于非默认构造器,我们要通过这个运行时类的对象去调用getConstructor方法获得非默认构造器,getConstructor(String.class,float.class)2个参数意思是指定反射Constructor(String name,float volume)这个构造方法。通过这个constructor对象再去产生一个新的实例,这时,就需要向将要生成的实例的非默认构造器中传递参数了。剩下的部分就和前面一样了。
public void reflectMethod() throws Exception{
//反射一个方法:
//默认构造器的反射方法:
String s = "com.angel.prac.Earth!nAMe!Earth";
String [] string = s.split("!");
Class<?> c = Class.forName(string[0]);
Earth earth = (Earth) c.newInstance();
Method setMethod = c.getMethod("set" + string[1].substring(0, 1).toUpperCase() +
string[1].substring(1, string[1].length()).toLowerCase(),String.class);
Method getMethod = c.getMethod("get" + string[1].substring(0, 1).toUpperCase() +
string[1].substring(1, string[1].length()).toLowerCase());
setMethod.invoke(earth, string[2]);
Object obj = getMethod.invoke(earth);
System.out.println(obj);
//非默认构造器的反射方法:
String s0 = "com.angel.prac.Mars!NaMe!vOLUme!Mars!521.1314f";
String [] str = s0.split("!");
Class<?> c1 = Class.forName(str[0]);
Constructor <?> constructor1 = c1.getConstructor(String.class, float.class);
Mars mars1 = (Mars)constructor1.newInstance(str[3] , Float.parseFloat(str[4]));
Method getMarsName = c1.getMethod("get" + str[1].substring(0, 1).toUpperCase()+
str[1].substring(1,str[1].length()).toLowerCase());
Method getMarsVolume = c1.getMethod("get" + str[2].substring(0, 1).toUpperCase()+
str[2].substring(1,str[2].length()).toLowerCase());
Object obj0 = getMarsName.invoke(mars1);
Object obj1 = getMarsVolume.invoke(mars1);
System.out.println(obj0);
System.out.println(obj1);
}
反射一个方法,splite这个方法是处理字符串的一个方法,意为分割,在本例中就是说只要遇到一个!号那么我就分割成一部分,存到一个数组中,最后返回一个字符串数组。用这个运行时类的对象去getMethod,就可以去获得一个程序员指定名字的方法了,在这里,我用了一系列处理字符串的API方法,在这里,就不多多细说了,大家可以参考API中文文档。在这里有一个非常重要的方法那就是invoke,意思是调用,就是说将执行earth这个新对象下的程序员想调用的方法,这个方法是返回在Method的对象中的。然后返回一个Object对象。
非默认下的反射方法,其实就是一点不用,参数进入到一个类中的方式不同了,前面已经说过,因为有了非默认构造器,所以也就不需要setXXX这个方法了。
public void reflectArray(){
//反射一个数组:
Object obj = Array.newInstance(int.class, 5);
for (int i = 0; i < 5; i++) {
Array.set(obj, i, i+1);
}
for (int i = 0; i < 5; i++) {
System.out.println(Array.get(obj, i));
}
}
反射一个数组,很简单,66行是说产生一个大小为5的int类型的数组对象。通过Array下的静态方法set和get向里面存和往外取数据。
public void reflectField() throws Exception{
//反射属性:
//默认构造器反射一个属性:
String s0 = "com.angel.prac.Earth!volume!5211314";
String [] str = s0.split("!");
Class<?> c0 = Class.forName(str[0]);
Earth earth = (Earth)c0.newInstance();
//java.lang.Class.getDeclaredField(String arg0)这个用于private的属性
//java.lang.Class.getField(String arg0)这个用于public属性
Field fie = c0.getDeclaredField(str[1]);
fie.setAccessible(true);
fie.set(earth, 3344);
System.out.println(fie.get(earth));
//非默认构造器反射一个属性:
String s = "com.angel.prac.Mars!name!volume!火星星!521.1314f";
String [] string = s.split("!");
Class<?> c = Class.forName(string[0]);
Constructor<?> constructor = c.getConstructor(String.class, float.class);
Mars mars = (Mars)constructor.newInstance(string[3],Float.parseFloat(string[4]));
//java.lang.Class.getDeclaredField(String arg0)这个用于private的属性
//java.lang.Class.getField(String arg0)这个用于public属性
Field field = c.getDeclaredField(string[1]);
Field f =c.getDeclaredField(string[2]);
field.setAccessible(true);
f.setAccessible(true);
field.set(mars, "为什么是火星星?");
f.setFloat(mars, 1234.567f);
System.out.println(field.get(mars));
System.out.println(f.get(mars));
}
反射一个方法,例子中已经注释了,getDeclaredField和getField的区别,我们可以通过getDeclaredField访问类中的私有属性,返回一个Field,要想对数据进行存取,必须将setAccessible设置为true,这时候,你就告诉JVM了,我要访问这个属性,我有对它操作的权利。非默认构造器中的获取在这就不重复了。
public void reflectIntrospection() throws Exception{
//内省(仅提供属性示例,也可以操作类和方法):
String s0 = "com.angel.prac.Earth";
Class<?> c0 = Class.forName(s0);
Earth earth = (Earth)c0.newInstance();
BeanInfo bean = Introspector.getBeanInfo(c0, Object.class);
PropertyDescriptor[] descriptor = bean.getPropertyDescriptors();
for(PropertyDescriptor p : descriptor){
if(p.getName().equals("name")){
p.getWriteMethod().invoke(earth, "地球球");
System.out.println(p.getReadMethod().invoke(earth));
}
}
//非默认构造器反射一个属性:
String s = "com.angel.prac.Mars!name!volume!火星星!123.09f";
String [] string = s.split("!");
Class<?> c = Class.forName(string[0]);
Constructor<?> constructor = c.getConstructor(String.class, float.class);
Mars mars = (Mars)constructor.newInstance(string[3],Float.parseFloat(string[4]));
BeanInfo beanInfo = Introspector.getBeanInfo(c, Object.class);
PropertyDescriptor [] pd = beanInfo.getPropertyDescriptors();
//直接操作属性:
pd[0].setValue("name", "反射学完了,感到so easy!");
pd[1].setValue("volume",234);
System.out.println(pd[0].getValue("name"));
System.out.println(pd[1].getValue("volume"));
//通过方法获得属性:
for (PropertyDescriptor propertyDescriptor : pd) {
if(propertyDescriptor.getName().equals("volume")){
System.out.println(propertyDescriptor.getReadMethod().invoke(mars));
}
}
}
内省,这是个什么东东呢?不妨我们去问一下google大神,看看google是如何说的:
内省是 Java 语言对 Bean 类属性、事件的一种缺省处理方法。例如类 A 中有属性 name, 那我们可以通过 getName,setName 来得到其值或者设置新的值。通过 getName/setName 来访问 name 属性,这就是默认的规则。 Java 中提供了一套 API 用来访问某个属性的 getter/setter 方法,通过这些 API 可以使你不需要了解这个规则(但你最好还是要搞清楚),这些 API 存放于包 java.beans 中。 一般的做法是通过类 Introspector 来获取某个对象的 BeanInfo 信息,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的 getter/setter 方法,然后我们就可以通过反射机制来调用这些方法。
我们可以直接操作属性,也可以通过获得方法来操作属性,具体的方法,大家可以根据eclipse中的提示和API文档慢慢琢磨。
public static void main(String[] args) throws Exception {
Reflaction reflection = new Reflaction();
reflection.reflectClass();
reflection.reflectMethod();
reflection.reflectArray();
reflection.reflectField();
reflection.reflectIntrospection();
}
}
好了,到这里呢,反射和内省讲的就差不多了,不明白?不要紧,什么也不要说,先照着我的代码打三遍,打完了再看一遍这篇文章,我相信你会有很大的收获。可能有很多的人问:我费这么大的劲,就为了传个参数何必要这么复杂呢?在以后的学习和工作中, Java 的反射以及内省应用到程序设计中去可以大大的提供程序的智能化和可扩展性,以后的web开发中我们只要操纵XML的配置文件就可以了,而不必再去修改源代码,这是不是很爽呢?当然也大大地提高了代码的可维护性。
在这里我只是简单的给大家介绍一下,大家想了解更多,很简单,去问一下google大神就可以了,对于我们深入了解java语言有很大的帮助。也欢迎大家一起和我进行交流,学习,欢迎大家指出不足之处,请联系QQ:497820217 备注:优快云。
本文为原创,如需要转载,请注明出处。