参考:https://mp.weixin.qq.com/s/UYqPCmo2vpAibJPh6cupLw
1.定义
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java 的反射机制的实现要借助于4个类:class,Constructor,Field,Method,我们依次来说一下
2.获取Class对象的三种方式(Class 是反射的入口)
这个三个方式,我之前有提到过,我们再来重温一下:
- 通过Class类的forName(String clazzName)静态方法,参数为类的完全限定名,即包含包名的完整路径。
- 通过类的class属性
- 通过调用某个对象的getClass()方法
相关代码如下:
思路首先创建Student实体类,然后用这三种方式获取。
创建实体类(我这里直接把最终的实体类呈现)
public class StudentBean {
public String name ;
public String gender;
//注意age属性是私有的,其余是公共的
private String age;
public StudentBean() {
}
public StudentBean(String name, String gender, String age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gendpublic class StudentBean extends DataSupport {
public String name ;
public String gender;
//注意age属性是私有的,其余是公共的
private int age;
public StudentBean(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
private StudentBean(String name, String gender) {
this.name = name;
this.gender = gender;
}
public StudentBean() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/**
* 学生信息打印公共方法
* @return
*/
public String getStudentMesage(){
return "该学生信息如下:{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age='" + age + '\'' +
'}';
}
/**
* 公共的带形参的方法
* @return
*/
public String getStudentName(String name){
return this.name;
}
/**
* 私有的带形参的方法
* @return
*/
private int getStudentAge(int age){
return this.age;
}
/**
* 公共的带私有形参的方法
* @return
*/
public int getPrivateAge(int age){
return age;
}
/**
* 反射运行XML文件调用的测试方法
*/
public void reflectXmlMethod(){
System.out.println("反射运行XML文件调用的方法打印成功");
}
}
获取Class对象方式
Class mStudentBean;
//第一种方式,通过Class.forName()
try {
mStudentBean=Class.forName("com.xzt.demo.bean.StudentBean");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//第二种方式,通过类.class
mStudentBean=StudentBean.class;
//第三种方式,通过对象的getClass()方法
mStudentBean=new StudentBean().getClass();
3.反射查看信息
我们开始说到反射的定义:对于任意一个类,都能够知道这个类中的所有属性和方法。
那我们继续尝试利用上面获取的Student对象获取它的属性和方法,但再次之前,我们需要了解Field.
成员变量-Field
java.lang.reflect.Field 为我们提供了获取当前对象的成员变量的类型,和重新设值 的方法。
成员变量Field的类型和我们常见的变量类型一样:
分为两种类型:基本类型和引用类型:
基本类型( 8 种)
整数:byte, short, int, long
浮点数:float, double
字符:char
布尔值:boolean
引用类型
所有的引用类型都继承自 java.lang.Object
类,枚举,数组,接口都是引用类型
java.io.Serializable 接口,基本类型的包装类(比如 java.lang.Double)也是引用>>类型
获取Field变量类型的方法
java.lang.reflect.Field 提供了两个方法获去变量的类型:
Field.getType():返回这个变量的类型
Field.getGenericType():如果当前属性有签名属性类型就返回,否则就返回
Field.getType()
3.1查看类属性
直接上代码:
/ /**
* 获取类属性
*/
public void getClassAttribute(Class aClass){
//获取class对象的所有属性
Field[] allFields = aClass.getDeclaredFields();
//获取class对象的public属性
Field[] publicFields = aClass.getFields();
try {
//获取class指定属性
Field ageField = aClass.getDeclaredField("age");
//获取class指定的public属性
Field nameField = aClass.getField("name");
Field genderField = aClass.getField("gender");
} catch (NoSuchFieldException e) {
e.printStackTrace();
Log.e(TAG, "getClassAttribute: "+e.getMessage() );
}
}
我们来总结一下这几个方法:
-
Class.getDeclaredFields(),此方法获取的是类的所有属性数组,包括Private,Public等,我们运行的结果如下:
-
Class.getFields(),此方法只能获取Public的属性数组,我们运行结果如下:
- Class.getDeclaredField("属性名"),获取指定属性,包括Private和Public等,如图:
-
Class.getField("属性名");词方法获取公共的属性,不包括Private属性。如下:
那如果我们用getField()方法获取Private属性会怎么样呢?
3.2查看类方法
成员方法-Method
Method代表我们获取到一个类的所有方法的属性,在method类里头,我们可以获取到所有关于这个类里头方法的所有信息,包括方法名,方法的修饰符和方法的入参和返回参数等等的详细信息。
直接上代码,注释都清楚:
/**
* 获取类方法
*/
public void getClassMethod(Class mClass){
//获取class类所有声明方法(Private,Public),构造方法除外
Method[] methods = mClass.getDeclaredMethods();
//获取class对象的所有public方法,包括父类的方法
Method[] allMethods = mClass.getMethods();
try {
//返回Class类中带形参的public方法,参数为方法名和参数类型
Method method = mClass.getMethod("getStudentName", String.class);
//返回Class类中带形参的方法,参数为方法名和参数类型
Method declaredMethod2= mClass.getDeclaredMethod("getStudentAge", int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
当然我只是举例了常用的方法,其他方法大家自行百度。
3.3 类的构造方法
类的构造器-Constructor
Constructor代表某个类的构造方法。
相关代码,都有注释就不过多解释了。
/**
* 获取类构造方法
* @param mClass
*/
public void getClassConstructMehtod(Class mClass){
//获取class对象的所有声明构造函数(Private,Public)
Constructor>[] allConstructors = mClass.getDeclaredConstructors();
//获取class对象public构造函数
Constructor>[] publicConstructors = mClass.getConstructors();
try {
//获取指定声明构造函数,参数为构造函数的参数类型(Private,Public)
Constructor> constructor = mClass.getDeclaredConstructor(String.class,String.class);
//获取指定声明的public构造函数
Constructor publicConstructor = mClass.getConstructor(String.class,String.class,int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
3.4 获取类的其他信息的相关方法
/**
* 获取类的其他信息
* @param mClass
*/
public void getClassOtherMessage(Class mClass){
////判断是否是基础类型
boolean isPrimitive = mClass.isPrimitive();
//判断是否是集合类
boolean isArray = mClass.isArray();
//判断是否是注解类
boolean isAnnotation = mClass.isAnnotation();
//判断是否是接口类
boolean isInterface = mClass.isInterface();
//判断是否是枚举类
boolean isEnum = mClass.isEnum();
//判断是否是匿名内部类
boolean isAnonymousClass=mClass.isAnonymousClass();
//判断是否被某个注解类修饰
boolean isAnnotationPresent=mClass.isAnnotationPresent(Deprecated.class);
//获取class名字 包含包名路径
String className = mClass.getName();
//获取class的包信息
Package aPackage = mClass.getPackage();
//获取class类名
String simpleName = mClass.getSimpleName();
//获取class访问权限
int modifiers = mClass.getModifiers();
//内部类
Class>[] declaredClasses = mClass.getDeclaredClasses();
//外部类
Class> declaringClass = mClass.getDeclaringClass();
}
4.创建Class实例对象的方式
4.1通过newInstance()
这种方式也是我们常用的创建实体类的方式,调用静态方法使用类的默认构造方法创建。
4.2通过获取Constructor对象
先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建该Class对象对应类的实例。通过这种方式可以选择使用指定的构造器来创建实例。
两种创建方式的相关代码如下:
/**
* 通过Class获取类实例的方式
* @param mClass
* @throws InstantiationException
* @throws IllegalAccessException
* @throws NoSuchMethodException
* @throws InvocationTargetException
*/
public void getInstance(Class mClass) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//第一种方式 Class对象调用newInstance()方法生成
StudentBean studentBean = null;
studentBean = (StudentBean) mClass.newInstance();
//第二种方式 对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成
//获取指定声明构造函数
Constructor> constructor = null;
constructor = mClass.getDeclaredConstructor(String.class, String.class,Integer.class);
studentBean = (StudentBean) constructor.newInstance("小米", "男",20);
}
4.调用类中的方法
相关代码如下:
/**
* 调用类中的方法
*/
public void useMethod(){
try {
//获取Class
Class studentClass =Class.forName("com.xzt.demo.bean.StudentBean");
try {
//创建Class实例
StudentBean studentBean = (StudentBean) studentClass.newInstance();
try {
//获取一个私有属性
Field field = null;
try {
field = studentClass.getDeclaredField("age");
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
//将获取的私有属性设置在使用时应该取消Java语言的访问权限检查
field.setAccessible(true);
//根据参数获取指定方法
Method method=studentClass.getMethod("getPrivateAge", int.class);
//调用方法
Object str = null;
try {
str = method.invoke(studentBean, 20);
Log.e(TAG, "获取的年龄是:"+str);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
这里主要有两个知识点:
- 我们通过invoke()方法来调用对应的方法。
- 如果程序确实需要调用某个对象的private方法或私有属性,则可以先调用Method对象的setAccessible(boolean flag)方法。值为true,指示该Method在使用时应该取消Java语言的访问权限检查;值为false,则知识该Method在使用时要实施Java语言的访问权限检查。
5 通过反射运行配置文件内容
首先我们现在res/assets目录下新建一个reflect的文本文件如图:
StudentBean中要调用的方法:
相关代码如下
/**
* 通过反射运行配置文件内容
*/
public void reflectXml() throws Exception{
//通过反射获取Class对象
Class stuClass = Class.forName(getValue("className"));
//2获取配置文件中的getStudentMesage方法
Method m = stuClass.getMethod(getValue("methodName"));
//3.调用show()方法
m.invoke(stuClass.getConstructor().newInstance());
}
//此方法接收一个key,在配置文件中获取相应的value
public String getValue(String key) throws IOException {
//获取配置文件的对象
Properties pro = new Properties();
//获取输入流
//FileReader in = new FileReader("reflect");
InputStream in =this.getAssets().open("reflect");
//将流加载到配置文件对象中
pro.load(in);
in.close();
//返回根据key获取的value值
return pro.getProperty(key);
}
先这么多!