Java反射机制总结

来源:互联网 发布:思源软件 编辑:程序博客网 时间:2024/05/13 03:17

什么是反射

反射是java的重要特征之一,通过反射可以在java程序运行时获得类自身的信息,并操作其属性和方法。反射最重要的用途就是开发各种通用框架(比如Spring、Struts 2)。对于应用层的开发者来说接触的相对较少。

要想理解什么是反射需要先知道Class类。Class类很特殊,它跟java中的其他类一样继承自Object,用来表达java程序运行时的类、接口、枚举、java Type等。每当一个类被加载时,jvm便自动产生一个Class对象与之对应。因此反射的核心就是如何获得相关Class对象,然后利用反射API提供的功能调用相关方法。通俗得讲就是通过反射机制访问java对象的属性,方法,构造方法等。

Java反射主要功能如下:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;

这些功能分别是通过如下几个类完成的:
- java.lang.Class;
- java.lang.reflect.Constructor;
- java.lang.reflect.Field;
- java.lang.reflect.Method;

如何使用反射功能

那么我们要如何使用反射功能呢?首先我们要明确一点反射功能的使用是建立在先获得对应的Class对象的基础上的。因此使用反射的第一步就是如何获得Class对象。然后才能通过Class对象的方法构造类的实例并通过实例调用类的字段和方法。

1、通过java反射机制获得Class对象。有三种方式:

(1)使用Class类的静态方法forName:

 public static Class<?> forName(String className) //例如:try {        //获得String类的Class对象        Class klass = Class.forName("java.lang.String");        } catch (ClassNotFoundException e) {            e.printStackTrace();        }

(2)通过类名直接获取对应象的class,比如:

Class<?> c = String.class;Class<?> cInt = Integer.TYPE;

(3)调用实例的getClass()方法,比如:

Person p1 = new Person("laoli,20");Class<?> klass = p1.getClass();

2、通过反射创建对应类的实例。有两种方式:

