欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java反射

程序员文章站 2022-04-14 16:45:40
Java反射机制在程序运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态的获取信息以及动态调用对象的方法 的功能称为java的反射机制。 首先你需要了解类加载的过程,这里我们简单提一下(加载-验证-准备-解析-初始化),反射是靠JV ......

Java反射

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

  • 在运行时分析类的能力
  • 在运行时检查对象,例如,编写一个适用于所有类的toString方法
  • 实现泛型数组操作代码
  • 利用Method对象
1、Class类

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

  1. class.getClass()
  2. Class.forName(className)
  3. class.class

虚拟机为每个类型管理一个唯一的Class对象。因此,可以使用==运算符实现两个类对象的比较。例如,

if(Employee.getClass()==Employee.class)//返回结果为true

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

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

如果这个类没有无参数的构造器,getConstructor方法会抛出一个异常。

API

java.lang.Class

  • static Class forName(String className)
    返回一个Class对象,表示名为className的类
  • Constructor getConstructor(Class… paramterTypes)
    生成一个对象,描述有指定参数类型的构造器

java.lang.reflect.Constructor

  • Object newInstance(Object… params)
    将params传递到构造器,来构造这个构造器声明类的一个新实例

2、资源

类通常有一些关联的数据文件,例如

  • 图像和声音文件
  • 包含消息字符串和按钮标签的文本文件。

Class类提供了一个很有用的服务可以查找资源文件。下面给出必要的步骤:

  1. 获得拥有资源的类的Class对象,例如,ResourceTest.class
  2. 有些方法,如ImageIcon类的getImage方法,接受描述资源位置URL。则要调用
URL url = class.getResource("about.gif")
  1. 否则,使用getResourceAsStream方法得到一个输入流来读取文件中的数据

ResourceTest.java

/**
 * 使用反射读取资源
 */
public class ResourceTest {
    public static void main(String[] args) throws IOException {
        Class cl = ResourceTest.class;
        URL url = cl.getResource("about.jpg");
        ImageIcon icon = new ImageIcon(url);

        InputStream stream = cl.getResourceAsStream("about.txt");//相对路径
        byte[] bytes = new byte[1024];
        stream.read(bytes);
        String about = new String(bytes, "UTF-8");

        InputStream stream2 = cl.getResourceAsStream("/com/company/reflection/about.txt");//绝对路径
        stream2.read(bytes);
        String title = new String(bytes, StandardCharsets.UTF_8).trim();

        JOptionPane.showMessageDialog(null,about,title,JOptionPane.INFORMATION_MESSAGE,icon);
    }
}
API

java.lang.Class

  • URL getResource(String name)
  • InputStream getResourceAsStream(String name)
    找到与类位于同一位置的资源,返回一个可以用来加载资源的URL或者输入流。如果没有找到资源,则返回null,所以不会抛出异常或者I/O错误

3、利用反射分析类的能力

在java.lang.reflect包中有三个类Field、Method、Constructor分别用来描述类的字段、方法和构造器。这三个类都有一个叫做getName的方法,用来返回字段、方法或构造器的名称。
Field类有一个getType方法,用来返回描述字段类型的一个对象,这个对象的类型同样是Class。Method和Constructor类有报告参数类型的方法,Method类还有一个报告返回类型的方法。这三个类都有一个名为getModifiers的方法,它将返回一个整数,用不同的0/1位描述所使用的修饰符,如public和static。另外,还可以利用java.lang.reflect包中的Modifier类的静态方法分析getModifiers返回的这个整数。例如,可以使用Modifier类中的isPublic、isPrivate或isFinal判断方法或构造器是public、private还是final。我们需要做的就是在getModifiers返回的整数上调用Modifier类中适当的方法,另外,还可以利用Modifier.toString方法将修饰符打印出来。
ReflectionTest.java

/**
 * 使用反射分析类的能力
 */
public class ReflectionTest {
    public static void main(String[] args) throws ClassNotFoundException {
        String name;
        if(args.length>0) name = args[0];
        else
        {
            Scanner in = new Scanner(System.in);
            System.out.println("Enter class name (e.g. java.util.Date): ");
            name = in.next();
        }

        Class cl = Class.forName(name);//使用name获取Class
        Class supercl = cl.getSuperclass();//获取父类的class
        String modifiers = Modifier.toString(cl.getModifiers());//获取修饰符
        if(modifiers.length()>0) System.out.print(modifiers+" ");
        System.out.print("class "+name);
        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("}");
    }

