reflection_java reflection - CSDN
  • Reflection是Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并显示出来。 Java 的这一...
    Reflection是Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性。例如,使用它能获得 Java 类中各成员的名称并显示出来。
    Java 的这一能力在实际应用中也许用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。

      JavaBean 是 reflection 的实际应用之一,它能让一些工具可视化的操作软件组件。这些工具通过 reflection 动态的载入并取得 Java 组件(类) 的属性。

    1. 一个简单的例子

      考虑下面这个简单的例子,让我们看看 reflection 是如何工作的。


    import java.lang.reflect.*;
    public class DumpMethods {
    public static void main(String args[]) {
    try {
    Class c = Class.forName(args[0]);
    Method m[] = c.getDeclaredMethods();
    for (int i = 0; i < m.length; i++)
    System.out.println(m[i].toString());
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      按如下语句执行:

    java DumpMethods java.util.Stack


      它的结果输出为:

    public java.lang.Object java.util.Stack.push(java.lang.Object)

    public synchronized java.lang.Object java.util.Stack.pop()

    public synchronized java.lang.Object java.util.Stack.peek()

    public boolean java.util.Stack.empty()

    public synchronized int java.util.Stack.search(java.lang.Object)


      这样就列出了java.util.Stack 类的各方法名以及它们的限制符和返回类型。

      这个程序使用 Class.forName 载入指定的类,然后调用 getDeclaredMethods 来获取这个类中定义了的方法列表。java.lang.reflect.Methods 是用来描述某个类中单个方法的一个类。


    2.开始使用 Reflection

      用于 reflection 的类,如 Method,可以在 java.lang.relfect 包中找到。使用这些类的时候必须要遵循三个步骤:第一步是获得你想操作的类的 java.lang.Class 对象。在运行中的 Java 程序中,用 java.lang.Class 类来描述类和接口等。
    下面就是获得一个 Class 对象的方法之一:

    Class c = Class.forName("java.lang.String");


      这条语句得到一个 String 类的类对象。还有另一种方法,如下面的语句:

    Class c = int.class;


      或者

    Class c = Integer.TYPE;


      它们可获得基本类型的类信息。其中后一种方法中访问的是基本类型的封装类 (如 Integer) 中预先定义好的 TYPE 字段。

      第二步是调用诸如 getDeclaredMethods 的方法,以取得该类中定义的所有方法的列表。

      一旦取得这个信息,就可以进行第三步了——使用 reflection API 来操作这些信息,如下面这段代码:

    Class c = Class.forName("java.lang.String");

    Method m[] = c.getDeclaredMethods();

    System.out.println(m[0].toString());


      它将以文本方式打印出 String 中定义的第一个方法的原型。

      在下面的例子中,这三个步骤将为使用 reflection 处理特殊应用程序提供例证。

    模拟 instanceof 操作符

      得到类信息之后,通常下一个步骤就是解决关于 Class 对象的一些基本的问题。例如,Class.isInstance 方法可以用于模拟 instanceof 操作符:

    class A {
    }

    public class instance1 {
    public static void main(String args[]) {
    try {
    Class cls = Class.forName("A");
    boolean b1 = cls.isInstance(new Integer(37));
    System.out.println(b1);
    boolean b2 = cls.isInstance(new A());
    System.out.println(b2);
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      在这个例子中创建了一个 A 类的 Class 对象,然后检查一些对象是否是 A 的实例。Integer(37) 不是,但 new A()是。


    3.找出类的方法

      找出一个类中定义了些什么方法,这是一个非常有价值也非常基础的 reflection 用法。下面的代码就实现了这一用法:

    import java.lang.reflect.*;

    public class method1 {
    private int f1(Object p, int x) throws NullPointerException {
    if (p == null)
    throw new NullPointerException();
    return x;
    }

    public static void main(String args[]) {
    try {
    Class cls = Class.forName("method1");
    Method methlist[] = cls.getDeclaredMethods();
    for (int i = 0; i < methlist.length; i++) {
    Method m = methlist[i];
    System.out.println("name = " + m.getName());
    System.out.println("decl class = " + m.getDeclaringClass());
    Class pvec[] = m.getParameterTypes();
    for (int j = 0; j < pvec.length; j++)
    System.out.println("param #" + j + " " + pvec[j]);
    Class evec[] = m.getExceptionTypes();
    for (int j = 0; j < evec.length; j++)
    System.out.println("exc #" + j + " " + evec[j]);
    System.out.println("return type = " + m.getReturnType());
    System.out.println("-----");
    }
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      这个程序首先取得 method1 类的描述,然后调用 getDeclaredMethods 来获取一系列的 Method 对象,它们分别描述了定义在类中的每一个方法,包括 public 方法、protected 方法、package 方法和 private 方法等。如果你在程序中使用 getMethods 来代替 getDeclaredMethods,你还能获得继承来的各个方法的信息。
    取得了 Method 对象列表之后,要显示这些方法的参数类型、异常类型和返回值类型等就不难了。这些类型是基本类型还是类类型,都可以由描述类的对象按顺序给出。

      输出的结果如下:

    name = f1

    decl class = class method1


    param #0 class java.lang.Object

    param #1 int

    exc #0 class java.lang.NullPointerException

    return type = int

    -----
    name = main

    decl class = class method1

    param #0 class [Ljava.lang.String;

    return type = void


    4.获取构造器信息

      获取类构造器的用法与上述获取方法的用法类似,如:

    import java.lang.reflect.*;

    public class constructor1 {
    public constructor1() {
    }

    protected constructor1(int i, double d) {
    }

    public static void main(String args[]) {
    try {
    Class cls = Class.forName("constructor1");
    Constructor ctorlist[] = cls.getDeclaredConstructors();
    for (int i = 0; i < ctorlist.length; i++) {
    Constructor ct = ctorlist[i];
    System.out.println("name = " + ct.getName());
    System.out.println("decl class = " + ct.getDeclaringClass());
    Class pvec[] = ct.getParameterTypes();
    for (int j = 0; j < pvec.length; j++)
    System.out.println("param #" + j + " " + pvec[j]);
    Class evec[] = ct.getExceptionTypes();
    for (int j = 0; j < evec.length; j++)
    System.out.println("exc #" + j + " " + evec[j]);
    System.out.println("-----");
    }
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      这个例子中没能获得返回类型的相关信息,那是因为构造器没有返回类型。

      这个程序运行的结果是:

    name = constructor1

    decl class = class constructor1

    -----
    name = constructor1

    decl class = class constructor1

    param #0 int

    param #1 double


    5.获取类的字段(域)
    找出一个类中定义了哪些数据字段也是可能的,下面的代码就在干这个事情:

    import java.lang.reflect.*;

    public class field1 {
    private double d;
    public static final int i = 37;
    String s = "testing";


    public static void main(String args[]) {
    try {
    Class cls = Class.forName("field1");
    Field fieldlist[] = cls.getDeclaredFields();
    for (int i = 0; i < fieldlist.length; i++) {
    Field fld = fieldlist[i];
    System.out.println("name = " + fld.getName());
    System.out.println("decl class = " + fld.getDeclaringClass());
    System.out.println("type = " + fld.getType());
    int mod = fld.getModifiers();
    System.out.println("modifiers = " + Modifier.toString(mod));
    System.out.println("-----");
    }
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      这个例子和前面那个例子非常相似。例中使用了一个新东西 Modifier,它也是一个 reflection 类,用来描述字段成员的修饰语,如“private int”。这些修饰语自身由整数描述,而且使用 Modifier.toString 来返回以“官方”顺序排列的字符串描述 (如“static”在“final”之前)。这个程序的输出是:

    name = d

    decl class = class field1

    type = double

    modifiers = private

    -----
    name = i

    decl class = class field1

    type = int

    modifiers = public static final

    -----
    name = s

    decl class = class field1

    type = class java.lang.String

    modifiers =


      和获取方法的情况一下,获取字段的时候也可以只取得在当前类中申明了的字段信息 (getDeclaredFields),或者也可以取得父类中定义的字段 (getFields) 。


    6.根据方法的名称来执行方法

      文本到这里,所举的例子无一例外都与如何获取类的信息有关。我们也可以用 reflection 来做一些其它的事情,比如执行一个指定了名称的方法。下面的示例演示了这一操作:

    import java.lang.reflect.*;
    public class method2 {
    public int add(int a, int b) {
    return a + b;
    }
    public static void main(String args[]) {
    try {
    Class cls = Class.forName("method2");
    Class partypes[] = new Class[2];
    partypes[0] = Integer.TYPE;
    partypes[1] = Integer.TYPE;
    Method meth = cls.getMethod("add", partypes);
    method2 methobj = new method2();
    Object arglist[] = new Object[2];
    arglist[0] = new Integer(37);
    arglist[1] = new Integer(47);
    Object retobj = meth.invoke(methobj, arglist);
    Integer retval = (Integer) retobj;
    System.out.println(retval.intValue());
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }

    假如一个程序在执行的某处的时候才知道需要执行某个方法,这个方法的名称是在程序的运行过程中指定的 (例如,JavaBean 开发环境中就会做这样的事),那么上面的程序演示了如何做到。

      上例中,getMethod用于查找一个具有两个整型参数且名为 add 的方法。找到该方法并创建了相应的Method 对象之后,在正确的对象实例中执行它。执行该方法的时候,需要提供一个参数列表,这在上例中是分别包装了整数 37 和 47 的两个 Integer 对象。执行方法的返回的同样是一个 Integer 对象,它封装了返回值 84。


    7.创建新的对象

      对于构造器,则不能像执行方法那样进行,因为执行一个构造器就意味着创建了一个新的对象 (准确的说,创建一个对象的过程包括分配内存和构造对象)。所以,与上例最相似的例子如下:

    import java.lang.reflect.*;

    public class constructor2 {
    public constructor2() {
    }

    public constructor2(int a, int b) {
    System.out.println("a = " + a + " b = " + b);
    }

    public static void main(String args[]) {
    try {
    Class cls = Class.forName("constructor2");
    Class partypes[] = new Class[2];
    partypes[0] = Integer.TYPE;
    partypes[1] = Integer.TYPE;
    Constructor ct = cls.getConstructor(partypes);
    Object arglist[] = new Object[2];
    arglist[0] = new Integer(37);
    arglist[1] = new Integer(47);
    Object retobj = ct.newInstance(arglist);
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      根据指定的参数类型找到相应的构造函数并执行它,以创建一个新的对象实例。使用这种方法可以在程序运行时动态地创建对象,而不是在编译的时候创建对象,这一点非常有价值。


    8.改变字段(域)的值

      reflection 的还有一个用处就是改变对象数据字段的值。reflection 可以从正在运行的程序中根据名称找到对象的字段并改变它,下面的例子可以说明这一点:

    import java.lang.reflect.*;

    public class field2 {
    public double d;

    public static void main(String args[]) {
    try {
    Class cls = Class.forName("field2");
    Field fld = cls.getField("d");
    field2 f2obj = new field2();
    System.out.println("d = " + f2obj.d);
    fld.setDouble(f2obj, 12.34);
    System.out.println("d = " + f2obj.d);
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      这个例子中,字段 d 的值被变为了 12.34。


    9.使用数组

      本文介绍的 reflection 的最后一种用法是创建的操作数组。数组在 Java 语言中是一种特殊的类类型,一个数组的引用可以赋给 Object 引用。观察下面的例子看看数组是怎么工作的:

    import java.lang.reflect.*;

    public class array1 {
    public static void main(String args[]) {
    try {
    Class cls = Class.forName("java.lang.String");
    Object arr = Array.newInstance(cls, 10);
    Array.set(arr, 5, "this is a test");
    String s = (String) Array.get(arr, 5);
    System.out.println(s);
    } catch (Throwable e) {
    System.err.println(e);
    }
    }
    }


      例中创建了 10 个单位长度的 String 数组,为第 5 个位置的字符串赋了值,最后将这个字符串从数组中取得并打印了出来。

      下面这段代码提供了一个更复杂的例子:

    import java.lang.reflect.*;

    public class array2 {
    public static void main(String args[]) {
    int dims[] = new int[]{5, 10, 15};
    Object arr = Array.newInstance(Integer.TYPE, dims);
    Object arrobj = Array.get(arr, 3);
    Class cls = arrobj.getClass().getComponentType();
    System.out.println(cls);
    arrobj = Array.get(arrobj, 5);
    Array.setInt(arrobj, 10, 37);
    int arrcast[][][] = (int[][][]) arr;
    System.out.println(arrcast[3][5][10]);
    }
    }


      例中创建了一个 5 x 10 x 15 的整型数组,并为处于 [3][5][10] 的元素赋了值为 37。注意,多维数组实际上就是数组的数组,例如,第一个 Array.get 之后,arrobj 是一个 10 x 15 的数组。进而取得其中的一个元素,即长度为 15 的数组,并使用 Array.setInt 为它的第 10 个元素赋值。

      注意创建数组时的类型是动态的,在编译时并不知道其类型。
    展开全文
  • 反射(Reflection)详解

    2018-08-09 13:37:24
    Reflection 产生原因:运行时获取、修改数据(类信息、成员数据信息)。 反射入口 java.lang.Class Class对象分为基本类型(8种)、引用类型:(具体内容在文章末尾) JVM会为每个对象(基础和引用类型)实例化一...

    Reflection

    产生原因:运行时获取、修改数据(类信息、成员数据信息)。

    反射入口 java.lang.Class

    Class对象分为基本类型(8种)、引用类型:(具体内容在文章末尾)

    JVM会为每个对象(基础和引用类型)实例化一个java.lang.Class实例。通过这个Class对象,可以在运行时访问对象的属性和类信息、也可以创建新的对象和类。反射的实现,首先要获取Class实例。

    获取Class实例的方法:

    • Object.getClass();
      Class class = object.getClass();
      枚举类型的对象获取的是枚举类的Class,数组对象获取的是数组元素的Class
    • .class
      Objcet.class; 
    
      int.class.newInstance()
    • Class.forName().
      只能用于引用类型,需要类的完整路径:如java.lang.String
    
      Class<?> class = Class.forName("java.lang.String");
    • static属性TYPE
      Class<String> class = String.TYPE;
      TYPE的定义:public static final Class<String> TYPE = (Class<String>) String[].class.getComponentType();

    获取相关类的Class对象:

    • 父类 getSuperclass()
    • 公共类、接口、枚举等 getClasses()
    • 显示申明的所有类、接口、枚举 getDeclaredClasses()
    • 成员声明类 getDeclaringClasses()

    Class对象获取类的信息:

    • 获取类的修饰符(修饰符介绍见本文末)
    
      int modifier = class.getModifiers() ;
    
      String modifierStr = Modifier.toString(int modifiers);
    
      同时因为Field继承了AnnotatedElement,所以在运行时可以获取注解,获取的注解必须是RUNTIME所修饰的注解

    获取类成员(Member)

    • 构造函数(Constructor)
    getDeclaredConstructor(Class<?>... parameterTypes)获取指定的构造方法,可获取私有方法
    getConstructor(Class<?>... parameterTypes) 获取指定的构造方法,不可获取私有方法
    getDeclaredConstructors() 获取所有构造方法,可获取私有方法
    getConstructor(Class<?>... parameterTypes) 获取所有构造方法,不可获取私有方法
    • 成员变量(Field)

    获取变量:

    getDeclaredField(String name);// 获取指定的变量   类自有变量  包括取私有变量,但是不能获取继承父类的
    
    getField(String name);  //获取指定的变量,可获取自身和继承自父类的public变量。
    
    getDeclaredFields(); //获取类自有的所有变量集合,包括私有变量,不包括继承变量
    
    getFields(); //获取自身和继承自父类的public变量的集合。

    获取变量类型:

    Field.getType() :返回Class对象  ,这个变量的类型
    Field.getGenericType():返回Type接口  ,如果类型是泛型,getGenericType可以得到这个泛型的参数类型(T),getType()只能得到这个类型的接口类型(Object)。

    设置变量的值

    Class cls = object.getClass();
    
    Field field = objcet.getField("name");
    
    field.set(object,"new Name");
    
    //修改变量的值时,编译器不会拆装箱操作,设置时必须设置相同的类型
    
    Integer itg;
    
    Class cls = object.getClass();
    
    Field field = objcet.getField("itg");
    
    field.set(object,new Integer(666));
    
    //直接修改final类型的变量会报错`IllegalAccessException` 需要setAccessible(true),
    //就可以访问修改了。(由于 `Field` 继承自 `AccessibleObject` , 我们可以使用 
    //`AccessibleObject.setAccessible()` 方法告诉安全机制,这个变量可以访问。 )
    • 成员方法(Method:java.lang.reflect.Method)
    //获取的方法属性同成员变量。
    getDeclaredMethod(String name,Class parameterTypes.....)
    getMethod(String name,Class parameterTypes.....)
    getDeclaredMethods()
    getMethods()
    

    继承于父类的方法因强制执行无法被反射,因此反射方法仅限本类方法。

    反射方法得到的数据由:修饰符、返回值、参数、注解、抛出异常五点组成:

    getName());  //获得单独的方法名 
    
    toGenericString()); //获得完整的方法信息(包括修饰符、返回值、路径、名称、参数、抛出值) 
    
    int modifiers = declaredMethod.getModifiers();Modifier.toString(modifiers)    /获得修饰符 
    
    getReturnType();   //获得返回值 
    
    getGenericReturnType();//获得完整信息的返回值 
    
    //获得参数类型 
    Class<?>[] parameterTypes = method.getParameterTypes(); 
    Type[] genericParameterTypes = method.getGenericParameterTypes();
    
    //获得异常名称 
    Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes();   
    Type[] genericExceptionTypes = declaredMethod.getGenericExceptionTypes();   
    
     //获得注解 
    Annotation[] annotations = declaredMethod.getAnnotations(); 
    annotations[i].annotationType();

    三种特殊的方法类型:

    synthetic(合成,内部类访问权限), varagrs(参数可变), bridge(桥接,泛型相关)
    

    反射 调用方法

    invoke() ;
    //java.lang.reflect.Method.invoke(Object receiver,Object... args)
    //该方法抛异常:java.lang.reflect.InvocationTargetException
    receiver 是方法所属于的对象,静态方法可直接传null,args传方法的参数
    //当访问private属性的方法时,需要设置setAccessible(true) 否则报`IllegalAccessException`异常
    
    

    相似概念:Introspection(自省):

    区别:

    Introspection 是在运行时获取对象的信息,Reflection是获取或者修改对象的信息
    Introspection多用于 instanceof 的判断,Reflection多用来实现访问私有方法,创建对象等
    总的说,Reflection是多了可进行修改信息。
    

    附录 :

    Class对象的基本类型(8种)、引用类型:

    基本类型:

    • 整型:默认类型int

      • byte : 8位、有符号的二进制补码表示整数、范围-128~127(-2^7~2^7-1)
      • short : 16位、有符号的二进制补码表示整数、范围-32768~32767(-2^15~2^15-1)
      • int : 32位、有符号的二进制补码表示整数、范围-214748348~2147483647(-2^31~2^31-1)
      • long : 64位 、 范围-9223372036854775808~9223372036854775807(-2^63~2^63-1) 。
        long a =1000000000000L;
    • 浮点类型:默认类型double

      • float : 单精度、32位浮点数,存储大型浮点数组较为节省空间、不能用来表示精确值如货币
      • double : 双精度、64位、也不可表示精确的数。
    • bool值
      boolean : true/false、 默认值false

    • 字符型

      • char : 单一的16位Unicode字符、最大值0(\u0000)、最大值65535(\uffff) 。char letter=”A”;

    引用类型:

    • 引用类型都是继承自java.lang.Object
    • 类、枚举、数组、接口
    • java.io.Serializable接口及其实现类、包装类Double、Integer。

    修饰符Modifier

    修饰符用来定义类、方法、变量,通常在语句首位,分为两大类:访问修饰符和非访问修饰符

    访问修饰符:

    default:

        缺省值,不写明修饰符,在同一包内可见(类,接口,变量,方法可用)
    
        接口中变量隐式声明为public static final,方法隐式声明为public
    

    public:

        对所有的类可见(类、接口、方法、变量可用)
    
        java 中main方法一定为共有的
    

    protected:

        同一个包中的所有子类和其他类都可以访问(变量、方法可用,不能修饰类(外部类)和接口(及接口的变量和方法))
    
        不同包中的子类可见(变量、方法可用,不能修饰类(外部类)和接口(及接口的变量和方法)),其他类不能访问。
    
        不同包中的子类不能使用父类的实例对象访问其父类的protect属性的变量和方法。即:superClass.protectParameter不可见。只能subClass.protectParameter访问。
    
        避免不相关的类访问。
    

    private:

        最严格访问级别,为了隐藏类实现的细节,保护类的数据,外部类和接口不能声明为private。
    
        只能在同一个类内可见(变量、方法可用,不能修饰外部类和接口(及接口的变量和方法))
    

    访问修饰符在继承中的规则:子类权限必须大于父类,具体如下:

        1.父类public子类必为public.
        2.父类protected子类必须protected/public.
        3. 父类private子类无法继承.
    

    非访问修饰符:

    abstract:

        创建抽象类和抽象方法
    
        一个类不能同时被abstract和final修饰,一个类只要包含抽象方法,就一定要声明为抽象类。抽象类可以包含抽象和非抽象的方法,也可以不包含抽象方法。
    
        抽象方法不能被声明为static 和final,子类必须实现父类的抽象方法,除非子类也是抽象类
    

    static:

        可以直接通过类名访问static修饰的变量和方法,如StaticClass.variableName /StaticClass.methodName().
    
        static 变量:也叫类变量,属于类所有,独立于类的实例对象(实例对象可以有多个,类变量只有一份),
        只能修饰成员变量,不可修饰局部变量。
    
        static 方法:类方法,独立于类的实例对象,方法内不能包含非静态的成员变量。
    

    final:

        final修饰的类不能被继承,方法不能被子类重写,变量为常量不可修改。
    
        final修饰的变量必须显示初始化并且只能赋值一次。
    
        final变量不可修改,是指的其引用不可修改,引用变量的值改变,fianl变量包含的数据是可以改变的。:
        final Person p = new Person(24,name);
        p.setAge(25);
    
        final一般结合static创建常量。static final String CONSTANT_STRING = "I'M CONSTANT STRING"
    
        final修饰的方法不可被子类修改,可以防止该方法被改动
    

    synchronized:

        说明被修饰的方法在同一个时间只能被一个线程访问,synchronized修饰符可以用应于 所有访问修饰符(public、default、private、protected)
    

    transient:

        序列化对象时,JVM会跳过transient修饰的变量。public transient String str = "I'll be ignored";
    

    volatile

        volatile修饰的成员变量在每次被线程访问时,都需要从共享内存中重新读取该成员变量的值,
        当此成员变量发生变化时,会强制线程将变化值写回共享内存,因此,在任何时刻,多个线程中此成员变量的值都相同。
    

    native

        在JAVA中调用非JAVA语言写的方法(如C、C++) :native void nativeMethod(.....);
        native 不可以和abstract修饰符一起使用。(native虽然在JAVA代码没有方法体,但本地方法是有实现的)
        使用native主要是为了和java环境和操作系统等交互,本地方法在JAVA加载是,放在DLL文件中,方法native方法调用前,本地方法会被加载。
    

    Thanks:

    https://blog.csdn.net/u011240877/article/details/54604146

    展开全文
  • 镜像矩阵(Reflection

    千次阅读 2018-10-11 15:53:46
    镜像(反射)矩阵是n维空间中的沿n-1维平面的一种矩阵变换,常见的应用场景是在2维空间图像处理、3维空间物体场景变换。先直观看看镜像变换的效果:    直观的感受了镜像变换的效果之后,接下来我们看看这个变换...

            镜像(反射)矩阵是n维空间中的沿n-1维平面的一种矩阵变换,常见的应用场景是在2维空间图像处理、3维空间物体场景变换。先直观看看镜像变换的效果:

                                                     

            直观的感受了镜像变换的效果之后,接下来我们看看这个变换的数学表达式是什么样的。首先n维度空间的镜像变换是基于某个n-1维度平面(对于2维度就是某条直线)来说的,线性代数的知识知道n维空间的n-1维平面存在法向量,假设u=(u_{1},u_{2},...,u_{n})是要做镜像平面的单位法向量,单位法向量||u||=1,定义镜像矩阵Q=I-2uu^{T}

            1. Q是对称和标准正交的,\because Q^{T}=(I-2uu^{T})^{T}=I-2uu^{T}\therefore Q是对称的。\becauseQ^{T}Q=I-4uu^{T}+4uu^{T}uu^{T}=I\thereforeQ是标准正交的。如果对矩阵运算背后的原理不太熟悉,我们也可以将Q展开:

            Q=I-2uu^{T}=\begin{bmatrix} 1-2u_{1}^{2} &-2u_{1}u_{2} &... &-2u_{1}u_{n} \\ -2u_{2}u_{1}&1-2u_{2}^{2} &... &-2u_{2}u_{n} \\ ...& ... & ... &... \\ -2u_{n}u_{2} & -2u_{n}u_{2} &... & 1-2u_{n}^{2} \end{bmatrix}

            由Q的矩阵展开形式,很明显看到Q_{ij}=Q_{ji}=-2u_{i}u_{j},i\neq j,所以Q是对称的。标准正交:q_{i},q_{j}是Q的两列,由标准正交的定义,需要满足如下条件:

            q_{i}q_{j}=\begin{cases} 0& \text{ if } x\neq j \\ 1& \text{ if } x=j \end{cases}

            q_{i}q_{j}=(1-2u_{i}^2)^{2}+4u_{i}^2u_{1}^2+...+4u_{i}^{2}u_{n}^2

                    =1+4u_{i}^4-4u_{i}^2+4u_{i}^2u_{1}^2+...+4u_{i}^{2}u_{n}^2

                    =1+4u_{i}^2(u_{1}^{2}+...+u_{n}^{2})-4\overset{||u||^{2}=1}{\rightarrow}

                    =1 (i=j)

             q_{i}q_{j}=(1-2u_{i}^2)(-2u_{i}u{j})+(1-2u_{j}^{2})(-2u_{j}u_{i})+4u_{i}u_{j}u_{1}^2+...4u_{i}u_{j}u_{k}^2...+4u_{i}u_{j}u_{n}^2,k\neq i,j

                          =-4u_{i}u_{j}+4u_{i}u{j}(u_{1}^2+...+u_{n}^2)

                          =0

             2.Q^{2}=Q^{T}Q=I,这条性质反应了镜像的特点,镜像两次等于原空间。

            上面是镜像矩阵的定义和它的一些性质和推导,那么我们再做一点深层次的思考:为什么镜像的表达会是一个矩阵变换,如果我做旋转是不是也是一个矩阵变换?首先,问题2的答案:YES,2维空间的旋转的变换矩阵是:\begin{bmatrix} cos\Theta & -sin\Theta \\ sin\Theta& cos\Theta \end{bmatrix}

            对于问题1,矩阵变换的本质可以理解成坐标系的变换,对于镜像和旋转我们都是对原坐标系中的向量(坐标系中的点可以表示成原点指向该点的向量)用新的坐标系中的基进行了表示。比如二维直角坐标系x0y的坐标基向量是(1,0)和(0,1),如果用矩阵表示:\begin{bmatrix} 1 &0 \\ 0 &1 \end{bmatrix},基向量就是矩阵的列向量,(1,1)点这个坐标系下的坐标(x,y)由方程组\begin{bmatrix} 1 &0 \\ 0 &1 \end{bmatrix}\begin{bmatrix} x\\ y \end{bmatrix}=\begin{bmatrix} 1\\ 1 \end{bmatrix}求得(x,y)=(1,1)因为在原坐标系下,假设我们对(1,1)点做沿x轴做镜像,这里的镜像很简单,x轴和原坐标系相同,y轴与原坐标系反号,所以镜像之后新的坐标系基向量是(1,0)和(0,-1)(如果你通过上面的镜像公式可以求得同样的答案,u=(0,1)),新的坐标系的矩阵表示\begin{bmatrix} 1 &0 \\ 0 &-1 \end{bmatrix},原坐标系下(1,1)点在新坐标系下的坐标有方程组\begin{bmatrix} 1 &0 \\ 0 &-1 \end{bmatrix}\begin{bmatrix} x\\ y \end{bmatrix}=\begin{bmatrix} 1\\ 1 \end{bmatrix}求得(x,y)=(1,-1),和我们直观认知一样:

                                                         

            最后我们说说2维度空间和3维空间的镜像矩阵的表达式,首先是2维空间,对于2-D空间的某个条直线做镜像,假设该直线的单位法向量u(x,y),由Q=I-2uu^{T}计算得到2-D空间的镜像矩阵:

            Q_{2d}=\begin{bmatrix} 1-2x^{2} &-2xy \\ -2xy& 1-2y^{2} \end{bmatrix}

            对于3-D空间的某平面做镜像:

            Q_{3d}=\begin{bmatrix} 1-2x^{2} &-2xy &-2xz \\ -2xy& 1-2y^{2} &-2yz \\ -2xz& -2yz &1-2z^{2} \end{bmatrix}

            镜像矩阵的另一种表示形式:Q'=2P-I, 其中P是投影矩阵,这个表达式和Q=I-2uu^{T}等价。我们证明一下2纬空间沿着某条线做镜像的情况下这两个矩阵的等价性。

    证明:

            假设a是P投影方向上的单位向量,u是垂直与投影方向上的单位向量,那么p=aa^{T},Q'=2P-I=2aa^{T}-I

            Q'-Q=2aa^{T}+2uu^{T}-2I=\begin{bmatrix} 2a_{1}^{2}+2u_{1}^{2} -2 &2a_{1}a_{2}+2u_{1}u_{2} \\ 2a_{2}a_{1}+2u_{2}u_{1}&2a_{2}^{2}+2u_{2}^2-2 \end{bmatrix}

            假设a与x轴的夹角为\theta,那么u与x轴的夹角为\theta +\pi /2,那么:

             a_{1}=cos\theta ,a_{2}=sin\theta

            u_{1}=cos(\theta+\pi/2)=-sin(\theta),u_{2}=sin(\theta+\pi/2)=cos\theta

            带入Q’-Q中得到:

            2a_{1}^{2}+2u_{1}^{2}-2=0

            2a_{2}^{2}+2u_{2}^{2}-2=0

            2a_{1}a_{2}+2u_{1}u_{2}=0

            所以Q'-Q=0,所以Q'-Q得证。

     

     

     

    展开全文
  • //新建一个类库项目,增加一个GetSum方法。 using System; namespace ClassLibrary1 { public class Class1 { public Class1() { } public int GetSum(int x, int y) ...

    展开全文
  • 以下分析来源:Seeing Deeply and Bidirectionally: A Deep Learning Approach for Single Image Reflection Removal 这篇文章说它提出了级联深度神经网络(cascade deep netural network),尽管每个深度学习都是...
  • Shader 反射光(reflection

    千次阅读 2016-10-25 11:16:18
    Unity中的shader使用反射光,相当于我们初中物理的反射光,入射光,发现,发射光的知识,实际上就是模拟现实中的光照。 但是Unity中的反射光向量Unity自己计算好的,不需要我们来计算,然后下文看书中的Cubemap有...
  • 什么是反射(Reflection )? 主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。 什么是Java反射? Java反射指的是在Java程序运行状态中,对于任何一个类,都可以获得这个类的所有属性和方法;对于给定...
  • Exploiting Reflection Change for Automatic Reflection Removal.去除像反光
  • C#中反射:Reflection

    2018-09-03 19:16:36
    **反射:**Reflection 定义: 动态获取类型信息,动态创建对象,动态访问成员的过程。 作用: 在编译时无法了解类型,在运行时获取类型信息,创建对象,访问成员。 举例说明: 1:在写万能单利模式的时候,由于...
  • unity中reflectionProbe研究

    千次阅读 2018-07-13 14:13:18
    unity提供的reflectionProbe可以提供反射周围真实环境的伪反射; 下面先从属性功能上解释一二: bake类型的type,其作用就是烘焙出反射贴图(cubemap)。当你需要反射贴图时,自己生成一个,多...
  • Unity Reflection Probe 测试

    万次阅读 2016-01-21 15:13:09
    unity升级到5.0版本以后,新加了很多东西,如果你还陷在老项目的泥潭中无法升级,就只能自己装个5.x体验了。...同时使用了一个Reflection Probe,就是一个反射探头,能记录一定空间的环境。如果使用了standard这
  • 公共技术点之 Java反射 Reflection

    千次阅读 热门讨论 2015-02-09 15:28:53
    公共技术之 Java反射 Reflection1. 了解Java中的反射1.1 什么是Java的反射Java反射是可以让我们在运行时获取类的函数、字段、父类、接口等Class内部信息的机制。通过反射还可以让我们在运行期实例化对象,调用方法,...
  • C# 反射(Reflection)

    2017-09-21 14:45:19
    原文链接:http://www.runoob.com/csharp/csharp-reflection.html反射指程序可以访问、检测和修改它本身状态或行为的一种能力。 程序集包含模块,而模块包含类型,类型又包含成员。 反射则提供了封装程序集、模块...
  • Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。java本来是一个静态的...
  • Unity5.x Reflection Probe反射探针

    万次阅读 2017-07-27 16:47:29
    下面说一下Reflection Probe, 这东西很扯, 工作原理在这里摆着呢, 显然Reflection无论怎么调节Bound的或者采样点的位置, 都无法适应一个”房间”或者一个”地板”, 他只适用于一个玻璃球, 一个杯子, 或者是其他的...
  • 版权声明:本文为博主原创文章,无需授权即可转载,甚至无需保留以上版权声明,转载时请务必注明作者。...解决——》MyBatisSystemException: nested exception is org.apache.ibatis.reflection.Reflection...
  • unity中的reflectionProbe的使用

    千次阅读 2017-09-05 14:23:04
    下面说一下Reflection Probe, 大家都知道:当使用标准着色器时,每一个材质都会具有一定程度的镜面反射(specularity)和金属反射 (metalness)属性,在没有强大的硬件来处理即时光迹追踪反射的情况下,我们得仰赖预先...
  • C#反射(Reflection)的概念和用法详解

    千次阅读 2016-12-07 14:52:08
    C#反射(Reflection)的概念和用法详解 1、 什么是反射 2、 命名空间与装配件的关系 3、 运行期得到类型信息有什么用 4、 如何使用反射获取类型 5、 如何根据类型来动态创建对象 6、 如何获取方法...
  • Java Reflection in Action 英文书 pdf和文中源代码
  • Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息...
1 2 3 4 5 ... 20
收藏数 100,171
精华内容 40,068
关键字:

reflection