(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。

Class<?> c = String.class;Object str = c.newInstance();

(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。

//获取String所对应的Class对象Class<?> c = String.class;Constructor constructor = null;try {    //获取String类带一个String参数的构造器    constructor = c.getConstructor(String.class);    //根据构造器创建实例    Object obj = constructor.newInstance("hahah");    System.out.println(obj);    } catch (Exception e) {      e.printStackTrace();    }

3、通过反射获取对应类的方法集合,主要有以下几个方法:

getDeclaredMethods()方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

public Method[] getDeclaredMethods() throws SecurityException

getMethods()方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。

public Method[] getMethods() throws SecurityException

getMethod方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象

public Method getMethod(String name, Class<?>... parameterTypes)

只是这样描述的话可能难以理解,我们用例子来理解这三个方法:

import java.lang.reflect.Method;public class Demo {    public static void main(String[] args) {        try {            Class<?> c = methodClass.class;            Object object = c.newInstance();            Method[] methods = c.getMethods();            Method[] declaredMethods = c.getDeclaredMethods();            //获取methodClass类的add方法            Method method = c.getMethod("add", int.class, int.class);            //getMethods()            System.out.println("getMethods获取的方法:");            for (Method m : methods)                System.out.println(m);            //getDeclaredMethods()            System.out.println("getDeclaredMethods获取的方法:");            for (Method m : declaredMethods)                System.out.println(m);        } catch (Exception e) {        }    }}class methodClass {    public final int fuck = 3;    public int add(int a, int b) {        return a + b;    }    public int sub(int a, int b) {        return a + b;    }}
getMethods获取的方法:public int methodClass.add(int,int)public int methodClass.sub(int,int)public final void java.lang.Object.wait() throws java.lang.InterruptedExceptionpublic final void java.lang.Object.wait(long,int) throws java.lang.InterruptedExceptionpublic final native void java.lang.Object.wait(long) throws java.lang.InterruptedExceptionpublic boolean java.lang.Object.equals(java.lang.Object)public java.lang.String java.lang.Object.toString()public native int java.lang.Object.hashCode()public final native java.lang.Class java.lang.Object.getClass()public final native void java.lang.Object.notify()public final native void java.lang.Object.notifyAll()getDeclaredMethods获取的方法:public int methodClass.add(int,int)public int methodClass.sub(int,int)

4、通过反射获取对应类的字段(或者变量)

  • getFiled: 访问公有的成员变量
  • getDeclaredField:所有已声明的某个成员变量。但不能得到其父类的成员变量
  • getFileds和getDeclaredFields用法同上(参照Method)

(1)获取所有已声明变量

import java.lang.reflect.Field;import java.lang.reflect.Modifier;public class Demo {    public static void main(String[] args) {        getClassFields();    }    //获取Integer的已声明字段   public static void getClassFields(){       Class c = null;       try {           c = Class.forName("java.lang.Integer");       } catch (ClassNotFoundException e) {           e.printStackTrace();       }       Field[] fs = c.getDeclaredFields();       StringBuffer sb = new StringBuffer();       sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() +"{\n");       for(Field field:fs){           sb.append("\t");           sb.append(Modifier.toString(field.getModifiers())+" ");           sb.append(field.getType().getSimpleName() + " ");           sb.append(field.getName()+";\n");       }       sb.append("}");       System.out.println(sb);   }}
//输出结果public final class Integer{        public static final int MIN_VALUE;        public static final int MAX_VALUE;        public static final Class TYPE;        static final char[] digits;        static final char[] DigitTens;        static final char[] DigitOnes;        static final int[] sizeTable;        private final int value;        public static final int SIZE;        public static final int BYTES;        private static final long serialVersionUID;}

(2)获取指定私有变量

import java.lang.reflect.Field;public class Demo {    public static void main(String[] args) {        getAndChangeField();    }    //获取User对象指定字段并更改   public static void getAndChangeField(){       Class c = null;       try {           //获取类           c = Class.forName("User");           //获取age属性           Field ageF = c.getDeclaredField("age");           //通过Class对象实例化           Object o = c.newInstance();           //将私有变量的access flag设置为true           //否则将报错 java.lang.IllegalAccessException: Class Demo can not access a member of class User with modifiers "private"           ageF.setAccessible(true);           //将字段重新赋值为40           ageF.set(o, "40");           System.out.println(ageF.get(o));       } catch (Exception e) {           e.printStackTrace();       }   }}class User {    private String age;    public String getAge() {        return age;    }    public void setAge(String age) {        this.age = age;    }}

结果将输出 age=40,说明已经通过反射给对象赋值。

5、利用反射调用对象方法

当我们从类中获取了一个方法后,我们就可以用invoke()方法来调用这个方法。invoke方法的原型为:

public Object invoke(Object obj, Object... args)throws IllegalAccessException, IllegalArgumentException,InvocationTargetException

下面是一个实例:

import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class test1 {    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {        Class<?> klass = methodClass.class;        //创建methodClass的实例        Object obj = klass.newInstance();        //获取methodClass类的add方法        Method method = klass.getMethod("add",int.class,int.class);        //调用method对应的方法 => add(1,4)        Object result = method.invoke(obj,1,4);        System.out.println(result);    }}class methodClass {    public final int c = 3;    public int add(int a,int b) {        return a+b;    }    public int sub(int a,int b) {        return a+b;    }}

因此通过java的反射机制可以获得或者操作指定对象的任意变量或者方法.

6、反射和配置文件结合使程序更加灵活

一般工厂模式通过比较判断来实例化对应的工厂,不够灵活

public class SendFactory {      public Sender produce(String type) {          if ("mail".equals(type)) {              return new MailSender();          } else if ("sms".equals(type)) {              return new SmsSender();          } else {              System.out.println("请输入正确的类型!");              return null;          }      }  }

当有多个工厂时往往要写多个判断语句同时如果添加一个工厂的话需要更改源代码,这样代码的维护成就增加了。如果使用反射,通过配置文件来制定工厂类型就可以避免上面缺点。

首先增加配置文件 factory.properties

mail=sms.liwl.factorydemo.factories.MailSendersms=com.liwl.factorydemo.factories.SmsSender

然后修改SendFactory的produce方法

    public Sender produce(String id) {        Sender sender = null;        Properties properties = Utils.loadProperties(factory.properties);        String clazz = properties.getProperty(id);        try {            server = (Sender) Class.forName(clazz).newInstance();        } catch (Exception e) {            e.printStackTrace();        }        return sender;    }

经过修改后的工厂模式可以做到传入工厂id就能生成对应工厂实例,同时,如果需要增加多一个工厂只需编写一个新的工厂类继承基类SendFactory,然后在配置文件中增加一行“id = 全类名”即可。不再需要修改现有的文件。

Java反射机制用处很大,此处只是探究皮毛。欢迎大家进一步讨论。

原创粉丝点击