zoukankan      html  css  js  c++  java
  • 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的官网阅读相关文档来了解更多信息。

  • 相关阅读:
    mybatis 错误 Invalid bound statement (not found)
    Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled. Please check Redis logs for details about the error.
    bug 记录 Unable to start ServletWebServerApplicationContext due to multiple ServletWebServerFactory beans
    解决:The Tomcat connector configured to listen on port 8182 failed to start. The port may already be in use or the connector may be misconfigured.
    jquery validate 验证插件 解决多个相同的Name 只验证第一个的方案
    phpStorm+xdebug调试(php7.3)
    小程序视频多个视频播放与暂停
    CSS实现单行、多行文本溢出显示省略号(…)
    Packet for query is too large (4,544,730 > 4,194,304). You can change this value on the server by setting the 'max_allowed_packet' variable.
    idea自动在文件头中添加作者和创建时间
  • 原文地址:https://www.cnblogs.com/itrena/p/9057574.html
Copyright © 2011-2022 走看看