ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java中的反射

2021-02-28 16:02:17  阅读:95  来源: 互联网

标签:lang 反射 Java double public static java Class


反射

反射库(reflection library)提供了一个丰富且精巧的工具集,可以用来编写能够动态操纵Java代码的程序。能够分析类能力的程序称为反射(reflective)。

Class类

在程序运行期间,Java运行时系统始终为所有对象维护一个运行时类型标识 。这个信息会跟踪每个对象所属的类。虚拟机利用运行时类型信息选择要执行的正确的方法。保存这些信息的类名为Class。

获得Class对象的三种方式:

  • Object类中的getClass方法将会返回一个Class类型的实例。
Employee e;
...
Class cl = e.getClass();
  • 还可以使用静态方法forName获得类名对应的Class对象。
String className = "java.util.Random";
Class cl = Class.forName(className);
  • 如果T是任意的Java类型,T.class将代表匹配的类对象。
Class cl1 = Random.class;
Class cl2 = int.class;
Class cl3 = Double[].class;

如果有一个Class类型的对象,可以用它构造类的实例。调用getConstructor方法将得到一个Constructor类型的对象,然后使用newInstance方法来构造一个实例:

var className = "java.util.Random";
Class cl = Class.forName(className);
Object obj = cl.getConstructor().newInstance();

利用反射分析类的能力

可以利用反射机制来检查类的结构。
在java.lang.reflect包中有三个类Field、Method和Constructor分别用于描述类的字段、方法和构造器。这三个类都有一个叫做getName的方法,用来返回字段、方法或构造器的名称。Field类有一个getType方法,用来返回描述字段类型的一个对象,这个对象的类型同样是Class。Method和Constructor类有报告参数类型的方法,Method类还有一个报告返回类型的方法。这三个类都有一个名为getModifiers的方法,它将返回一个整数,用不同的0/1位描述所使用的修饰符,如public和static。另外,还可以使用java.lang.reflect包中Modifier类的静态方法分析getModifiers返回的这个整数。

Class类中的getFields、getMethods和getConstructors方法将分别返回这个类支持的公共字段、方法和构造器的数组,其中包括超类的公共成员。Class类的getDeclareFields、getDeclareMethods和getDeclareConstructors方法将分别返回类中声明的全部字段、方法和构造器的数组,其中包括私有成员、包成员和受保护成员,但不包括超类的成员。

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class ReflectionTest {

    public static void main(String[] args) throws ClassNotFoundException {
        String className = "java.lang.Double";
        Class cl = Class.forName(className);
        Class supercl = cl.getSuperclass();
        String modifiers = Modifier.toString(cl.getModifiers());
        if (modifiers.length() > 0) System.out.print(modifiers + " ");
        System.out.print("class " + className);
        if (supercl != null && supercl != Object.class) System.out.print(" extends " + supercl.getName());
        System.out.print("\n{\n");
        printConstructors(cl);
        System.out.println();
        printMethods(cl);
        System.out.println();
        printFields(cl);
        System.out.println("}");
    }


    public static void printConstructors(Class cl) {
        Constructor[] constructors = cl.getDeclaredConstructors();
        for (Constructor c : constructors) {
            String name = c.getName();
            System.out.print("  ");
            String modifiers = Modifier.toString(c.getModifiers());
            if (modifiers.length() > 0) System.out.print(modifiers + " ");
            System.out.print(name + "(");

            Class[] paramTypes = c.getParameterTypes();
            for (int j = 0;j < paramTypes.length;j++) {
                if (j > 0) System.out.print(", ");
                System.out.print(paramTypes[j].getName());
            }
            System.out.println(");");
        }
    }

    public static void printMethods(Class cl) {
        Method[] methods = cl.getDeclaredMethods();
        for (Method m : methods) {
            Class retType = m.getReturnType();
            String name = m.getName();
            System.out.print("  ");
            String modifiers = Modifier.toString(m.getModifiers());
            if (modifiers.length() > 0) System.out.print(modifiers + " ");
            System.out.print(retType.getName() + " " + name + "(");

            Class[] paramTypes = m.getParameterTypes();
            for (int j = 0;j < paramTypes.length;j++) {
                if (j > 0) System.out.print(", ");
                System.out.print(paramTypes[j].getName());
            }
            System.out.println(");");
        }
    }

    public static void printFields(Class cl) {
        Field[] fields = cl.getDeclaredFields();

        for (Field f : fields) {
            Class type = f.getType();
            String name = f.getName();
            System.out.print("  ");
            String modifiers = Modifier.toString(f.getModifiers());
            if (modifiers.length() > 0) System.out.print(modifiers + " ");
            System.out.println(type.getName() + " " + name + ";");
        }
    }
}

输出:

public final class java.lang.Double extends java.lang.Number
{
  public java.lang.Double(double);
  public java.lang.Double(java.lang.String);

  public boolean equals(java.lang.Object);
  public static java.lang.String toString(double);
  public java.lang.String toString();
  public int hashCode();
  public static int hashCode(double);
  public static double min(double, double);
  public static double max(double, double);
  public static native long doubleToRawLongBits(double);
  public static long doubleToLongBits(double);
  public static native double longBitsToDouble(long);
  public volatile int compareTo(java.lang.Object);
  public int compareTo(java.lang.Double);
  public byte byteValue();
  public short shortValue();
  public int intValue();
  public long longValue();
  public float floatValue();
  public double doubleValue();
  public static java.lang.Double valueOf(java.lang.String);
  public static java.lang.Double valueOf(double);
  public static java.lang.String toHexString(double);
  public static int compare(double, double);
  public static boolean isNaN(double);
  public boolean isNaN();
  public static boolean isFinite(double);
  public static boolean isInfinite(double);
  public boolean isInfinite();
  public static double sum(double, double);
  public static double parseDouble(java.lang.String);

