Java反射机制
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
反射的本质就是:在运行时,把 Java 类中的各种成分映射成一个个的 Java 对象。
反射机制很重要的一点就是“运行时”,其使得我们可以在程序运行时加载、探索以及使用编译期间完全未知的 .class 文件。换句话说,Java 程序可以加载一个运行时才得知名称的 .class 文件,然后获悉其完整构造,并生成其对象实体、或对其 fields(变量)设值、或调用其 methods(方法)。
Class类将一个类的组成封装成各个属性,并实现了各个getXxx()方法。
public static void getInfo(Class cls) throws NoSuchMethodException {
//构造方法
cls.getConstructor();
//获取某个方法
cls.getMethod("");
//包含的方法
cls.getMethods();
//获取某个属性
cls.getField("");
//包含的属性
cls.getFields();
//实现的接口
cls.getInterfaces();
//包含的Annotation
cls.getAnnotations();
//内部类
cls.getDeclaredClasses();
//外部类
cls.getDeclaringClass();
//获取类名
cls.getName();
//获取包名
cls.getPackage();
//获取修饰符
cls.getModifiers();
}
创建使用类
public class FatherClass {
protected String mFatherName;
protected int mFatherAge;
public FatherClass() {
}
}
public class SonClass extends FatherClass {
private String mSonName;
protected int mSonAge;
public String mSonBirthday;
@GET("https:\\www.baidu.com")
private <T,K> String toStr(T t, K k) {
return t.toString() + k.toString();
}
public String getmSonName() {
return mSonName;
}
public void setmSonName(String mSonName) {
this.mSonName = mSonName;
}
}
各种操作实现:
-
通过反射获取类的三种方式
/**
* 通过反射获取类的三种方式
* @throws ClassNotFoundException
*/
private void getClassWay() throws ClassNotFoundException {
Class cls = null;
cls = FatherClass.class;
cls = new FatherClass().getClass();
cls = Class.forName("com.example.genericannotaionreflect.reflactdemo.FatherClass");
}
-
反射创建实例
public static void getConstructor() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
Class<?> cls = StringBuilder.class;
StringBuilder sb = (StringBuilder) cls.newInstance();
sb.append("hello");
System.out.println(sb.toString());
Class<?> cls2 = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = cls2.getConstructor(String.class);
//根据构造器constructor创建实例
String str = (String) constructor.newInstance("hello");
System.out.println(str);
}
获取类的所有变量信息
/**
* 通过反射获取类的所有变量
*/
public static void printFileds(Class cls) {
System.out.println("类名:" + cls.getName());
// 获取所有 public 访问权限的变量
// 包括本类声明的和从父类继承的
Field[] fields = cls.getFields();
// 获取所有本类声明的变量,包含各种访问权限
Field[] declaredFields = cls.getDeclaredFields();
for (Field field: declaredFields) {
//获取访问权限并输出
field.setAccessible(true);
int modifier = field.getModifiers();
System.out.println("属性访问权限是否是 PROTECTED: " + Modifier.isProtected(modifier));
//输出变量的类型及变量名
System.out.println("属性类型:" + field.getType().getName() + "属性名:" + field.getName());
}
}
//调用
ClassReflact.printFileds(FatherClass::class.java)
打印:
类名:com.example.genericannotaionreflect.reflactdemo.FatherClass
属性访问权限是否是 PROTECTED: true
属性类型:int属性名:mFatherAge
属性访问权限是否是 PROTECTED: true
属性类型:java.lang.String属性名:mFatherName
getFields() :获取所有 public 访问权限的变量,非public的获取不到
getDeclaredFields():获取所有本类声明的变量,包含各种访问权限
-
获取类的所有方法的所有元素
@RequiresApi(api = Build.VERSION_CODES.P)
public static void printMethods(Class cls) {
System.out.println("类名:" + cls.getName());
// 获取所有 public 访问权限的方法
// 包括本类声明的和从父类继承的
Method[] methods = cls.getMethods();
// 获取所有本类声明的方法,包含各种访问权限
Method[] declaredMethods = cls.getDeclaredMethods();
for (Method method : declaredMethods) {
method.setAccessible(true);
System.out.println("方法名: " + method.getName());
System.out.println("属性访问权限是否是 public: " + Modifier.isProtected(method.getModifiers()));
//获取方法返回值类型
Class<?> returnType = method.getReturnType();
System.out.println( "返回类型: " + returnType.getName());
//获取方法所有参数
Parameter[] parameters = method.getParameters();
for (Parameter parameter: parameters) {
System.out.println("拥有参数:" + parameter.getName() + "--" + parameter.getType().getName());
}
//获取方法抛出的异常
Class<?>[] exceptionTypes = method.getExceptionTypes();
//获取方法注解
Annotation[] annotations = method.getAnnotations();
//方法是否有某个注解
if (method.isAnnotationPresent(GET.class)) {
GET annotation = method.getAnnotation(GET.class);
System.out.println(annotation.value());
}
//获取方法参数类型的泛型参数
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type type: genericParameterTypes) {
System.out.println(type.getTypeName());
}
}
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface GET {
String value();
}
//调用
ClassReflact.printMethods(SonClass::class.java)
打印:
类名:com.example.genericannotaionreflect.reflactdemo.SonClass
方法名: toStr
属性访问权限是否是 public: false
返回类型: java.lang.String
拥有参数:arg0--java.lang.Object
拥有参数:arg1--java.lang.Object
https:\www.baidu.com
T
K
类名:com.example.genericannotaionreflect.reflactdemo.SonClass
方法名: toStr
属性访问权限是否是 public: false
返回类型: java.lang.String
拥有参数:arg0--java.lang.Object
拥有参数:arg1--java.lang.Object
https:\www.baidu.com
T
K
方法名: getmSonName
属性访问权限是否是 public: false
返回类型: java.lang.String
方法名: setmSonName
属性访问权限是否是 public: false
返回类型: void
拥有参数:arg0--java.lang.String
java.lang.String
-
反射执行某个对象的私有方法
public static void invokeMethod() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class cls = SonClass.class;
SonClass sonClass = (SonClass) cls.newInstance();
Method method = cls.getDeclaredMethod("setmSonName", String.class); //获取setmSonName方法
method.setAccessible(true); //可以访问对象的私有方法
method.invoke(sonClass, "lala"); //使用 invoke 反射调用私有方法,传需要操作的对象和方法的各个参数
System.out.println(sonClass.getmSonName());
}
-
反射操作对象属性值
public static void modifyField() throws NoSuchFieldException, IllegalAccessException {
Class cls = SonClass.class;
Field field = cls.getDeclaredField("mSonAge");
field.setAccessible(true);
SonClass sonClass = new SonClass();
field.set(sonClass, 18);
System.out.println("age: " + sonClass.mSonAge);
}
java 反射为什么会耗性能
1.反射调用过程中会产生大量的临时对象,这些对象会占用内存,可能会导致频繁 gc,从而影响性能。
2.反射调用方法时会从方法数组中遍历查找,并且会检查可见性等操作会耗时。
3.反射在达到一定次数时,会动态编写字节码并加载到内存中,这个字节码没有经过编译器优化,也不能享受JIT优化。
4.反射一般会涉及自动装箱/拆箱和类型转换,都会带来一定的资源开销。
参考:
https://juejin.cn/post/6844904098207105038
参考:https://juejin.cn/post/6844904005294882830