在运行状态中,对于任意一个类,都能够知道其所有属性和方法,对于任意一个对象,都能够调用其任意方法和属性,这种动态获取信息、动态调用方法的能力称为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