前言
相信很多人都知道反射可以说是Java中最强大的技术了,它可以做的事情太多太多,很多优秀的开源框架都是通过反射完成的,比如最初的很多注解框架,后来因为java反射影响性能,所以被运行时注解APT替代了,java反射有个开源框架jOOR相信很多人都用过,不过我们还是要学习反射的基础语法,这样才能自己写出优秀的框架,当然这里所讲的反射技术,是学习Android插件化技术、Hook技术等必不可少的!
一、什么是反射?
定义:通过Class动态获取类的相关信息,动态的的创建对象,动态的调用方法,动态的改变对象上面的属性,这种方式就称为反射.
- 每个类在运行的时候都会有对应的Class
- 我们要想执行反射操作,必须先要获取指定的Class
反射在开发中的作用:要创建那个类的对象,调用该对象的那个方法,这些信息被写到一个文件中,程序运行的时候,读取这些信息,然后根据这些信息,动态的创建对象,动态的调用方法(后面的框架都是这么做的)
二、反射相关的类和接口
三.Class类
类对象有哪些信息?
- 包名信息
- 类的信息: 修饰符,类名 ,继承的基类,实现的接口
- 类的属性信息: 修饰符,类型,属性名称 ,缺省值
- 类的方法的信息: 修饰符,返回类型,方法名称,参数列表,抛出的异常
- 类的构造方法的信息: 修饰符,类名,参数列表,抛出的异常
为了描述类对象的相关信息,我们需要定义一个Class类
因为类对象的相关信息保存在Class类中,所以Class类中会有获取这些信息的方法
java中的每个类在运行的时候都会有一个对象的Class,这个Class就是我们上面说的Class
我们要想分享一个类的相关信息,需要先获取到这个类对象的Class
如果来获取指定类(引用类型)的Class呢? (获取引用类型Class的不同方式)
- Class claz = Class.forName(“完整的类名”)
- 类名.class
- 对象.getClass();
获取基本类型的Class的不同方式
- 基本类型.class ; 例如: int.class
- 包装器类型.TYPE; 例如: Integer.TYPE , 获取到的是int的class
3.1 通过Class获取属性Field
Java中把类中的属性封装成Field
- 相关的方法
A. 获取多个属性的方法
(a)public Field[] getFields():
则此方法返回该类及其所有超类的公共字段
(b)public Field[] getDeclaredFields() :
获取本类中声明的所有的字段(公开的,保护的,缺省的,私有的),但是没有继承的
B. 获取单个的属性的方法
(a) public Field getField(String name) :
获取指定名称的公开的字段,包括本类中和继承下来的公开的字段
(b) public Field getDeclaredField(String name)
获取指定名称的字段,包括本类中声明的字段
3.2 通过Class获取构造方法Constructor
Java中把构造方法封装成Constructor
-
获取多个构造方法
(a) public Constructor<?>[] getDeclaredConstructors()
获取类中声明的构造方法,包括公开的,保护的,缺省的,私有的构造方法
(b) public Constructor<?>[] getConstructors()
获取本类中所有的公开的构造方法 -
获取单个的构造方法
(a) public Constructor getConstructor(Class<?>… parameterTypes)
获取指定的公开的构造方法,通过Class[]指定参数列表
(b) public ConstructorgetDeclaredConstructor(Class<?>…parameterTypes)
获取指定的声明的构造方法 ,可以是公开的,保护的,缺省的,私有的
3.2.1 构造器的作用是什么? 是用来创建对象的!
因为构造器是用来创建对象的,所以构造器上面有一个newInstance()的方法
newInstance() 是创建实例的意思,也就是创建对象!
Class上面也有newInstance()的方法,该方法没有任何参数, 实际上就是调用的类中无参数的构造方法
3.3 通过Class获取构造方法Method
因为:方法是用来被调用的; 所以Method上面有一个invoke()方法
四.完整代码演示
package com.oracle.refelect.test2;
import java.io.Serializable;
public class Student extends Person implements Serializable {
/**
* 学生类
*/
private static final long serialVersionUID = 8855872842402324442L;
private String className;
private int score;
public String schoolName; // 学校名称
private static String teacherName;
public static String getTeacherName() {
return teacherName;
}
public static void setTeacherName(String teacherName) {
Student.teacherName = teacherName;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public Student() {
super();
}
public Student(String className, int score) {
super();
this.className = className;
this.score = score;
}
}
package com.Oracle.demo.s91.Reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.TypeVariable;
public class GetClassBaseInfo {
/**
* 获取类的基本相关信息 类的基本信息包括: 包名, 修饰符,类名,基类,接口
*/
public static void getPackageName() {
try {
Class clazz = Class.forName("java.lang.String");
//Class clazz = String.class;
String packageName = clazz.getPackage().getName();// 获取包名信息
String className = clazz.getName();// 获取类名信息
int a = clazz.getModifiers();
String mod = Modifier.toString(a);// 获取修饰符信息
String superName = clazz.getSuperclass().getName();// 获取基类的信息
Class[] interfaces = clazz.getInterfaces();// 获取实现的接口的信息
for (Class class1 : interfaces) {
String str = class1.getName();
System.out.println(str);
}
System.out.println("=====");
System.out.println(packageName);
System.out.println(className);
System.out.println( mod );
System.out.println(superName);
System.out.println();
String sre2 = null;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取类的属性的相关信息 类属性的信息包括: 修饰符, 类型,属性名称,缺省值 Java中把属性封装成Field
*/
public static void getFieldsName() {
Class claz = Teacher.class;
// 获取多个属性的不同方式
// 1. public Field[] getFields(): 则此方法返回该类及其所有超类的公共字段
// 2. public Field[] getDeclaredFields() :
// 获取本类中声明的所有的字段(公开的,包含的,缺省的,私有的),但是没有继承的
Field[] fields = claz.getDeclaredFields();
for (Field field : fields) {
String mod = Modifier.toString(field.getModifiers());// 修饰符
System.out.print(mod+" ");
System.out.print(field.getType().getName() + " ");// 属性类型
System.out.print(field.getName() + " ");// 属性名称
System.out.println();
}
}
/**
* 获取类中的构造方法的相关信息
*/
public static void getConstructorInfo() {
Class claz1= Teacher.class;
// 获取类中所有的声明的构造方法,包括公开的,保护的,缺省的,私有的
Constructor[] de = claz1.getDeclaredConstructors();
for (Constructor con : de) {
//System.out.println(con);
System.out.println(Modifier.toString(con.getModifiers()) + " "); // 修饰符
System.out.print(con.getName() + " (");//方法名
Class[] parameterTypes = con.getParameterTypes();//参数类型
for (Class class1 : parameterTypes) {
System.out.print(class1.getName() + ","); //参数名
}
System.out.print(")");
Class[] exceptionClasses = con.getExceptionTypes();//异常类型
if(exceptionClasses.length > 0) {
System.out.print(" throws ");
for (Class exception : exceptionClasses) {
System.out.print(exception.getName() + ",");
}
}
System.out.println();
}
// 获取单个的指定参数的构造方法
/*Class[] paramClasses1 = { String.class, Integer.TYPE }; // 指定参数列表
try {
Constructor con = claz.getDeclaredConstructor(paramClasses1);
System.out.print(Modifier.toString(con.getModifiers()) + " "); // 修饰符
System.out.print(con.getName() + " (");
Class[] paramClasses = con.getParameterTypes();
for (Class paramclass : paramClasses) {
System.out.print(paramclass.getName() + ",");
}
System.out.print(")");
Class[] exceptionClasses = con.getExceptionTypes();
if (exceptionClasses.length > 0) {
System.out.print(" throws ");
for (Class exceptionClass : exceptionClasses) {
System.out.print(exceptionClass.getName() + ",");
}
}
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}*/
}
// 获取类中的指定的构造器,然后通过构造器来创建对象
public static Object createObject() {
Object obj = null;
Class claz = Teacher.class;
Class[] paramClasses = {String.class, String.class, int.class, int.class, String.class};
try {
// 获取类中的指定的构造器
Constructor con = claz.getDeclaredConstructor(String.class, String.class, int.class, int.class, String.class);
// 设置私有的成员可以访问
con.setAccessible(true);
// 因为构造器是用来创建对象的,所以构造器上有newInstance()的方法
Object[] params = {"时间福建省", "大专", 200000, 30, "女"};
obj = con.newInstance(params);
System.out.println(obj);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return obj;
}
// 通过反射机制,调用指定的方法
public static void invokeMethod() {
Class claz = Teacher.class;
Object obj = createObject();// 调用createObject()方法,创建Student的对象(被当做Object类型)
// 获取指定参数的方法
// getDeclaredMethod()方法的第一个参数是方法名称,第二个参数是参数列表的数组
// 因为toString1()方法没有参数列表,所以第二个参数不用
// Method method = claz.getDeclaredMethod("toString1");
try {
//Method method = claz.getDeclaredMethod("toString1" );
Method method = claz.getDeclaredMethod("toString2", String.class, String.class );
method.setAccessible(true);
// toString1()方法是一个Student类中的实例方法,必须要有一个对象才能调用实例方法toString1()
// invoke()方法的第一个参数是一个Object,就是上面说的所需要的对象
// 我们调用方法的时候,方法会有实参列表,所以第二个参数是一个Object[],表示调用该方法的时候所需要的实参数组
// toString1()方法没有实参,所以invoke()方法的第二个参数不需要
// 我们调用方法的时候,方法会有返回值;而调用method.invoke()的返回值,就是调用方法之后的返回结果
// Object res = method.invoke(obj);
Object ins = method.invoke(obj, "已婚", "3个");
System.out.println("调用方法的返回结果:" + ins);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 调用类中私有的静态方法
// 调用静态方法的时候不需要对象
public static void invokeStaticMethod() {
Class claz = Teacher.class;
try {
Method method = claz.getDeclaredMethod("privateStaticMethod");
method.setAccessible(true);
// 调用静态方法的时候不需要对象 ,所以可以传递一个null
method.invoke(null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
// 通过反射机制动态的创建对象,并且动态改变对象上的属性
public static void changeFieldValue() {
// 创建对象的时候,对象的属性值如下: "张三", 23, "计算机1班", 98, "清华大学"
Object obj = createObject(); // 动态的创建Student的对象,被当做Object类型
// 通过对象来获取Class
Class claz = obj.getClass();
try {
// 通过Class获取类中指定名称的Field
// getDeclaredField()方法的参数是一个字符串,表示属性名称
Field field = claz.getDeclaredField("sex");
field.setAccessible(true);// 设置私有的属性可访问
// 取得该属性的值
// Student类中的schoolName 是一个实例属性,必须要有对象之后,该属性才会有效
// field.get()方法的参数是一个Object,也就是上面说的对象
// field.get(obj)方法返回的就是该属性的值
Object ject = field.get(obj);
System.out.println(ject);
field.set(obj, "男"); // 改变对象上面的schoolName属性的值
System.out.println(ject);
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
//getPackageName() ;
//getFieldsName();
//getConstructorInfo();
//createObject();
//invokeMethod();
//invokeStaticMethod();
//changeFieldValue();
}
}
五.反射应用场景
反射在开发中的作用:要创建那个类的对象,调用该对象的那个方法,这些信息被写到一个文件中,程序运行的时候,读取这些信息,然后根据这些信息,动态的创建对象,动态的调用方法(后面的框架都是这么做的)
ClassName=com.oracle.refelect.test4.Student
ConstructorParames=java.lang.String;int;java.lang.String;int;java.lang.String
MethodName=toString1
以上为配置文件信息
package com.Oracle.demo.s91.Reflect;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;
/*
要创建那个类的对象,调用该对象的那个方法,这些信息被写到一个文件中,程序运行的时候,读取这些信息,
然后根据这些信息,动态的创建对象,动态的调用方法(后面的框架都是这么做的)
*/
public class ReflectApply {
private static String className;// 类名
private static String constructorParams;// 构造器的参数列表
private static String methodName;// 要调用的方法名称
//方法参数列表
static {
// 使用流连接配置文件
// RefelectTest.class.getResourceAsStream("/obj.txt")这种方式是专门读取src下面的文件的
// RefelectTest.class.getClassLoader().getResourceAsStream("obj.txt")也可以读取src下面指定名称的文件
// RefelectTest.class.getResourceAsStream("obj.txt"); 会去类所在的包中读取文件
try(InputStream is = ReflectApply.class.getResourceAsStream("obj.properties");){
// 为了读取obj.properties这种特殊格式的文件,创建一个Properties类的对象
Properties pro = new Properties();
pro.load(is);// 让Properties类的对象和流连接起来,这样就可以通过Properties类的对象读取文件的内容
className = pro.getProperty("ClassName");
constructorParams = pro.getProperty("CondtructorParams");
methodName = pro.getProperty("MethodName");
} catch (IOException e) {
e.printStackTrace();
}
}
//从参数字符串中分析出指定方法所需要的参数的Class[],因为在获取构造方法,获取方法的时候必须要通过Class[] 来指明参数列表
private static Class[] getMethodParam(String str) {
//java.lang.String,int,java.lang.String,int;java.lang.String
String[] strArr = str.split(",");
Class[] paramClasses = new Class[strArr.length];
try {
for(int i=0;i<paramClasses.length;i++) {
Class claz = getClassByStr(strArr[i]);
paramClasses[i] = claz;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return paramClasses;
}
//根据参数的名称,获取参数的Class
private static Class getClassByStr(String param) throws ClassNotFoundException {
Class claz = null;
if(param.indexOf(".") != -1) {// 如果param中有. 则表示这是一个引用类型
claz = Class.forName(param);
}
else { //参数是基础类型
if(param.equals("byte")) {
claz = byte.class;
}
else if(param.equals("short")) {
claz = short.class;
}
else if(param.equals("int")) {
claz = int.class;
}
else if(param.equals("long")) {
claz = long.class;
}
else if(param.equals("char")) {
claz = char.class;
}
else if(param.equals("float")) {
claz = float.class;
}
else if(param.equals("double")) {
claz = double.class;
}
else if(param.equals("boolean")) {
claz = boolean.class;
}
else {
throw new ClassNotFoundException("指定的类型:" + param + "不存在");
}
}
return claz;
}
// 动态的创建对象,动态的调用方法
public static void invokeMethod() {
try {
// 获取指定类名的Class对象
Class claz = Class.forName(className);
// 获取指定参数列表的构造方法
// 首先要获取构造方法的参数列表
Class[] paramClasses = getMethodParam(constructorParams);
Constructor con = claz.getDeclaredConstructor(paramClasses);
con.setAccessible(true);
// 通过指定的构造器创建对象
Object obj = con.newInstance("健身房看时间", "康师傅", 200000, 30, "3个");
// 获取指定名称,指定参数列表的方法
Method method = claz.getDeclaredMethod(methodName);
//Method method = claz.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
// 调用方法的时候,需要传递实参; 调用方法之后,方法会有返回类型
// method.invoke(obj, "HelloWorld", 23) 就是我们自己调用方法:
// stu.toString1("Hello",23);
// 调用toString1()方法,会有返回结果,所以method.invoke()返回的结果就是调用方法之后的返回结果
Object ins = method.invoke(obj);
System.out.println(ins);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
invokeMethod();
}
}
以上均为个人的学习见解,如有错误,敬请指正!