• java反射教程


    什么是反射,为什么它是有用的,以及如何使用它?

    1.什么是反射?

    “反射通常是JVM中运行的程序需要检测和修改运行时程序的行为的一种能力。”这个概念通常与内省(Introspection)混淆。以下是这两个术语在维基百科中的定义:

    1. 内省是指计算机程序在运行时检查对象类型的一种能力,通常也可以称作运行时类型检查。
    2. 反射是指计算机程序在运行时可以访问、检测和修改它本身状态或行为的一种能力。

    从他们的定义可以看出,内省是反射的一个子集。有些语言支持内省,但不支持反射,如C++。

    reflection-introspection-650x222.png

    内省例子: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中,更关注于反射而非内省,因为你不能改变一个对象的结构。

    2.为什么我们需要反射?

    反射能够让我们:

    • 运行时检测一个对象所属的类;
    • 运行时构造一个类的对象;
    • 运行时检测一个类的字段和方法;
    • 运行时调用一个对象的任意方法;
    • 修改构造函数、方法、字段的访问权限,AccessibleObject的setAccessible(boolean flag)方法;
    • 等等

    反射是框架中常用的方法。

    例如,JUnit通过反射来查找标记为 @Test 注解的方法,并在运行单元测试时调用这些方法。

    对于Web框架,产品开发人员在配置文件中定义接口和类的实现。通过使用反射,框架可以快速动态地初始化所需要的类。

    例如,Spring框架使用bean的配置,如:

    <bean id="someID" class="com.programcreek.Foo">
       <property name="someField" value="someValue" />
    </bean>

    当Spring上下文处理<bean>元素时,将使用Class.forName(String)与参数"com.programcreek.Foo"来实例化类。然后,它会再次使用反射来得到<property>元素对应的setter方法并设置指定的值。 

    相同的机制也用于Servlet web applications:

    <servlet>
       <servlet-name>someServlet</servlet-name>
       <servlet-class>com.programcreek.WhyReflectionServlet</servlet-class>
    <servlet>

    3.如何使用反射?

    通过以下几个典型的小例子来学习如何使用反射。

    例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”的方法,然后调用它。

    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(" Array 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反射,你需要在Oracle的官网阅读相关文档来了解更多信息。

  • 相关阅读:
    知识图谱学习与实践(4)——通过例句介绍Sparql的使用
    知识图谱学习与实践(3)——知识表示
    知识图谱学习与实践(2)——知识图谱数据模型的构建
    知识图谱学习与实践(1)——知识图谱的演化过程
    NIO客户端主要创建过程
    NIO服务端主要创建过程
    Relative path in absolute URI: ${system:java.io.tmpdir%7D/$%7Bhive.session.id%7D_resources
    ubuntu中mysql安装失败
    使用ant build build.xml报“includeantruntime was not set”警告及"Class not found: javac1.8"问题
    maven编译报错 -source 1.5 中不支持 lambda(或diamond) 表达式,编码 UTF-8 的不可映射字符
  • 原文地址:https://www.cnblogs.com/weiguoaa/p/9057761.html
Copyright © 2020-2023  润新知