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方法






0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 清理垃圾软件打打不开怎么办 电脑清理后软件打不开了怎么办 手机黑屏开不了机怎么办 红米手机wifi打不开怎么办 苹果8开不开机怎么办 mac电脑打不开机怎么办 steam改密码上限了怎么办 qq加密了登不了怎么办 别人登我的淘宝怎么办 植物2被禁止登录怎么办 淘宝网东西未收到怎么办 淘宝网卖家不许退货退款怎么办 身份证以前开过淘宝店怎么办 支付宝登录名尚未激活怎么办 淘宝退货卖家不收货退款买家怎么办 淘宝账号刷得太多违规怎么办 闲鱼交易关闭了怎么办 淘宝店开了没做怎么办 微店店铺严重违规怎么办 淘宝违规扣2分怎么办 淘宝被扣6分怎么办 淘宝被扣2分怎么办 淘宝被海关扣了怎么办 淘宝被扣36分后怎么办 淘宝售假查封店铺资金怎么办 淘宝店扣48分怎么办 淘宝a内被扣48分怎么办 饿了么店铺满减怎么办 淘宝店扣a48分怎么办 淘宝短信营销无法获取人群怎么办 淘宝货发了退款怎么办 极速退款后卖家不确认收货怎么办 把货退了卖家不退款怎么办? 退款了又收到货怎么办 退货忘了填单号怎么办 手机换号了淘宝怎么办 换了手机支付宝怎么办 手机丢了微信登不上去了怎么办 前面手机丢了微信登不上去怎么办 淘宝密码忘了怎么办呢 融e借逾期一天怎么办