Java 反射机制
来源:互联网 发布:网络报纸取代传统报纸 编辑:程序博客网 时间:2024/05/22 19:26
原创文章,转载请注明出处。
Spring 之所以能够实例化并装配好程序所用到的 JavaBean,都归功于 Java 的反射机制。Java 反射机制的定义为:Java 语言允许通过程序化的方式间接对 Class 的对象实例操作,Class 文件由类装载器装载后,在 JVM 中将形成一份描述 Class 结构的信息对象,通过该元信息对象可以获知 Class 的结构信息,如构造函数、属性和方法等。下面将用一个简单示例,通过比较传统方法以及反射机制创建类实例的不同,来引入 Java 反射机制的原理。
一、简单示例演示 Java 反射机制的应用。
1、首先创建一个 Car 类,它包含三个属性,两个构造函数和一个方法。
public class Car {private String brand;private String color;private int maxSpeed;// 默认构造函数public Car() {System.out.println("init car.");}// 带参的构造函数public Car(String brand, String color, int maxSpeed) {this.brand=brand;this.color=color;this.maxSpeed=maxSpeed;}// 不带参的方法public void introduce() {System.out.println("brand:"+brand+";color:"+color+";maxSpeed:"+maxSpeed);}public String getBrand() {return brand;}public void setBrand(String brand) {this.brand=brand;}public String getColor() {return color;}public void setColor(String color) {this.color=color;}public int getMaxSpeed() {return maxSpeed;}public void setMaxSpeed(int maxSpeed) {this.maxSpeed=maxSpeed;}}2、传统的调用方法是使用构造函数设置属性或者 set 方法设置属性。
(1)构造函数的方法:Car car=new Car("奔驰","white","180");
(2)Set 方法:Car car=new Car(); car.setBrand("奔驰");
3、使用 Java 反射机制,以一种更通用的方式间接地操作目标类。
public class ReflectTest {public static Car initByDefaultConst() throws Throwable {// 1、通过类装载器获取 Car 对象ClassLoader loader=Thread.currentThread().getContextClassLoader();Class clazz=loader.loadClass("com.springstudy.reflect.Car");// 获取全限定类来装载对应的反射实例// 2、获取类的默认构造器对象并实例化 CarConstructor cons=clazz.getDeclaredConstructor((Class[])null);// 通过Car的反射类对象来获取它的构造类对象Car car=(Car)cons.newInstance();// 实例化Car对象,其效果等同于new Car()// 3、通过反射方法设置属性Method setBrand=clazz.getMethod("setBrand",String.class);// 第一个参数是目标类的方法,第二个是方法入参的对象类型setBrand.invoke(car,"奔驰");// 获取方法反射对象之后,使用invoke()来调用目标类的方法,第一个参数是操作的目标类对象的实例,第二个则是目标方法的入参Method setColor=clazz.getMethod("setColor",String.class);setColor.invoke(car,"white");Method setMaxSpeed=clazz/getMethod("setMaxSpeed",int.class);setMaxSpeed.invoke(car,"180");return car;}public static Car initByParamConst() throw Throwable {// 1、通过类装载器获取 Car 类对象ClassLoader loader=Thread.currentThread().getContextClassLoader();Class clazz=loader.loadClass("com.springstudy.reflect.Car");// 2、获取类的带有参数的构造器对象Constructor cons=clazz.getDeclaredConstructor(new Class[]{String.class,String.class,int.class});// 3、使参数的构造器对象实例化 CarCar car=(Car)cons.new Instance(new Object[]{"特斯拉","black",200});return car;}public static void main(String[] args) {Car car1=initByDefaultConst();Car car2=initByParamConst();car1.introduce();car2.introduce();}}
类装载器就是寻找类的字节码文件并构造出类在 JVM 内部表示的对象组件,主要工作由 ClassLoader 及其子类负责,ClassLoader 是一个重要的 Java 运行时系统组件,它负责在运行时查找和装入 Class 字节码文件。
1、类装载器的主要工作机制:
在 Java 中,类装载器把一个类装入到 Java 虚拟机当中需要经过一下的步骤:
(1)装载:查找和导入 Class 文件。
(2)链接:执行校验、准备和解析步骤。其中校验主要是检查载入 Class 文件的正确性,而准备工作就是给类的静态变量来分配存储空间,而解析就是将符号引用转变成直接引用。
(3)初始化:对类的静态变量、静态代码块执行初始化工作。
JVM 在运行时会产生 3 个 ClassLoader,根装载器、EXTclassLoader(扩展类装载器)、和 AppClassLoader(系统类装载器)。其中根装载器不是 ClassLoader 的子类,它是使用 C++ 编写的,所以我们在 Java 中看不到。根装载器来负责装载 JRE 的核心类库。而 ExtClassLoader 和 AppClassLoader 都是 ClassLoader 的子类,其中 ExtClassLoader 负责装载 JRE 扩展目录 ext 中的 JAR 类包,AppClassLoader 则负责装载 Classpath 路径下的类包。它们存在父子关系,即根装载器是 ExtClassLoader 的父装载器,而 ExtClassLoader 是 AppClassLoader 的父装载器。在默认情况下,使用 AppClassLoader 装载应用程序的类。
public class ClassLoaderTest {public static void main(String[] args) {// 从当前线程中获取当前线程的类装载器对象ClassLoader loader=Thread.currentThread().getContextClassLoader();// 打印出当前类装载器System.out.println("current loader:"+loader);// 打印出当前类装载器的父装载器System.out.println("parent loader:"+loader.getParent());// 打印出当前类装载器的父装载器的父装载器System.out.println("grandparent loader:"+loader.getParent().getParent());}}该段程序的三个输出依次为:AppClassLoader、ExtClassLoader、null(根装载器在 Java 中找不到,所以返回 null 值)。
2、类装载器的重要方法:
(1)Class loadClass(String name):其中 name 参数是指定类装载器需要装载的类的名字,在这里需要注意的是我们必须使用全限定类名。
(2)Class defineClass(String name,byte[] b,int off,int len):这个方法是将类文件的字节数组转换成 java 虚拟机内部的 java.lang.Class 对象。字节数组可以从本地文件系统、远程网络进行获取。而 name 为字节数组对应的全限定类名。
(3)Class findSystemClass(String name):主要是从本地文件系统中来载入 class 文件。如果本地文件系统中不存在该 class 文件,那么它将会抛出 ClassNotFoundException 这个异常。这个方法是 Java 虚拟机默认使用的装载机制。
(4)Class findLoadedClass(String name): 调用该方法来查看 ClassLoader 是否已装入到某个类当中。
(5)ClassLoader getParent():获取类装载器的父装载器。除了根装载器外,所有的类装载器都有且只有一个父装载器。
三、反射类
Class 反射对象描述类语义结构,可以从 Class 对象中获取构造函数,成员变量,方法等类元素的反射对象,并以编程的方式通过这些反射对象对目标类对象进行操作。这些反射类在 java.reflect 包中定义,下面是最主要的三个反射类:
1、Constructor:
类的构造函数反射类,通过 getConstructors() 方法可以获得类的所有构造函数反射对象数组。Constructor 的一个主要方法是 newInstance(),通过该方法可以创建一个对象类的实例,相当于 new 关键字。
2、Method:
类方法反射类,通过 getDeclaredMethods() 方法可以获取类的所有方法的反射对象数组。Method 的主要方法是 invoke() 方法。还有一些常用的获取类信息的方法:
(1)Class getReturnType():获取方法的返回值类型。
(2)Class[] getParameterTypes():获取方法的入参类型数组。
(3)Class[] getExceptionTypes():获取方法的异常类型数组。
(4)Annotation[][] getParameterAnnotations():获取方法的注解信息。
3、Field:
类的成员变量的反射类,通过 getDeclaredFields() 方法可以获取类的成员变量的反射对象数组。通过这个方法可以获取某个特定名称的成员变量反射对象。
* 此外,java 还为“包”提供了 package 反射类。
四、与 IoC 关系
在 Spring 中,通过 IoC 可以将实现类、参数信息等配置在其对应的配置文件中,那么当需要更改实现类或参数信息时,只需要修改配置文件即可,我们还可以对某对象所需要的其它对象进行注入,这种注入都是在配置文件中做的。
Spring 的 IoC 实现原理利用的就是 Java 的反射机制,Spring 的工厂类会帮我们完成配置文件的读取、利用反射机制注入对象等工作,我们可以通过 bean 的名称获取对应的对象。下面的例子和注释就演示了这些配置文件的读取、利用反射机制注入对象等的过程。
1、BeanFactory.java
package com.springstudy.reflect;import java.io.InputStream;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import org.dom4j.Attribute;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.SAXReader;public class BeanFactory {private Map<String, Object> beanMap = new HashMap<String, Object>();/** * bean工厂的初始化. * * @param xml xml配置文件 */public void init(String xml) {try {//1.创建读取配置文件的reader对象SAXReader reader = new SAXReader();//2.获取当前线程中的类装载器对象ClassLoader classLoader = Thread.currentThread().getContextClassLoader();//3.从class目录下获取指定的xml文件InputStream ins = classLoader.getResourceAsStream(xml);Document doc = reader.read(ins);Element root = doc.getRootElement();Element foo;//4.遍历xml文件当中的Bean实例for (Iterator i = root.elementIterator("bean"); i.hasNext();) {foo = (Element) i.next();//5.针对每个一个Bean实例,获取bean的属性id和classAttribute id = foo.attribute("id");Attribute cls = foo.attribute("class");//6.利用Java反射机制,通过class的名称获取Class对象Class bean = Class.forName(cls.getText());//7.获取对应class的信息java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean);//8.获取其属性描述java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();//9.创建一个对象,并在接下来的代码中为对象的属性赋值Object obj = bean.newInstance();//10.遍历该bean的property属性for (Iterator ite = foo.elementIterator("property"); ite.hasNext();) {Element foo2 = (Element) ite.next();//11.获取该property的name属性Attribute name = foo2.attribute("name");String value = null;//12.获取该property的子元素value的值for (Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();) {Element node = (Element) ite1.next();value = node.getText();break;}//13.利用Java的反射机制调用对象的某个set方法,并将值设置进去 for (int k = 0; k < pd.length; k++) {if (pd[k].getName().equalsIgnoreCase(name.getText())) {Method mSet = null;mSet = pd[k].getWriteMethod();mSet.invoke(obj, value);}}}//14.将对象放入beanMap中,其中key为id值,value为对象beanMap.put(id.getText(), obj);}} catch (Exception e) {System.out.println(e.toString());}}/** * 通过bean的id获取bean的对象. * * @param beanName * bean的id * @return 返回对应对象 */public Object getBean(String beanName) {Object obj = beanMap.get(beanName);return obj;}/** * 测试方法. * * @param args */public static void main(String[] args) {BeanFactory factory = new BeanFactory();factory.init("conf/config.xml");JavaBean javaBean = (JavaBean) factory.getBean("javaBean");System.out.println("userName=" + javaBean.getUserName());System.out.println("password=" + javaBean.getPassword());}}2、JavaBean.java
package com.springstudy.reflect;public class JavaBean { private String userName; private String password; public String getPassword() { return password; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public void setPassword(String password) { this.password = password; }}3、config.xml
<?xml version="1.0" encoding="UTF-8"?><beans> <bean id="javaBean" class="com.springstudy.reflect.JavaBean"> <property name="userName"> <value>root</value> </property> <property name="password"> <value>123456</value> </property> </bean></beans>
以上只是 Java 反射机制和 Spring IoC 的一些简单的应用,实际操作中可能更加复杂,但是其原理是一样的。下面有两点总结,参考自点击打开链接:
1、什么是反射机制?
简单的来说,反射机制指的是程序在运行时能够获取自身的信息。在 java 中,只要给定类的名字,那么就可以通过反射机制来获得类的所有信息。
2、利用反射机制能获得什么信息?
一句话,类中有什么信息,它就可以获得什么信息,不过前提是得知道类的名字,要不就没有后文了。
(1)首先得根据传入的类的全名来创建Class对象。
Class c=Class.forName("className"); // 注意:className必须为全名,也就是得包含包名,比如,cn.netjava.pojo.UserInfo;
Object obj=c.newInstance(); // 创建对象的实例
(2)有了对象就什么都好办了,想要什么信息就有什么信息了。
①获得构造函数的方法
Constructor getConstructor(Class[] params) // 根据指定参数获得public构造器
Constructor[] getConstructors() // 获得public的所有构造器
Constructor getDeclaredConstructor(Class[] params)// 根据指定参数获得public和非public的构造器
Constructor[] getDeclaredConstructors() // 获得public的所有构造器
②获得类方法的方法
Method getMethod(String name, Class[] params) // 根据方法名,参数类型获得方法
Method[] getMethods() // 获得所有的public方法
Method getDeclaredMethod(String name, Class[] params)// 根据方法名和参数类型,获得public和非public的方法
Method[] getDeclaredMethods() // 获得所以的public和非public方法
③获得类中属性的方法
Field getField(String name) // 根据变量名得到相应的public变量
Field[] getFields() // 获得类中所以public的方法
Field getDeclaredField(String name) // 根据方法名获得public和非public变量
Field[] getDeclaredFields() // 获得类中所有的public和非public方法
- 【反射】JAVA反射机制
- 【Java】JAVA反射机制
- Java 反射机制[Field反射]
- Java 反射机制[Method反射]
- Java反射机制笔记-反射机制
- java的反射机制
- Java的反射机制
- java反射机制详解!
- Java反射机制
- Java的反射机制
- java 反射机制--侯捷
- java反射机制
- java反射机制
- [候捷]Java反射机制
- java 反射机制
- java 反射机制初探
- 关于Java反射机制
- java反射机制
- 南阳OJ 题目27 水池数目
- 05-树7 堆中的路径 (25分)
- 了解twisted时遇到的问题
- 【响应式Web设计】读书笔记
- 避免这些坑,让你快速找到好工作
- Java 反射机制
- 【通俗理解线性代数】 -- 施密特正交化与QR分解
- 《Android开发艺术探索》读书笔记----第一章:IntentFilter匹配规则
- 多说评论完美迁移Disqus
- 类模板中友元函数是函数模板
- FunDA(17)- 示范:异常处理与事后处理
- matlab中窗函数的使用(一)
- Android ScrollView 滚动视图
- HDU 5325 Crazy Bobo