在java的运行环境中,对于一个人任意的类,如果要获取这个类的所有属性和方法(公有的和私有的),并且能够调用这个类的任意方法,那么可以借助Java语言的反射(Reflection)机制来动态获取类的信息。
Java反射机制提供的功能有:
1.在运行时候判构造一个类的对象
2.在运行时判断任意一个对象 所属的类
3.在运行时判断 任意一个类所具有的成员变量和方法
4.生成动态代理
5 在运行时调用任意一个对象的方法
软件开发人员进行单元测试有时候要访问类的非公有成员或方法,这时候就要利用好java的反射机制了。
但是访问类的私有变量和方法违反了 Java 语言封装性的基本规则,所以是否要用反射机制看具体情况。
访问类的私有变量和方法一般来说有2种方式:一种是直接修改权限修饰符,将private改成public,protected,或者直接将private修饰符删除。一种是借助反射和安全性管理器相结合的方式。
Java 运行时依靠一种安全性管理器来检验调用代码对某一特定的访问而言是否有足够的权限。具体来说,安全性管理器是 java.lang.SecurityManager 类或扩展自该类的一个类,且它在运行时检查某些应用程序操作的权限。换句话说,所有的对象访问在执行自身逻辑之前都必须委派给安全管理器,当访问受到安全性管理器的控制,应用程序就只能执行那些由相关安全策略特别准许的操作。因此安全管理器一旦启动可以为代码提供足够的保护。默认情况下,安全性管理器是没有被设置的,除非代码明确地安装一个默认的或定制的安全管理器,否则运行时的访问控制检查并不起作用。我们可以通过这一点在运行时避开 Java 的访问控制检查,达到我们访问非公有成员变量或方法的目的。为能访问我们需要的非公有成员,我们还需要使用 Java 反射技术。Java 反射是一种强大的工具,它使我们可以在运行时装配代码,而无需在对象之间进行源代码链接,从而使代码更具灵活性。在编译时,Java 编译程序保证了私有成员的私有特性,从而一个类的私有方法和私有成员变量不能被其他类静态引用。然而,通过 Java 反射机制使得我们可以在运行时查询以及访问变量和方法。由于反射是动态的,因此编译时的检查就不再起作用了。这种方式的缺点是:1.性能问题,用于字段和方法接入时反射要远慢于直接代码。第二个是权限问题,有些涉及 Java 安全的程序代码并没有修改安全管理器的权限,此时本方法失效。
public class Test {
public static void main(String[] args) {
String a="com.ccc.Day0409.Reflection.Test";
String m="m";
Class<String> c=String.class;
Class<List> c1=List.class;
Test t=new Test();
Class<? extends Test> tc=t.getClass();
System.out.println("t.getClass()= "+tc);//t.getClass()= class com.ccc.Day0409.Reflection.Test
try {
//获取class com.ccc.Day0409.Reflection.Test对象
Class<?> ca=Class.forName(a);//Class.forName(a)=class com.ccc.Day0409.Reflection.Test
System.out.println("Class.forName(a)="+ca);
System.out.println();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Class<ArrayList> al=ArrayList.class;
System.out.println("ArrayList.class="+al.getName());
Class<?>[] aa=al.getInterfaces();
for(Class<?> class1:aa){
System.out.println("class1.getSimpleName()="+class1.getSimpleName());
System.out.println("class1.getName()="+class1.getName());
}
}
}
使用反射调用方法的步骤:
1)要想使用反射,首先需要获得待处理类或对象所对应的Class对象。
2)获取某个类或某个对象所对应的Class对象的常用的3种方式:
a).类名.class
b)getClass()方法
c)Class.forName()方法
getName(): 获得类的名称,包括包名
getSimpleName(): 获得类的名称,不包括包名
getSuperClass(): 获得本类的父类的class对象
getInterfaces():获得本类所实现的所有接口的class对象