    /**
     * prints all constructors of a class
     */
    public static void printConstructors(Class cl){
        Constructor[] constructors = cl.getDeclaredConstructors();//获取全部构造器的数组

        for(Constructor c : constructors){//遍历构造器数组
            String name = c.getName();//获取构造器的name
            System.out.print(" ");
            String modifiers = Modifier.toString(c.getModifiers());//获取构造器的修饰符
            if(modifiers.length()>0) System.out.print(modifiers+" ");
            System.out.print(name+"(");

            Class[] parameterTypes = c.getParameterTypes();//获取构造器的参数数组
            for(int j = 0; j < parameterTypes.length; j++){//遍历参数数组
                if(j>0) System.out.print(", ");//逗号分隔开
                System.out.print(parameterTypes[j].getName());//打印参数name
            }
            System.out.println(");");
        }
    }

    /**
     * prints all methods of a class
     */
    public static void printMethods(Class cl){
        Method[] methods = cl.getDeclaredMethods();

        for(Method m : methods){
            Class<?> returnType = m.getReturnType();
            String name = m.getName();

            System.out.print(" ");
            //print modifiers,return type and method name
            String modifiers = Modifier.toString(m.getModifiers());
            if(modifiers.length()>0) System.out.print(modifiers + " ");
            System.out.print(returnType.getName() + " " + name + "(");

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

    /**
     * prints all fields of a class
     */
    public static void printFields(Class cl){
        Field[] fields = cl.getDeclaredFields();

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

输入

java.lang.Double

输出

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;
}
API

java.lang.Class

  • Field[] getFields()
    返回这个类支持的公共字段
  • Field[] getDeclaredFields()
    返回类中声明的全部字段
  • Method[] getMethods()
    返回这个类支持的公共方法
  • Field[] getDeclaredMethods()
    返回类中声明的全部方法
  • Constructor[] getConstructors()
    返回这个类支持的公共构造器
  • Constructor[] getDeclaredConstructors()
    返回类中声明的全部构造器

java.lang.reflect.Field
java.lang.reflect.Method
java.lang.reflect.Constructor

  • int getModifiers()
    返回一个整数,描述这个构造器、方法或字段的修饰符。使用Modifier类中的方法来分析这个返回值
  • String getName()
    返回一个表示构造器名、方法名或字段名的字符串
  • Class[] getParamterTypes() (在Constructor 和 Method classes 类中)
    返回一个Class对象数组,其中各个对象表示参数的类型。
  • Class getReturnType() (在 Method 类中)
    返回一个用于表示返回类型的Class对象

java.lang.reflect.Modifier

  • static String toString(int modifiers)
    返回一个字符串,包含对应modifiers中位设置的修饰符

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

在编写程序时,如果知道想要查看的字段名和类型,查看对象中指定字段的内容是一件很容易的事情。而利用反射机制可以查看在编译时还不知道的对象字段。
要做到这一点,关键方法时Field类中的get方法。如果f是一个Field类型的对象,obj是某个包含f字段的类的对象,f.get(obj)将返回一个对象,其值为obj的当前字段值。
ObjectAnalyzerTest.java

public class ObjectAnalyzerTest {
    public static void main(String[] args) throws IllegalAccessException {
        ArrayList<Integer> squares = new ArrayList<>();
        for (int i = 1; i <= 5; i++)
            squares.add(i*i);
        System.out.println(new ObjectAnalyzer().toString(squares));
    }
}

ObjectAnalyzer.java

/**
 * 使用反射在运行时分析对象
 */
public class ObjectAnalyzer {

    private ArrayList<Object> visted = new ArrayList<>();

    /**
     * 将对象转换为列出所有字段的字符串表示形式
     */
    public String toString(Object obj) throws IllegalAccessException {
        if (obj == null) return null;
        if (visted.contains(obj)) return "...";
        visted.add(obj);
        Class cl = obj.getClass();
        if (cl == String.class) return (String)obj;
        if (cl.isArray()){
            String r = cl.getComponentType() + "[]{";// cl.getComponentType:返回表示数组的元素类型的Class
            for (int i = 0; i< Array.getLength(obj); i++){
                if (i > 0) r += ",";
                Object val = Array.get(obj, i);
                if (cl.getComponentType().isPrimitive()) r += val;// 如果是基本类型直接追加
                else r += toString(val);// 如果不是基本类型递归调用toString方法
            }
            return r + "}";
        }

        String r = cl.getName();
        // 检查此类和所有超类的字段
        do{
            r += "[";
            Field[] fields = cl.getDeclaredFields();
            AccessibleObject.setAccessible(fields,true);// 设置一个对象数组的可访问标志
            // 获取所有字段的名称和值
            for (Field field : fields){
                if (!Modifier.isStatic(field.getModifiers())){
                    if (!r.endsWith("[")) r += ",";
                    r += field.getName() + "=";
                    Class type = field.getType();
                    Object val = field.get(obj);// 返回obj对象中用这个Field对象描述的字段的值
                    if (type.isPrimitive()) r += val;// 如果是基本类型直接追加
                    else r += toString(val);// 如果不是基本类型递归调用toString方法
                }
            }
            r += "]";
            cl = cl.getSuperclass();
        }
        while (cl != null);

        return r;
    }
}

输出

java.util.ArrayList[elementData=class java.lang.Object[]{java.lang.Integer[value=1][][],java.lang.Integer[value=4][][],java.lang.Integer[value=9][][],java.lang.Integer[value=16][][],java.lang.Integer[value=25][][],null,null,null,null,null},size=5][modCount=5][][]
API

java.lang.reflect.AccessibleObject

  • void setAccessible(boolean flag)
    设置或取消这个可访问对象的可访问标志,如果拒绝访问则抛出一个IllegalAccessException异常
  • static void setAccessible(AccessibleObject[] array, boolean flag)
    这是一个便利方法,用于设置一个对象数组的可访问标志。

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

CopyOfTest.java

/**
 * 使用反射编写泛型数组代码
 */
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));

