一、概述
(1)Java反射机制定义
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
(2)Java反射机制的功能
1)在运行时判断任意一个对象所属的类。
2)在运行时构造任意一个类的对象。
3)在运行时判断任意一个类所具有的成员变量和方法。
4)在运行时调用任意一个对象的方法。
5)生成动态代理。
(3)Java反射机制的应用场景
1)逆向代码,例如反编译。
2)与注解相结合的框架,例如Retrofit
3)单纯的反射机制应用框架,例如EventBus
4)动态生成类框架,例如Gson。
二、通过Java反射查看类信息
(1)Class对象
每个类被加载之后,系统就会为该类生成一个对应的Class对象。通过该Class对象就可以访问到JVM中的这个类。
在Java程序中获得Class对象通常有如下三种方式:
1)使用Class类的forName(String clazzName)静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定名(必须添加完整包名)。
2)调用某个类的class属性来获取该类对应的Class对象。
3)调用某个对象的getClass()方法。该方法是java.lang.Object类中的一个方法。
//第一种方式,使用Class.forName静态方法,传入完整类路径获取
try{
Class<?> class1=Class.forName("com.qd.threadcontact.Student");
}catch (ClassNotFoundException e)
{
e.printStackTrace();
}
//第二种方式,通过类的class属性
Class<Student> class2=Student.class;
//第三种方式,通过对象的getClass方法获取
Student stu=new Student();
Class<?> class3=stu.getClass();
(2)获取class对象的属性、方法、构造函数等
1)获取class对象的成员变量
//获取class对象的所有属性
Field[] allFields = class2.getDeclaredFields();
//获取class对象的public属性
Field[] publicFields = class2.getFields();
try{
//获取class指定属性
Field ageField = class2.getDeclaredField("name");
//获取class指定的public属性 由于age属性是private,所以会抛出异常
Field desField = class2.getField("age");
}catch (NoSuchFieldException e)
{
e.printStackTrace();
}
for(Field f:allFields)
{
System.out.println(f.getName());
}
//Student类中并没有public属性
if(publicFields.length==0)
{
System.out.println("没有public属性");
}else{
System.out.println("有public属性");
}
2)获取class对象的方法
//获取class对象的所有声明方法
Method[] methods = class2.getDeclaredMethods();
//获取class对象的所有public方法 包括父类的方法
Method[] allMethods = class2.getMethods();
try{
//返回次Class对象对应类的、带指定形参列表的public方法
Method method = class2.getMethod("test3", Student.class);
//返回次Class对象对应类的、带指定形参列表的方法
Method declaredMethod = class2.getDeclaredMethod("test1",Student.class);
}catch(NoSuchMethodException e)
{
e.printStackTrace();
}
//打印出本类的所有方法,不包括父类的方法
for(Method m:methods)
{
System.out.println(m.getName());
}
System.out.println("<<-------------------->>");
//返回Class对象所有的public方法,包括父类的方法
for(Method m:allMethods)
{
System.out.println(m.getName());
}
3)获取class对象的构造函数
//获取class对象的所有声明构造函数
Constructor<?>[] allConstructors = class2.getDeclaredConstructors();
//获取class对象public构造函数
Constructor<?>[] publicConstructors = class2.getConstructors();
try{
//获取指定声明构造函数
Constructor<?> constructor = class2.getDeclaredConstructor(String.class);
//获取指定声明的public构造函数
Constructor publicConstructor = class2.getConstructor(String.class,Integer.class);//获取指定声明的public构造函数
}catch (NoSuchMethodException e)
{
e.printStackTrace();
}
for(Constructor<?> c:allConstructors)
{
System.out.println(c.toGenericString());
}
for(Constructor<?> c:publicConstructors)
{
System.out.println(c.toGenericString());
}
4)其他方法
Annotation[] annotations = class2.getAnnotations();//获取class对象的所有注解
Annotation annotation =class2.getAnnotation(TestAnno1.class);//获取class对象指定注解
Type genericSuperclass = class2.getGenericSuperclass();//获取class对象的直接超类的 Type
Type[] interfaceTypes = class2.getGenericInterfaces();//获取class对象的所有接口的type集合
for(Annotation a:annotations)
{
System.out.println(a.annotationType().getName());
}
System.out.println("type:"+genericSuperclass.getTypeName());
for(Type t:interfaceTypes)
{
System.out.println(t.getTypeName());
}
(3)获取class对象的信息
boolean isPrimitive = class1.isPrimitive();//判断是否是基础类型
boolean isArray = class1.isArray();//判断是否是集合类
boolean isAnnotation = class1.isAnnotation();//判断是否是注解类
boolean isInterface = class1.isInterface();//判断是否是接口类
boolean isEnum = class1.isEnum();//判断是否是枚举类
boolean isAnonymousClass = class1.isAnonymousClass();//判断是否是匿名内部类
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);//判断是否被某个注解类修饰
String className = class1.getName();//获取class名字 包含包名路径
Package aPackage = class1.getPackage();//获取class的包信息
String simpleName = class1.getSimpleName();//获取class类名
int modifiers = class1.getModifiers();//获取class访问权限
Class<?>[] declaredClasses = class1.getDeclaredClasses();//内部类
Class<?> declaringClass = class1.getDeclaringClass();//外部类
三、通过反射生成并操作对象
(1)生成类的实例信息
1)使用Class对象的newInstance()方法来创建该Class对象对应类的实例。这种方式要求该Class对象的对应类有默认构造器,而执行newInstance()方法时实际上是利用默认构造器来创建该类的实例。
2)先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用指定的构造器来创建实例。
//第一种方式 newInstance()
Class<Student> classStudent=Student.class;
try {
//必须得确认是否有无参数构造函数
Student student=classStudent.newInstance();
if(student!=null)
{
System.out.println("Student Name:"+student.getName());
}else{
System.out.println("Student null");
}
}catch (InstantiationException e)
{
e.printStackTrace();
}catch (IllegalAccessException e){
e.printStackTrace();
}
//第二种方式 对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成
try{
Class<Student> studentClass=Student.class;
Constructor<?> constructor = studentClass.getDeclaredConstructor(String.class,int.class);//获取指定声明构造函数
Student student=(Student)constructor.newInstance("qd",20);
System.out.println("Student Name:"+student.getName());
}catch (NoSuchMethodException e)
{
e.printStackTrace();
}catch (InstantiationException e)
{
e.printStackTrace();
}catch (InvocationTargetException e)
{
e.printStackTrace();
}catch(IllegalAccessException e)
{
e.printStackTrace();
}
(2)调用类的方法
1)通过Class对象的getMethods()方法或者getMethod()方法获得指定方法,返回Method数组或对象。
2)调用Method对象中的Object invoke(Object obj, Object... args)方法。第一个参数对应调用该方法的实例对象,第二个参数对应该方法的参数。
try{
// 生成新的对象:用newInstance()方法
Class<Student> classs=Student.class;
Student obj = classs.newInstance();
//首先需要获得与该方法对应的Method对象
Method method = classs.getDeclaredMethod("tests", String.class);
method.setAccessible(true); // 抑制Java的访问控制检查
//调用指定的函数并传递参数
method.invoke(obj,"YaYaYa");
}catch (NoSuchMethodException e)
{
e.printStackTrace();
}catch (InstantiationException e)
{
e.printStackTrace();
}catch (InvocationTargetException e)
{
e.printStackTrace();
}catch(IllegalAccessException e)
{
e.printStackTrace();
}
当通过Method的invoke()方法来调用对应的方法时,Java会要求程序必须有调用该方法的权限。如果程序确实需要调用某个对象的private方法,则可以先调用Method对象的如下方法。
setAccessible(boolean flag):将Method对象的acessible设置为指定的布尔值。值为true,指示该Method在使用时应该取消Java语言的访问权限检查;值为false,则知识该Method在使用时要实施Java语言的访问权限检查。
(3)访问成员变量
1)通过Class对象的getFields()方法或者getField()方法获得指定方法,返回Field数组或对象。
2)Field提供了两组方法来读取或设置成员变量的值:
getXXX(Object obj):获取obj对象的该成员变量的值。此处的XXX对应8种基本类型。如果该成员变量的类型是引用类型,则取消get后面的XXX。setXXX(Object obj,XXX val):将obj对象的该成员变量设置成val值。
try{
//通过反射获取属性
Class<Student> c1=Student.class;
//获取age成员变量
Field field = c1.getDeclaredField("age");
field.setAccessible(true);// 抑制Java的访问控制检查
Student s=c1.newInstance();
//将obj对象的age的值设置为10
field.setInt(s, 10);
//获取obj对象的age的值
field.getInt(s);
}catch (InstantiationException e)
{
e.printStackTrace();
}catch(IllegalAccessException e)
{
e.printStackTrace();
}catch(NoSuchFieldException e)
{
e.printStackTrace();
}
网友评论