反射:
Java的反射机制就是,在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;
这种动态获取信息以及动态调用对象方法的功能成为Java的反射机制。
对反射机制的一点小小的测试
首先定义一个类People
,这个类主要用于测试反射的各种功能,因此特别分别声明了一些private和public的属性与方法。
class People{
public int ID;
private String name;
public People(int ID,String name){
this.ID = ID;
this.name = name;
}
public People(String name){
this.ID = 0;
this.name = name;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
private void amethod(){
System.out.println("a private method invoked");
}
}
接下来,定义一个testReflect
类来测试反射机制的应用:
public class testReflect {
public static void main(String[] args) {
try{
Class<?> c = Class.forName("People");
People p = (People)c.getDeclaredConstructor(new Class<?>[]{int.class,String.class}).newInstance(1,"lwx");
Method[] methods = c.getDeclaredMethods();
System.out.println("Methods:");
for(Method method:methods){
System.out.println(method.getName());
}
Method m = c.getDeclaredMethod("setName",String.class);
m.invoke(p,"zz");
Method m1 = c.getDeclaredMethod("getName");
System.out.println("name is:"+m1.invoke(p));
Method m2 = c.getDeclaredMethod("amethod");
m2.setAccessible(true);
m2.invoke(p);
Field[] fields = c.getDeclaredFields();
System.out.println("Fields:");
for(Field f:fields){
System.out.println(f.getName());
}
Field f = c.getDeclaredField("name");
f.setAccessible(true);
f.set(p,"hh");
System.out.println("now name is:"+m1.invoke(p));
}catch(Exception e){
e.printStackTrace();
}
}
}
分段来慢慢研究这个测试类运行的结果:
Class<?> c = Class.forName("People");
People p = (People)c.getDeclaredConstructor(new Class<?>[]{int.class,String.class}).newInstance(1,"lwx");
首先用Class.forName
方法,传入类的路径来加载People
类,这里由于我的People
类与TestReflect
类在同一个文件里,因此直接将People作为参数就能找到这个类并加载。
然后,通过反射机制,使用getDeclaredConstructor
方法,找到People
类指定参数的构造方法,并使用.newInstance()
方法构建一个新的实例。
这里比较有趣的一点是,由于我指定的构造方法有一个参数是int
,int
类型作为基础类型应该是没有对应的类的。但是,在使用getDeclaredConstructor
方法时需要传递指定构造函数的参数的类。然后,就发现还真可以传一个int.class
作为参数,这是咋回事呢。
上网查了一下,大概是这么一回事:
有9个预先定义好的Class对象代表8个基本类型和void,它们被java虚拟机创建,和基本类型有相同的名字
boolean
,byte
,char
,short
,int
,long
,float
,double
.
这8个基本类型的Class对象可以通过java.lang.Boolean.TYPE
,java.lang.Integer.TYPE
等来访问,同样可以通过int.class
,boolean.class
等来访问.
也就是说,int.class
是虚拟机运行时加载到方法区里的,所以我们编写程序的时候是找不到这么一个类的。同时,此处也可以用Integer.TYPE
来代替int.cass
,即可以这样写:
People p = (People)c.getDeclaredConstructor(new Class<?>[]{Integer.TYPE,String.class}).newInstance(1,"lwx");
接着看代码:
Method[] methods = c.getDeclaredMethods();
System.out.println("Methods:");
for(Method method:methods){
System.out.println(method.getName());
}
这里通过调用getDeclaredMethods
函数,获取刚刚创建的People
类声明的所有方法的列表,然后将其打印出来,结果如下:
然后,试试用反射来调用这些方法:
Method m = c.getDeclaredMethod("setName",String.class);
m.invoke(p,"zz");
Method m1 = c.getDeclaredMethod("getName");
System.out.println("name is:"+m1.invoke(p));
Method m2 = c.getDeclaredMethod("amethod");
m2.setAccessible(true);
m2.invoke(p);
我们首先通过反射找到了名为setName
的方法,生成了一个特殊的Method
对象,然后通过该对象的invoke
方法,来调用这个方法。值得注意的是,这个方法的第一个参数,正是一个People
类的对象,也就是说这个invoke
方法可以理解为p
对象传入参数"zz"来调用了方法setName
。
然后,再用同样的方法调用getName
方法,可以看到p
对象的name
已经被修改为了“zz”。
最后,再试试调用p对象的私有方法。这里稍微不同的是,要调用private方法必须先将该方法设置为可访问,即setAccessible(true)
,这样才能成功执行,否则会抛出异常IllegalAccessEsxception·
这段程序运行的结果如下:
最后,试试用反射来查询和访问类的属性:
Field[] fields = c.getDeclaredFields();
System.out.println("Fields:");
for(Field f:fields){
System.out.println(f.getName());
}
Field f = c.getDeclaredField("name");
f.setAccessible(true);
f.set(p,"hh");
System.out.println("now name is:"+m1.invoke(p));
总得来说,和上面访问Method
的方法类似,此处也用到了一个特殊的类Field
。反射机制是通过该类的实例来访问People
类的属性的。
这段程序运行的结果如下:
总结
反射就是将Java类中的方法和属性映射为一个个对象,如同Method对象将类的方法映射为一个方法对象,Field对象将类的属性映射为属性对象,Class对象为类对象。
反射的优点:
1.反射提高的Java程序的灵活性和拓展性,降低了耦合性。它允许程序创建和控制任何类的对象,无需提前硬编码目标类;
2,反射是其他一些常用语言都不具备的。
3.反射技术的应用领域很广,大多数流行的开源框架在实现过程中都应用了反射。
反射的缺点:
1.性能问题:使用反射基本是一种解释性的操作,要远慢于直接代码。因此反射主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
2.逻辑问题:使用反射会模糊程序内部的逻辑,反射技术绕过了源代码,从而会带来维护的问题。
3。使用反射的代码一般会比直接的代码更加复杂。