反射的定义
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要获取一个类,必须先要获取到该类的字节码文件对象。而获取使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。
反射就是把java类中的各种成分映射成一个个的Java对象。
反射的作用通过反射可以使程序代码访问装载到JVM 中的类的内部信息;
获取已装载类的成员变量信息;
获取已装载类的方法;
获取已装载类的构造方法信息;
反射的应用场景
在编译时根本无法知道该对象或类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息。
Java中的两种类型:编译时类型/运行时类型。
// Person为p的编译时类型,Student为p的运行时类型Person p = new Student();
在一些极端的情况下,外部传入一个对象时,该对象的编译时类型是object,但程序又需要调用该类的 运行时类型的方法。
解决这个问题我们在编程时有两种方法解决。
我们知道传入的是类的运行时类型,就可以使用instanceof进行判断,再强制类型转换,就可以使用这个类的方法和变量。
我们不知道传入的对象的类型,也不了解该类的方法和属性,程序只能靠运行时信息来发现对象和类,这时候我们就必须使用反射。
绝大部分框架的AOP,如Spring,需要依托反射机制实现。
反射的实现
通过Class.forName
通过Class.forName()方法加载字符串,就可以得到该字符串做代表的Class对象。
例如:Class> clazz = Class.forName("java.lang.String")就可以得到String类的Class对象。值得注意的是,字符串必须是类的全名,即包名+类名。
下边的代码是Struts配置文件struts.xml中的一个action的配置。
/registeResult.jsp
/registe2.jsp
这里的class属性给出了一个类的全名的字符串,服务器是如何通过这个字符串得到类对象的呢?就是通过反射机制RegisteAction对象的。然后再去调用这个类中的默认的execute()方法。
通过类名调用class属性得到该类的Class对象
例如:Class> clazz = String.class也可以得到String类的Class对象。
调用实例的getClass()方法
例如:
Date date = new Date();
Class> clazz = date.getClass();
通过上边的两句代码就可以得到date实例的Class对象。
如果是基本类型的包装类,则可以通过调用包装类的Type属性来获得该包装类的Class对象
例如:
Class> clazz = Integer.TYPE;
无论使用哪种方法获得了Class对象,只要得到了Class对象,下来的操作方法都是相同的。
Field[] fields = clazz.getDeclaredFields();获取类中定义的属性,包括private的属性,但是不包括从父类继承下来的属性。
Method[] methods = clazz.getDeclaredMethods();获取了类中的方法,包括private的方法,但是不包括从父类继承下来的方法。
反射的步骤
获取目标对象的class,一般使用Class.forName(String clazzName);
通过class对象分别获得该类型的,构造函数,属性,方法。
通过获得的属性和方法,进行进一步操作。
反射类
反射的完成依赖了Java提供的反射类,Class,Constructor(构造器),Field(属性), Method(方法)。
Class
每一个类都有一个Class对象,每当编译一个新类就产生一个Class对象。
Class对象对应着java.lang.Class类,如果说类是对象抽象和集合的话,那么Class类就是对类的抽象和集合。
Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
Java获取Class的三种方式
运用.class的方式来获取Class实例
对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例。例如,Integer.TYPE与 int.class是等效的,都是int。
利用对象调用getClass()方法获取该对象的Class实例
public class Test {
public static void main(String[] args) {
System.out.println("第一种实例化:");
System.out.println(int.class);
System.out.println(Integer.TYPE);
System.out.println("第二种实例化:");
Test test = new Test();
System.out.println(test.getClass().getName());
System.out.println(test.getClass().getSimpleName());
}
}
使用Class类的静态方法forName
使用Class类的静态方法forName(),用类的名字获取一个Class实例(static Class forName(String className) ),这种方式灵活性最高,根据类的字符串全名即可获取Class实例,可以动态加载类,框架设计经常用到。
结语
虽然反射看起来无所不能,但Java反射默认受限于Java的访问控制。
如:无法访问private私有的方法和字段,Java的安全机制不允许查看这些对象的直接值,若强制读取则抛出异常。
以上就是本篇全部内容,是很早的时候,在用反射机制做日志管理的时候写的,也是边学边总结,不是很全面或可能有错误。如果本文有任何错误或有什么看法,欢迎指出。原作者:东野啊
原文链接:这个是考点:Java反射机制
原出处:公众号