Java教程

Java基础学习11--反射机制

本文主要是介绍Java基础学习11--反射机制,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

一、什么是反射

二、Class 类

 三、Class 类的一些常用方法 

1、获取方法信息

2、获取属性信息

3、获取构造方法信息

四、动态调用

1、创建对象

2、修改属性

3、调用方法

4、操作动态数组

 五、总结


一、什么是反射

Java 反射(Reflection)是指 Java 程序在运行时,可以动态的加载、探知、使用编译期间完全未知的类。也就是说,Java 程序可以加载一个运行时才得知类名的类,获得类的完整构造方法,并实例化出对象,给对象属性设定值或者调用对象的方法等。这种在运行时动态获取类的信息以及动态调用对象方法的功能称为 Java 反射机制

例如在使用 Eclipse 时,当开发者定义了一个类Car,里面写了一些方法,再创建Car类对象car并输入car.时,Eclipse 会弹出car对象可用的方法给程序员选择,这些都是反射机制最常见的例子

二、Class 类

Class 类是使用 Java 反射机制的入口,封装了一个类或接口的运行时信息,开发者通过调用 Class 类的方法可以获取这些信息。例如可以通过 Class 中的 getDeclaredMethods()方法获取类的所有方法

下面通过一个案例来演示如何通过以上几种方式获取Class类对象

    import java.lang.reflect.*;
        public class TestClass{
            public static void main(String[] args) {
                //如果将被建模的类类型未知,用Class<?>表示
                Class<?> c1 = null;
                Class<?> c2 = null;
                Class<?> c3 = null;
                Class<?> c4 = null;
                Class<?> c5 = null;
                try{
                //建议采用这种形式
                c1 = Class.forName("java.lang.Object");
                }catch(Exception e){
                    e.printStackTrace();
                }
                c2 = new TestClass().getClass();
                c3 = TestClass.class;
                String name = new String("大力士");
                c4 = name.getClass();
                c5 = name.getClass().getSuperclass();
                System.out.println("Class.forName(\"java.lang.Object\") 类名称:" + c1.getName());
                System.out.println("new TestClass().getClass() 类名称:" + c2.getName());
                System.out.println("TestClass.class 类名称:" + c3.getName());
                System.out.println("String name = \"大力士\"");
                System.out.println("name.getClass() 类名称:" + c4.getName());
                System.out.println("name.getClass().getSuperclass() 类名称:" + c5.getName());
        }
    }

运行结果: 

 三、Class 类的一些常用方法 

  • Field[] getFields()

    返回一个包含 Field 对象的数组,存放该类或接口的所有符合访问修饰符要求的公共属性(含继承而来的属性)。

  • Field[] getDeclaredFields()

    返回一个包含 Field 对象的数组,存放该类或接口中 private 等四种访问修饰符修饰的所有属性(不含继承而来的属性)。可见,该方法可以突破访问修饰符的限制。

  • Method[] getMethods()

    返回一个包含 Method 对象的数组,数组中存放的是该类及父类(或父接口)中用 public 修饰的方法。

  • Method[] getDeclaredMethods()

    返回一个包含 Method 对象的数组,存放该类(或接口)中 private 等四种访问修饰符修饰的所有方法(不含父类或父接口中定义的方法)。可见,该方法也可以突破访问修饰符的限制。

  • Constructor[] getConstructors()

    返回一个包含 Constructor 对象的数组,存放该类中所有用 public 修饰的公共构造方法。

  • Constructor getConstructor(Class[] args)

    返回一个指定参数列表的 Constructor 对象。

  • Class[] getInterfaces()

    返回一个包含 Class 对象的数组,存放该类或接口实现的接口。

  • T newInstance()

    使用无参构造方法创建该类的一个新实例。

  • String getName()

    以 String 的形式返回该类(类、接口、数组类、基本类型或 void)的完整名。

1、获取方法信息

