reflection_reflections - 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-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得证。

     

     

     

    展开全文
  • 反射(Reflection

    2020-10-09 11:49:01
    反射(Reflection) java是一种静态语言 反射是java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。 Class c = Class.for...

    反射(Reflection)

    • java是一种静态语言

    • 反射是java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

      Class c = Class.forName("java.lang.String")
      
    • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以使用通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为反射。

      • 正常方式:引入需要的“包类”名称 -> 通过new实例化 -> 取得实例化对象
      • 反射方式:实例化对象 -> getClass()方法 -> 得到完整的“包类”名称

    功能

    • 在运行时判断任意一个对象所属的类
    • 在运行时构造任意一个类的对象
    • 在运行时判断任意一个类所具有的成员变量和方法
    • 在运行时获取泛型信息
    • 在运行时调用任意一个对象的成员变量和方法
    • 在运行时处理注解
    • 生成动态代理

    优缺点

    • 优点
      • 可以实现动态创建对象和编译,体现出很大的灵活性
    • 缺点
      • 对性能有影响。使用反射基本上是一种解释操作我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。

    相关api和反射案例

    • 相关API
      • java.lang.Class:代表一个类
      • java.lang.reflect.Method:代表类的方法
      • java.lang.reflect.Field:代表类的成员变量
      • java.lang.reflect.Conftructor:代表类的构造器
    • 反射案例
    public class Test{
    	public static void main(String[] args) throws ClassNotFountException{
    		Calss c1 = class.forName("com.ysu.reflect.User");
    		System.out.println(c1);
    		Calss c2 = class.forName("com.ysu.reflect.User");
    		System.out.println(c1.hashCode()+"\n"+c2.hashCode())
    	}
    }
    class User{
    	private String name;
    	private int id;
    	private int age;
    	get..
    	set..
    	toString..
    }
    

    Class类

    在Object类中定义了一下的方法,此方法将被所有子类继承

    public final Calss getClass()
    

    以上方法返回值得类型是一个Clas类,此类是反射的源头,可以通过对象反射求出类的名称。

    1. Class本身也是一个类
    2. Class对象只能由系统建立对象
    3. 一个加载的类在JVM中只会有一个Class实例
    4. 一个Class对象对应的是一个加载到JVM中的一个class文件
    5. 每个类的实例队徽记得在即是由哪个Class实例所生成的
    6. 通过Class可以完整地得到一个类中的所有倍加载的结构
    7. Class类是Relection的根源,针对任何你想动态加载、运行的类,唯有先获得Class类

    Class类的常用方法

    image-20200929193309479

    获取Class类的实例的方式

    • 若一直具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
    Class clazz = User.class;
    
    • 一直某个类的实例,调用该实例的getClass()方法获取Class对象
    Class clazz = user1.getClass();
    
    • 一直一个类的全类名,且该类在类路径下,可以通过Class类的静态方法forName()获取,可能会抛出ClassNotFoundException
    Class clazz = Class.forName("com.ysu.reflect.User")
    
    • 内置基本数据类型(Byte,Float,Double,Integer,Boolean,Character)可以直接用类名.Type
    • 利用ClassLoader

    具有Class对象的类型

    • class:外部类、成员(成员内部类,静态内部类),局部内部类,匿名内部类
    • interface
    • []
    • enum
    • annotation
    • primitive type
    • void

    累的加载与ClassLoader的理解

    image-20200929200140875

    类加载的底层分析

    什么时候会发生类初始化

    • 类的主动引用(一定会发生类的初始化)

      • 当虚拟机重启动,先初始化main方法所在的类
      • new一个类的对象
      • 调用趔的静态成员(除了final常量)和静态方法
      • 使用java.lang.reflect包的方法对类进行反射调用
      • 当初始化一个类,如果父类没有被初始化,则先会初始化它的父类
    • 类的被动医用(不会发生类的初始化)

      • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致类初始化
      • 通过数组定义类引用,不会触发此类的初始化
      • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)

    通过反射动态创建对象

    • 调用Class对象的newInstance()方法

      • 类必须有一个无参构造器
      • 类的构造器访问权限需要足够
      Class c = Class.forName("com.ysu.reflect.User");
      User user = (User)c.newInstance();
      
    • 通过Constructor实例化对象

      • 通过Calss趔的getDeclaredConstructor(Class…parameterTypes)取得本类的指定形参类型的构造器
      • 项构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数
      • 通过Constructor实例化对象
      Class c = Class.forName("com.ysu.reflect.User");
      Constructor constructor = c.getDeclaredConstructor(String.class, int.class, int.class);
      User user1 = (User)constructor.newInstance("x",1,19);
      
    • 通过反射调用普通方法

      Method.invoke(对象,“方法的参数”)

      Class c = Class.forName("com.ysu.reflect.User");
      User user = (User)c.newInstance();
      Method setName = c.getDeclaredMethod("setName", String.class);
      setName.invoke(user, "x");
      
    • 通过反射操作属性

      不能直接操作私有属性,需要先关闭Java语言访问检查,属性或方法的setAccessible(true).

      setAccessible(true)的作用

      • 提高反射效率,如果代码中必须使用反射,而这句代码需要频繁的被调用,那么请设置为true
      • 使得原本无法访问的私有成员也可以访问
      Class c = Class.forName("com.ysu.reflect.User");
      User user = (User)c.newInstance();
      Field name = c.getDeclaredField("name");
      name.setAccessible(true);
      name.set(user,"x");
      

    性能分析

    image-20200929205953322

    通过反射获取泛型

    • Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除
    • 为了通过 反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型
      • ParameterizedType:表示一种参数化类型,比如Collection
      • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
      • TypeVariable:是各种类型变量的公共父接口
      • WildcardType:代表一种通配符类型表达式
    //获取方法参数泛型类型
    Method method = User.class.getMethod("x",Map.class,List.class);
    Type[] genericParameterTypes = method.getGenericParameterTypes();
    for(Type genericParameterType : genericParameterTypes){
    	if(genericParameterType instanceof ParameterizedType){
    		Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
    		for(Type actualTypeArgument : actualTypeArguments){
    			System.out.println(actualTypeArgument);
    		}
    	}
    }
    //或许返回泛型类型
    Method method = User.class.getMethod("x",Map.class,List.class);
    Type genericReturnType = method.getGenericReturnTypes();
        if(genericReturnType instanceof ParameterizedType){
        Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
        for(Type actualTypeArgument : actualTypeArguments){
        System.out.println(actualTypeArgument);
        }
    }
    
    

    反射操作注解

    • 了解什么是ORM?

      • Object relationship Mapping -->对象关系映射
      • 类和表结构对应
      • 属性和字段对应
      • 对象和记录对应
    • 利用注解和反射完成类和表结构的映射关系

      image-20200929214539064

    image-20200929215734276

    反射的应用

    • 需求:写一个框架,不能改变类的任何代码的前提下,可以创建任意类的对象,执行任意的方法

      • 实现条件:1反射,2配置文件
    • 先创建两个备用类

      public class Student{
      	public viod sleep(){
      		System.out.println("sleep...");
      	}
      }
      
      public class Person{
      	public viod eat(){
      		System.out.println("eat...");
      	}
      }
      
    • 创建一个框架类

      public class ReflectTest{
      	public static void main(String args args){
      		//可以创建任意类的对象,可以执行任意方法
      		//1.加载配置文件
      		//1.1创建Properties对象
      		Properties pro = new Properties();
      		//1.2加载配置文件,转换为一个集合
      		//1.2.1获取class目录下的配置文件
      		ClassLoader classLoader = ReflectTest.class.getClassLoader();
      		InputStream is = classLoader.getResourceAsStream("pro.properties");
      		pro.load(is);
      		//2.获取配置文件中定义的数据
      		String className = pro.getProperty("className");
      		String methodName = pro.getProperty(methodName);
      		//3.加载该类进内存
      		Class cls = class.forName(className);
      		//4.创建对象
      		Object obj = cls.newInstance();
      		//5.获取方法对象
      		Method method = cls.getMethod(methodName);
      		//6.执行方法
      		method.invoke(obj);
      	}
      }
      
    • 创建一个配置文件pro.properties

      className = com.ysu.reflect.Person
      methodName = eat
      
    • 执行结果为

      eat...
      
    • 修改pro.properties配置文件后

      className = com.ysu.reflect.Student
      methodName = sleep
      
    • 执行结果为

      sleep...
      
    展开全文
  • 反射(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(反射)

    2020-10-07 11:34:05
    1.Reflection (反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。 Class c = Class. forName("java.lang.String") ...
  • 以下分析来源:Seeing Deeply and Bidirectionally: A Deep Learning Approach for Single Image Reflection Removal 这篇文章说它提出了级联深度神经网络(cascade deep netural network),尽管每个深度学习都是...
  • 什么是反射(Reflection )? 主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。 什么是Java反射? Java反射指的是在Java程序运行状态中,对于任何一个类,都可以获得这个类的所有属性和方法;对于给定...
  • nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 还需要在bidInfo里面加入user引用
  • org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'xxxx' in 'class xx.xxx.xxx'  at org.mybatis.s.....
  • 正在运行转换: System.Reflection.TargetInvocationException: 调用的目标发生了异常。 ---> System.IO.FileNotFoundException: 无法定位文件  在 Microsoft.VisualStudio.TextTemplating.VSHost....
  • 在 .Net MVC中 原来 直接在 public JsonResult Test() ...序列化类型为“System.Reflection.RuntimeModule”的对象时检测到循环引用 可以考虑 在ajax中增加 dataType:'json'. 及在后台
  • org.mybatis.spring.MyBatisSystemException: nested exception is org.Apache.ibatis.reflection.ReflectionException: There is no getter for property named 'userId1' in ‘entity.java’ 这个异常信息在...
  • org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'createdDate' in 'class com.nowcoder.model.Comment' 分析解决 @Insert({"insert into ", TABLE_NAME...
  • Reflection的getCallerClass的使用

    万次阅读 2015-02-05 15:27:57
    Reflection的getCallerClass的使用:可以得到调用者的类.这个方法是很好用的. 0 和小于0 - 返回 Reflection类 1 - 返回自己的类 2 - 返回调用者的类 3. 4. ....层层上传。 输出结果:-1 : class ...
  • 记录平时遇到过的问题,这个问题并不大,只是太久没写mybatis,忘了一些细节 总结:mybatis传单个String类型,可以不用@param注解,前提是xml中不含有条件表达式(when,if..标签中没有引用到该参数) ...
  • windows7系统运行没有问题,但是放到Windows...System.Reflection.TargetInvocationException: 调用的目标发生了异常。 ---> System.Reflection.TargetInvocationException: 调用的目标发生了异常。 ---> Sy...
  • System.Reflection.TargetInvocationException,This implementation is not part of the windows platform FIPS validated...... 解决方法:  打开注册表,HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control...
  • Unity5.x Reflection Probe反射探针

    万次阅读 2017-07-27 16:47:29
    下面说一下Reflection Probe, 这东西很扯, 工作原理在这里摆着呢, 显然Reflection无论怎么调节Bound的或者采样点的位置, 都无法适应一个”房间”或者一个”地板”, 他只适用于一个玻璃球, 一个杯子, 或者是其他的...
  • “System.Reflection.TargetInvocationException”类型的异常在 mscorlib.dll 中发生,但未在用户代码中进行处理
  • nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for propert named 'ITEM_ID' in 'class java.lang.String' Xml映射文件配置(部分) &lt;insert id="...
1 2 3 4 5 ... 20
收藏数 101,380
精华内容 40,552
关键字:

reflection