  public static final double POSITIVE_INFINITY;
  public static final double NEGATIVE_INFINITY;
  public static final double NaN;
  public static final double MAX_VALUE;
  public static final double MIN_NORMAL;
  public static final double MIN_VALUE;
  public static final int MAX_EXPONENT;
  public static final int MIN_EXPONENT;
  public static final int SIZE;
  public static final int BYTES;
  public static final java.lang.Class TYPE;
  private final double value;
  private static final long serialVersionUID;
}

使用反射在运行时分析对象

利用反射机制可以查看在编译时还不知道的对象字段。

要做到这一点,关键方法是Field类中的get方法。如果f是一个Field类型的对象,obj是某个包含f字段的类的对象,f.get(obj)将返回一个对象,其值为obj的当前字段值。

var harry = new Employee("Harry",123);
Class cl = harry.getClass();
Field f = cl.getDeclaredField("name");
Object v = f.get(harry);
//v = "Harry"

当然,不仅可以获得值,也可以设置值。调用f.set(obj,value)将把对象obj的f表示的字段设置为新值。

当name是一个私有字段,所以get和set方法会抛出一个IllegalAccessException。只能对可以访问的字段使用get和set方法。Java安全机制允许查看一个对象有哪些字段,但是除非拥有访问权限,否则不允许读写那些字段的值。

反射机制的默认行为受限于Java的访问控制。不过,可以调用Field、Method或Constructor对象的setAccessible方法覆盖Java的访问控制。

f.setAccessible(true);

setAccessible方法是AccessibleObject类中的一个方法,它是Field、Method和Constructor类的公共超类。

使用反射编写泛型数组代码

java.lang.reflect包中的Array类允许动态地创建数组。其中最关键的是Array类的静态方法newInstance,这个方法能够构造一个新数组。

现在来实现一个可以扩展任意类型数组的程序,具体步骤:

  • 首先确认a数组的类对象。
  • 确认它确实是一个数组。
  • 使用Class类的getComponentType方法确定数组的正确类型。

为了实现扩展任意类型的数组,而不仅是对象数组,应该将参数声明为Object类型,而不要声明为对象型数组(Object[])。整型数组类型int[]可以转换成Object,但不能转换成对象数组。

import java.lang.reflect.Array;
import java.util.Arrays;

public class CopyOfTest {

    public static void main(String[] args) {
        int[] a = {1,2,3};
        a = (int[]) goodCopyOf(a,10);
        System.out.println(Arrays.toString(a));

        String[] b = {"tom", "dick", "harry"};
        b = (String[]) goodCopyOf(b,10);
        System.out.println(Arrays.toString(b));

        System.out.println("the following call will generate an exception.");
        b = (String[]) badCopyOf(b,10);
    }

    public static Object[] badCopyOf(Object[] a, int newLength) {
        Object[] newArray = new Object[newLength];
        System.arraycopy(a,0,newArray,0,Math.min(a.length,newLength));
        return newArray;
    }

    public static Object goodCopyOf(Object a, int newLength) {
        Class cl = a.getClass();
        if (!cl.isArray()) return null;
        Class componentType = cl.getComponentType();
        System.out.println(componentType.getName());
        int length = Array.getLength(a);
        Object newArray = Array.newInstance(componentType,newLength);
        System.arraycopy(a,0,newArray,0,Math.min(length,newLength));
        return newArray;
    }

}

输出:

int
[1, 2, 3, 0, 0, 0, 0, 0, 0, 0]
java.lang.String
[tom, dick, harry, null, null, null, null, null, null, null]
the following call will generate an exception.
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
	at section5_5.CopyOfTest.main(CopyOfTest.java:18)

调用任意方法和构造器

反射机制允许调用任意的方法。

前面写了可以用Field类的get方法查看一个对象的字段。与之类似,Method类有一个invoke方法,允许调用包装在当前Method对象中的方法。invoke方法的签名是:

Object invoke(Object obj, Object... args)

第一个参数是隐式参数,其余的对象提供了显示参数。
对于静态方法,第一个参数可以忽略,即可以将它设置为null。

如何得到Method对象呢?可以调用getDeclareMethods方法,然后搜索返回的Method对象数组,直到发现想要的方法为止。也可以调用Class类的getMethod方法得到想要的方法。不过,有可能存在若干个同名的方法,因此要准确地得到想要的那个方法必须格外小心。有鉴于此,还必须提供想要的方法的参数类型。

Method getMethod(String name,Class... parameterTypes)

可以使用类似的方法调用任意的构造器。将构造器的参数类型提供给Class.getConstructor方法,并把参数值提供给Constructor.newInstance方法:

Class cl = Random.class;
Constructor cons = cl.getConstructor(long.class);
Object obj = cons.newInstance(42L);
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MethodTest {

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method square = MethodTest.class.getMethod("square",double.class);
        print(10,square);
    }

    public static double square(double x) {
        return x * x;
    }

    public static void print(double x, Method f) throws InvocationTargetException, IllegalAccessException {
        System.out.println(f);
        double y = (Double) f.invoke(null,x);
        System.out.println(y);
    }

}

输出:

public static double section5_5.MethodTest.square(double)
100.0

标签:lang,反射,Java,double,public,static,java,Class
来源: https://blog.csdn.net/qq_41242680/article/details/114205957

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有