        //以下调用将生成异常
        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();// 返回表示数组的元素类型的Class
        int length = Array.getLength(a);
        Object newArray = Array.newInstance(componentType, newLength);// 使用反射包里的Array构造一个新数组,提供两个参数,一个是数组的元素类型,一个是数组的长度
        System.arraycopy(a,0,newArray,0,Math.min(length,newLength));
        return newArray;
    }
}

输出

[1, 2, 3, 0, 0, 0, 0, 0, 0, 0]
[Tom, Dick, Harry, null, null, null, null, null, null, null]
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
	at com.company.reflection.CopyOfTest.main(CopyOfTest.java:20)
API

java.lang.reflect.Array

  • static Object get(Object array,int index)
  • static void set(Object array,int index,Object newValue)
  • static int getLength(Object array)
    返回给定数组的长度
  • static Object newInstance(Class componentType,int Length)
    返回一个有给定类型、给定大小的新数组

6、调用任意方法和构造器

Method类中有一个invoke方法,允许调用包装在Method对象中的方法。invoke方法的签名是:

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

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

/**
 * 使用反射调用任意方法和构造器
 */
public class MethodTableTest {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // 获取指向square和sqrt方法的方法指针
        Method square = MethodTableTest.class.getMethod("square", double.class);// 平方
        Method sqrt = Math.class.getMethod("sqrt", double.class);// 平方根

        // 打印x和y值表
        printTable(1,10,10,square);
        printTable(1,10,10,sqrt);
    }

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

    public static void printTable(double from, double to, int n, Method f) throws InvocationTargetException, IllegalAccessException {
        // 打印该方法作为表头
        System.out.println(f);

        double dx = (to - from) / (n - 1);

        for (double x = from; x <= 10; x += dx){
            double y = (Double) f.invoke(null,x);
            System.out.printf("%10.4f | %10.4f%n",x,y);
        }
    }
}

输出

public static double com.company.reflection.MethodTableTest.square(double)
    1.0000 |     1.0000
    2.0000 |     4.0000
    3.0000 |     9.0000
    4.0000 |    16.0000
    5.0000 |    25.0000
    6.0000 |    36.0000
    7.0000 |    49.0000
    8.0000 |    64.0000
    9.0000 |    81.0000
   10.0000 |   100.0000
public static double java.lang.Math.sqrt(double)
    1.0000 |     1.0000
    2.0000 |     1.4142
    3.0000 |     1.7321
    4.0000 |     2.0000
    5.0000 |     2.2361
    6.0000 |     2.4495
    7.0000 |     2.6458
    8.0000 |     2.8284
    9.0000 |     3.0000
   10.0000 |     3.1623
API

java.lang.reflect.Method

  • public Object invoke(Object implicitParamter, Object[] explicitParamters)
    调用这个对象描述的方法,传入给定参数,并返回方法的返回值。对于静态方法,传入null作为隐式参数。

本文地址:https://blog.csdn.net/kzmzzz/article/details/107878591