在运行状态中,对于任意一个类,都能够知道其所有属性和方法,对于任意一个对象,都能够调用其任意方法和属性,这种动态获取信息、动态调用方法的能力称为Java语言的反射机制,大部分框架都有用到反射机制,了解反射的使用方法非常重要。
一个类通常包含了属性、方法、构造函数等,而Java一般情况下是现有类再有对象,通过对象调用各种属性和方法,而Java反射则是通过已有的对象,反过来得到其所属类的相关信息,调用所属类的相关方法。
我们知道在Java的世界中,万事万物皆对象。其实类本身也是对象,任何一个类都是Class类的实例对象。
//定义了一个SuperHero的类 public class SuperHero {}
如上面定义的SuperHero类,是类也是对象,
对象:SuperHero类是Class类的实例,Class类是SuperHero的类类型,故而为对象;
类:以类的方式创建,SuperHero本身可以调用SuperHero ironMan = new SuperHero ()被实例化,ironMan 就是创建的实体,故而也是类。
Class类很特殊,它表示了某个类的类类型,被不可被继承,每个类的Class对象仅有一个,Class类没有公共构造函数。 相反, Class对象由Java虚拟机自动构建,因为加载了类,并且通过调用类加载器中的defineClass方法,原始Java类型( boolean , byte , char , short , int , long , float和double ),和关键字void也表示为Class对象
//Class源码,final修饰不可被继承,构造函数是private的,不可手动实例化 public final class Class<T> { private Class(ClassLoader loader) { // Initialize final field for classLoader. The initialization value of non-null // prevents future JIT optimizations from assuming this final field is null. classLoader = loader; } }
public static void main(String[] args) { try { Class clazz1 = Class.forName("java.lang.Integer"); Class clazz2 = Class.forName("java.lang.Integer"); System.out.println(clazz1 == clazz2); System.out.println(int.class); System.out.println(void.class); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
运行结果为:
true int void
先定义一个类
package reflectdemo; import java.io.Serializable; /** * 超级英雄类 */ public class SuperHero implements Serializable { public static final String ADDRESS = "earth"; private String id; private String name; private Integer age; private String skill; public SuperHero() { } public SuperHero(String id, String name, Integer age, String skill) { this.id = id; this.name = name; this.age = age; this.skill = skill; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getSkill() { return skill; } public void setSkill(String skill) { this.skill = skill; } public void print(){ System.out.println("超级英雄:" + this.name); } }
public static void main(String[] args) { SuperHero ironMan = new SuperHero("1","钢铁侠",35, "战甲"); Class clazz = ironMan.getClass(); System.out.println(clazz.getName()); }
输出结果:
reflectdemo.SuperHero
public static void main(String[] args) { Class clazz = SuperHero.getClass(); System.out.println(clazz.getName()); }
输出结果:
reflectdemo.SuperHero
public static void main(String[] args) { try { Class clazz = Class.forName("reflectdemo.SuperHero"); System.out.println(clazz.getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
输出结果:
reflectdemo.SuperHero
第一种方式对象已经有了,所有的操作直接通过该对象进行即可,
第二种方式需要import将类引入,也不是常用的方式,
第三种仅需传入类的路径,即可得到类的相关信息,是最常用的方式。
public static void main(String[] args) { try { Class clazz = Class.forName("reflectdemo.SuperHero"); //获取类名称(含路径) System.out.println(clazz.getName()); //获取类名称(不含路径) System.out.println(clazz.getSimpleName()); //获取所在包 System.out.println(clazz.getPackage()); //通过Class创建对象 SuperHero hero = (SuperHero)clazz.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } }
输出结果:
reflectdemo.SuperHero SuperHero package reflectdemo
这里提前说明一下:Class中两个功能相同的方法,若其中一个带有Declared字样,表示针对类中所有声明的变量、方法、构造函数等,而对应不带Declared字样的方法,则表示仅对公有(public)成员变量、方法起作用,下面不再重复描述,下面仅对带有Declared字样的方法进行讲解。
public class ClassUtils { /** * 获取构造函数 * @param clazz 类 * @param params 构造函数参数类型 * @throws NoSuchMethodException */ public static void getDeclaredConstructor(Class clazz, Class[] params) throws NoSuchMethodException { System.out.println(clazz.getDeclaredConstructor(params)); } } public class ClassTest { public static void main(String[] args) { try { Class clazz = Class.forName("reflectdemo.SuperHero"); //打印无参构造函数 ClassUtils.getDeclaredConstructor(clazz, null); //打印有参构造函数 ClassUtils.getDeclaredConstructor(clazz, new Class[]{String.class, String.class, Integer.class, String.class}); } catch (Exception e) { e.printStackTrace(); } } }
输出结果为:
public reflectdemo.SuperHero() public reflectdemo.SuperHero(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String)
public class ClassUtils { /** * 遍历构造函数 * @param clazz 类 */ public static void getDeclaredConstructors(Class clazz){ //获取所有的构造函数 Constructor[] constructors = clazz.getDeclaredConstructors(); for (Constructor constructor : constructors) { //直接打印构造函数 System.out.println(constructor); //打印构造函数名称 System.out.println(constructor.getName()); //打印构造函数参数 Parameter[] parameters = constructor.getParameters(); for(Parameter parameter : parameters){ System.out.print(parameter); System.out.print(", "); } System.out.println("---------------------"); } } } public class ClassTest { public static void main(String[] args) { try { Class clazz = Class.forName("reflectdemo.SuperHero"); //遍历构造函数 ClassUtils.getDeclaredConstructors(clazz); } catch (Exception e) { e.printStackTrace(); } } }
输出结果为:
public reflectdemo.SuperHero() reflectdemo.SuperHero --------------------- public reflectdemo.SuperHero(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String) reflectdemo.SuperHero java.lang.String arg0, java.lang.String arg1, java.lang.Integer arg2, java.lang.String arg3, ---------------------
public class ClassUtils { /** * 获取属性字段 * @param clazz 类 * @param fieldName 属性名称 * @throws Exception */ public static void getDeclaredField(Class clazz, String fieldName) throws Exception{ System.out.println(clazz.getDeclaredField(fieldName)); } } public class ClassTest { public static void main(String[] args) { try { Class clazz = Class.forName("reflectdemo.SuperHero"); //测试公有属性 ClassUtils.getDeclaredField(clazz, "ADDRESS"); //测试私有属性 ClassUtils.getDeclaredField(clazz, "name"); } catch (Exception e) { e.printStackTrace(); } } }
输出结果为:
public static final java.lang.String reflectdemo.SuperHero.ADDRESS private java.lang.String reflectdemo.SuperHero.name
public class ClassUtils { /** * 遍历clazz对象已有的成员变量 * @param clazz */ public static void getDeclaredFields(Class clazz){ Field[] fields = clazz.getDeclaredFields(); for (Field field: fields) { //如果要设置值,需要加入下面这句,反射对象在使用时不使用Java语言访问检查 //field.setAccessible(true); //直接打印Field System.out.println(field); //手动获取变量类型和变量名称 System.out.println(field.getType().getName() + " " +field.getName()); System.out.println("--------------------"); } } } public class ClassTest { public static void main(String[] args) { try { Class clazz = Class.forName("reflectdemo.SuperHero"); //遍历成员变量 ClassUtils.getDeclaredFields(clazz); } catch (Exception e) { e.printStackTrace(); } } }
输出结果为:
public static final java.lang.String reflectdemo.SuperHero.ADDRESS java.lang.String ADDRESS -------------------- private java.lang.String reflectdemo.SuperHero.id java.lang.String id -------------------- private java.lang.String reflectdemo.SuperHero.name java.lang.String name -------------------- private java.lang.Integer reflectdemo.SuperHero.age java.lang.Integer age -------------------- private java.lang.String reflectdemo.SuperHero.skill java.lang.String skill --------------------
public class ClassUtils { /** * 获取成员方法 * @param clazz 类 * @param methodName 方法名称 * @param params 参数列表 * @throws Exception */ public static void getDeclaredMethod(Class clazz, String methodName, Class[] params) throws Exception{ Method method = clazz.getDeclaredMethod(methodName, params); System.out.println("直接打印"); System.out.println(method); System.out.println("手动构建"); //获取返回类型 System.out.print(method.getReturnType().getSimpleName() + " "); //获取方法名称 System.out.print(method.getName() + "("); //获取参数类型 Class[] paramTypes = method.getParameterTypes(); for(int i = 0; i < paramTypes.length; i++){ Class param = paramTypes[i]; if(i < paramTypes.length - 1){ System.out.print(param.getSimpleName() + ", "); }else { System.out.print(param.getSimpleName()); } } System.out.print(")"); System.out.println(); } } public class ClassTest { public static void main(String[] args) { try { Class clazz = Class.forName("reflectdemo.SuperHero"); //打印无参数方法 ClassUtils.getDeclaredMethod(clazz, "getName", null); //打印有参数方法 ClassUtils.getDeclaredMethod(clazz, "setName", new Class[]{String.class}); } catch (Exception e) { e.printStackTrace(); } } }
输出结果为:
直接打印 public java.lang.String reflectdemo.SuperHero.getName() 手动构建 String getName() 直接打印 public void reflectdemo.SuperHero.setName(java.lang.String) 手动构建 void setName(String)
public class ClassUtils { /** * 遍历方法 * @param clazz */ public static void getDeclaredMethods(Class clazz){ //获取类中所有声明的方法 Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods){ System.out.println(method); } } } public class ClassTest { public static void main(String[] args) { try { Class clazz = Class.forName("reflectdemo.SuperHero"); //遍历方法 ClassUtils.getDeclaredMethods(clazz); } catch (Exception e) { e.printStackTrace(); } } }
输出结果为:
public java.lang.String reflectdemo.SuperHero.getName() public java.lang.String reflectdemo.SuperHero.getId() public void reflectdemo.SuperHero.setName(java.lang.String) public void reflectdemo.SuperHero.print() public java.lang.String reflectdemo.SuperHero.getSkill() public void reflectdemo.SuperHero.setAge(java.lang.Integer) public void reflectdemo.SuperHero.setSkill(java.lang.String) public void reflectdemo.SuperHero.setId(java.lang.String) public java.lang.Integer reflectdemo.SuperHero.getAge()
public class ClassUtils { /** * 执行set方法(通过Method的invoke方法) * @param o 待执行的实体 * @param methodName 方法名称 * @param params 方法参数类型 * @throws Exception */ public static void invokeSetMethod(Object o, String methodName, Class[] params) throws Exception { Method method = o.getClass().getDeclaredMethod(methodName, params); method.invoke(o, "钢铁侠"); } /** * 执行get方法(通过Method的invoke方法) * @param o 待执行的实体 * @param methodName 方法名称 * @throws Exception */ public static void invokeGetMethod(Object o, String methodName) throws Exception{ Method method = o.getClass().getDeclaredMethod(methodName); Object obj = method.invoke(o); System.out.println(obj); } } public class ClassTest { public static void main(String[] args) { try { Class clazz = Class.forName("reflectdemo.SuperHero"); //创建实体 Object o = clazz.newInstance(); //调用set方法 ClassUtils.invokeSetMethod(o, "setName", new Class[]{String.class}); //调用get方法 ClassUtils.invokeGetMethod(o, "getName"); } catch (Exception e) { e.printStackTrace(); } } }
输出结果为:
钢铁侠
下面是对invoke方法的API说明
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
在具有指定参数的方法对象上调用此方法对象表示的基础方法。
如果底层方法是静态的,则指定的obj参数将被忽略。 它可能为null。
如果底层方法所需的形式参数的数量为0,则提供的args数组的长度为0或为空。
如果底层方法是一个实例方法,它将使用动态方法查找来调用,如“Java语言规范”第二版,第15.12.4.4节所述; 特别是将会发生基于目标对象的运行时类型的覆盖。
如果底层方法是静态的,则如果尚未初始化该方法,那么声明该方法的类将被初始化。
如果方法正常完成,则返回的值将返回给调用者; 如果值具有原始类型,则首先将其适当地包装在对象中。 但是,如果该值具有基本类型的数组的类型,则该数组的元素 不会包含 在对象中; 换句话说,返回一个原始类型的数组。 如果底层方法返回类型为void,则调用返回null。
obj - 从底层方法被调用的对象
args - 用于方法调用的参数
由该对象表示的方法在 obj上调用 args
IllegalAccessException - 如果这个 方法对象正在强制执行Java语言访问控制,并且底层方法是无法访问的。
IllegalArgumentException - 如果方法是一个实例方法,并且指定的对象参数不是声明底层方法(或其子类或实现者)的类或接口的实例; 如果实际和正式参数的数量不同; 如果原始参数的解包转换失败; 或者如果在可能的展开之后,通过方法调用转换,参数值不能转换为相应的形式参数类型。
InvocationTargetException - 如果底层方法抛出异常。
NullPointerException - 如果指定的对象为空,该方法为实例方法。
ExceptionInInitializerError - 如果由此方法引发的初始化失败。
本文对反射的定义,反射使用过程中重要的、常用的类和方法进行了讲解,包括Class类,Constructor类,Field类,Method类的说明及使用。反射机制允许在运行时判断任意一个对象所属的类、构造任意一个类的对象、判断任意一个类所具有的成员变量和方法、调用任意一个对象的方法。大大提高了系统的灵活性和扩展性,不过凡事都有两面性,反射破坏了Java封装的特性,相对来说不安全,需要根据场景酌情考虑,若有不对之处,请批评指正,望共同进步,谢谢!
Linux公社的RSS地址 : https://www.linuxidc.com/rssFeed.aspx
本文永久更新链接地址: https://www.linuxidc.com/Linux/2019-08/160185.htm