通过 Class 类的 getMethods()方法、getDeclaredMethods()方法、getMethod(String name, Class[] args)方法和 getDeclaredMethod(String name, Class[] args)等方法,程序员可以获得对应类的特定方法组或方法,返回值为 Method 对象数组或 Method 对象。

Class 类的 getDeclaredMethods()方法获得了 Class 所表示的要获取的类的 private 等全部修饰符修饰的方法,但不包括继承而来的方法。使用 Class 类的 getMethods()方法获取的是 Class 所表示的要获取的类及其父类中所有的公共方法(即 public 修饰的方法)。这就是 getDeclaredMethods()和 getMethods()方法的主要区别。 

2、获取属性信息

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Scanner;

public class TestField {
    public static void main(String args[]) {
        try {
            Class c = Class.forName("Sub");
            Scanner input = new Scanner(System.in);
            System.out.print("请输入你想获取Sub类的哪个属性的类型:");
            String name = input.next();
            //通过指定属性名获取属性对象
            Field sf = c.getDeclaredField(name);
            //得到属性类型
            System.out.println("Sub类" + name + "属性的类型为:" + sf.getType());
            System.out.println("****************************************");
            //返回Field对象数组,存放该类或接口的所有属性(不包含父类或父接口中的方法)
            Field flist[] = c.getDeclaredFields();
            System.out.println("Sub类getDeclaredFields()得到的属性如下:");
            //遍历所有属性
            for (int i = 0; i < flist.length; i++) {
                System.out.println("****************************************");
                Field f = flist[i];
                System.out.println("属性" + (i + 1) + "名称为:" + f.getName());    //得到属性名
                System.out.println("该属性所在的类或接口为:" + f.getDeclaringClass());
                System.out.println("该属性的类型为:" + f.getType());        //得到属性类型
                //以整数形式返回由此Field对象表示的属性的Java访问权限修饰符
                int m = f.getModifiers();
                //使用Modifier类对表示访问权限修饰符的整数进行解码显示
                System.out.println("该属性的修饰符为:" + Modifier.toString(m));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3、获取构造方法信息

通过 Class 类,可以获得属性和方法,但是 getDeclaredMethods()和 getMethods()等获取方法的方法只能获得普通方法,不能获得类的构造方法。接下来,通过 Class 类的 getConstructors()方法和 getDeclaredConstructors()方法,可以获得对应类的构造方法,返回值为 Constructor 对象数组

四、动态调用

通过 Class类的方法获取了对应类的属性、方法和构造方法的详细信息,但反射的意义远不止如此。接下来,将通过之前获取的属性、方法和构造方法的详细信息,来动态创建对象、修改属性和调用方法。

1、创建对象

可使用 newInstance()方法和使用 newInstance(Object[] args)方法两种方式实例化对象

到目前为止,可以看出点反射机制的作用—可以根据用户运行时输入的信息,动态创建不同的对象,再调用对象的方法执行相关的功能

通过 Class 类的 newInstance()方法创建对象,该方法要求该 Class 对应类有无参构造方法。执行 newInstance()方法实际上就是使用对应类的无参构造方法来创建该类的实例,其代码的作用等价于Super sup = new Super();

如果要想使用有参构造方法创建对象,则需要先通过 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance(Object[] args)方法来创建该 Class 对象对应类的实例

需要注意的是,通过 Class 对象获得指定 Constructor 对象的方法 getDeclaredConstructor((Class[] args))中,参数列表为 Class 类数组。通过 Constructor 的 newInstance()方法,也可以创建无参对象,只要在调用 getDeclaredConstructor((Class[] args))和 newInstance(Object[] args)方法时,参数列表为空即可。

2、修改属性

import java.lang.reflect.*;
public class TestChangeField{
  public static void main(String args[]) {
    try {
      Class c = Class.forName("Super2");
      Super2 sup = (Super2)c.newInstance();
      //通过属性名获得Field对象
      Field f = c.getDeclaredField("supPri");//supPri为私有属性
      //取消属性的访问权限控制,即使private属性也可以进行访问
      f.setAccessible(true);
      //调用get(Object o)方法取得对象o对应属性值
      System.out.println("取消访问权限控制后访问supPri,其值为:" + f.get(sup));
      //调用set(Object o,Object v)方法设置对象o对应属性值
      f.set(sup, 20);
      System.out.println("f.set(sup, 20)后访问supPri,其值为:" + f.get(sup));
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

3、调用方法

使用反射机制,通过调用 Method 类的一些方法,动态执行 Class 对应类的方法

假设现在有这样的需求,需要在程序运行时,根据用户提供的不同参数列表(方法名称、参数个数、参数类型),动态调用不同的方法完成不同的功能。

import java.lang.reflect.*;
public class TestInvokeMethod {
public int add(int x, int y) {
return x + y;
}

    public int add(int x) {
        return x + 1;
    }

    public int multiply(int x, int y) {
        return x * y;
    }

    public int multiply(int x) {
        return x * x;
    }

    public static void main(String args[]) {
        try {
            Class c = TestInvokeMethod.class;
            Object obj = c.newInstance();
            //通过方法名、参数类型列表,获得Method对象
            Method m = c.getDeclaredMethod("multiply", new Class[]{int.class, int.class});
            //invoke(Object o,Object[] args)方法调用对象o对应方法
            System.out.println("调用方法:multiply,输入值为int型3和4,结果为:"
                    + m.invoke(obj, new Object[]{3, 4}));
            Method m2 = c.getDeclaredMethod("add", new Class[]{int.class});
            System.out.println("调用方法:add,输入值为int型18,结果为:"
                    + m2.invoke(obj, new Object[]{18}));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

4、操作动态数组

Java 在创建数组的时候,需要指定数组长度,且数组长度不可变。而 java.lang.reflect 包下提供了一个 Array 类,这个类中包括一系列 static 方法,通过这些方法可以创建动态数组,对数组元素进行赋值、取值操作

Array 类提供的主要方法(均为静态方法)如下。

  • Object newInstance(Class componentType, int length)

    创建一个元素类型为 componentType、长度为 length 的新数组。

  • Object newInstance(Class componentType, int... dimensions)

    创建一个元素类型为 componentType、长度为 length、维度为 dimensions 的多维数组。

  • void setXxx(Object array, int index,xxx val)

    将数组对象 array 中索引元素的值设置为指定的 xxx 类型的 val 值。

  • xxx getXxx(Object array, int index)

    获取数组对象 array 中索引为 index 的数组元素值。

 五、总结

  • Java 反射是指 Java 程序在运行时,可以动态的加载类,并调用类的属性和方法;
  • 使用反射可以获取类的属性、方法、构造方法、父类、父接口等所有信息,并且可以动态的调用属性、方法;
  • 反射的入口类是 Class,常用的获取 Class 对象的方法有 Class.forName("全类名")**、 **类名.class和 对象名.getClass() 三种;
  • getMethods()只能获取 public 修饰的方法,但这些方法既可以是本类中定义的、也可以在父类(或父接口)中定义的;getDeclaredMethods()可以获取 private 等四种访问修饰符修饰的方法,但这些方法只能是在本类定义的,不包含父类(或父接口)中定义的方法。getDeclaredFields()与 getFields(),及 getDeclaredConstructors()和 getConstructors()方法的区别与之类似;
  • 在使用反射突破访问修饰符限制时,需要先对方法或属性设置 setAccessible(true)
  • Array 类提供了一系列 static 方法,可以创建动态数组,对数组元素进行赋值、取值操作。例如,可以使用 Array.newInstance()方法来创建数组对象。
  • 反射大量存在于后续学习的框架底层,但在初级阶段应用的比较少。
这篇关于Java基础学习11--反射机制的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!