Java 反射机制

本篇文章,翻译自Java Reflection Tutorial

什么是反射?为什么要使用反射?如何用它?

什么是反射

在JVM中,当一个程序需要在运行时修改或者检测应用运行的行为时,则需要使用反射。反射的概念容易和自我检查(introspection)相混淆。wiki上关于反射与自我检查的定义如下,

  1. 自我检查(introspection):能够在运行时检测对象类型属性.
  2. 反射(reflection):能够在运行时检测修改对象的结构行为.

从上述定义可知,自我检查是反射的子集。有些语言是支持自我检查,但是不支持反射, 例如C++。

java反射机制

自我检查的例子:instanceof操作检查对象是否属于某个类。

    if(obj instanceof Dog){
        Dog d = (Dog)obj;
        d.bark();
    }

反射的例子:Class.forName()方法返回与给定的类名的类关联的Class对象。forName方法可以使得这个类实例化。

    // with reflection
    Class c = Class.forName("classpath.and.classname");
    Object dog = c.newInstance();
    Method m = c.getDeclaredMethod("bark", new Class[0]);
    m.invoke(dog);

在Java中,反射的功能要比自我检查多,因为自我检查不能修改对象的结构。

为什么需要反射

反射有以下用途:

  • 在运行时,检查对象所属的类
  • 在运行时,构造一个类的对象
  • 在运行时,检查一个类的域和方法
  • 在运行时,调用类的任何方法
  • 改变构造器、方法和域的访问标志
  • 等等...

反射是框架的基本方法。
例如, JUnit使用反射区查找含有@Test注解的方法,并且在运行单元测试时,调用这些方法。
对于web框架,产品开发者定义他们自己的接口和类的实现,并且将这些接口和类放入配置文件。使用反射,可以快速的,动态的初始化这些类。
例如,Spring使用这样的bean配置文件,

    
        
    

当Spring上下文处理这个元素时,它将使用Class.forName(String className)和”com.programcreek.Foo“字符串作为参数去实例化这个类。然后,它将再次使用反射,get属性的值和set属性的值。

这种机制也被用于Servlet 框架中。

    
        someServlet
        com.programcreek.WhyReflectionServlet
    

如何使用反射

例1:获取对象的类名。

    package myreflection;
    import java.lang.reflect.Method;
     
    public class ReflectionHelloWorld {
        public static void main(String[] args){
            Foo f = new Foo();
            System.out.println(f.getClass().getName());         
        }
    }
     
    class Foo {
        public void print() {
            System.out.println("abc");
        }
    }

输出

    myreflection.Foo

例2:调用未知类型对象的方法
对于下面的例子,考虑对象的类型是未知的。通过使用反射,这段代码可以使用这个对象,并且如果这个对象有名为“print”的方法,那么就调用print()方法。

    package myreflection;
    import java.lang.reflect.Method;
     
    public class ReflectionHelloWorld {
        public static void main(String[] args){
            Foo f = new Foo();
     
            Method method;
            try {
                method = f.getClass().getMethod("print", new Class[0]);
                method.invoke(f);
            } catch (Exception e) {
                e.printStackTrace();
            }           
        }
    }
     
    class Foo {
        public void print() {
            System.out.println("abc");
        }
    }

输出:

    abc

例3:从Class实例中创建对象

    package myreflection;
 
    public class ReflectionHelloWorld {
        public static void main(String[] args){
            //create instance of "Class"
            Class c = null;
            try{
                c=Class.forName("myreflection.Foo");
            }catch(Exception e){
                e.printStackTrace();
            }
     
            //create instance of "Foo"
            Foo f = null;
     
            try {
                f = (Foo) c.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }   
     
            f.print();
        }
    }
     
    class Foo {
        public void print() {
            System.out.println("abc");
        }
    }

例4:获取构造方法,并且创建实例

    package myreflection;
 
    import java.lang.reflect.Constructor;
     
    public class ReflectionHelloWorld {
        public static void main(String[] args){
            //create instance of "Class"
            Class c = null;
            try{
                c=Class.forName("myreflection.Foo");
            }catch(Exception e){
                e.printStackTrace();
            }
     
            //create instance of "Foo"
            Foo f1 = null;
            Foo f2 = null;
     
            //get all constructors
            Constructor cons[] = c.getConstructors();
     
            try {
                f1 = (Foo) cons[0].newInstance();
                f2 = (Foo) cons[1].newInstance("abc");
            } catch (Exception e) {
                e.printStackTrace();
            }   
     
            f1.print();
            f2.print();
        }
    }
     
    class Foo {
        String s; 
     
        public Foo(){}
     
        public Foo(String s){
            this.s=s;
        }
     
        public void print() {
            System.out.println(s);
        }
    }

输出:

    null
    abc

此外,你可以使用Class实例去获得该类实现的接口,父类,声明的域等等。

例5:通过反射,改变数组的大小

    package myreflection;
     
    import java.lang.reflect.Array;
     
    public class ReflectionHelloWorld {
        public static void main(String[] args) {
            int[] intArray = { 1, 2, 3, 4, 5 };
            int[] newIntArray = (int[]) changeArraySize(intArray, 10);
            print(newIntArray);
     
            String[] atr = { "a", "b", "c", "d", "e" };
            String[] str1 = (String[]) changeArraySize(atr, 10);
            print(str1);
        }
     
        // change array size
        public static Object changeArraySize(Object obj, int len) {
            Class arr = obj.getClass().getComponentType();
            Object newArray = Array.newInstance(arr, len);
     
            //do array copy
            int co = Array.getLength(obj);
            System.arraycopy(obj, 0, newArray, 0, co);
            return newArray;
        }
     
        // print
        public static void print(Object obj) {
            Class c = obj.getClass();
            if (!c.isArray()) {
                return;
            }
     
            System.out.println("\nArray length: " + Array.getLength(obj));
     
            for (int i = 0; i < Array.getLength(obj); i++) {
                System.out.print(Array.get(obj, i) + " ");
            }
        }
    }

输出:

    Array length: 10
    1 2 3 4 5 0 0 0 0 0 
    Array length: 10
    a b c d e null null null null null 

总结

上面代码展示了Java反射的一小部分使用方法,更详细的介绍,参考这里。

你可能感兴趣的:(Java 反射机制)