Java反射(概述)

Java反射(概述),第1张

目录
    • 一、反射
      • 1.1 概述
      • 1.2 Class类
    • 二、反射使用场景
    • 三、常用反射API
        • 1. 获取Class对象
        • 2. 获取Constructor
        • 3. 获取Method
        • 4. 获取Field
        • 5.其他
    • 四、总结

一、反射 1.1 概述     Java反射机制使我们可以在运行时获取程序自身字段、方法和构造函数等信息。一般情况下对象类型在编译期就确定下来了,但通过反射机制即使在编译期未知的对象我们也可以动态的进行创建以及调用访问其方法属性。

总结:Java反射机制的要点就是在运行期间动态加载类且不需要在编译期确定运行对象。常见的通过反射机制以及给定类的全限定名(全限定名 = 包名 + 类型名)我们就可以获取该类的所有属性和方法。

1.2 Class类

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。

使用反射的前提条件是需要获得其对应的Class对象,通过Class类我们就可以获取关于目标类的相关信息。而每一个类都有其对应的Class对象,程序编译后生成的.class文件保存了类的相关信息,当类在被第一次使用时就会随着.class文件被加载到JVM中,接着JVM就会据此生成Class对象。

小结: Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。程序运行的时候JVM会先检查类对应的class对象是否被加载,如果未加载,JVM根据类名查找.class文件,并将其载入。可参考文章:《Java中Class对象详解》

Class 和 java.lang.reflect ⼀起对反射提供了⽀持,java.lang.reflect 类库主要包含了以下三个类:

  • Field :可以使⽤ get() 和 set() ⽅法读取和修改 Field 对象关联的字段;
  • Method :可以使⽤ invoke() ⽅法调⽤与 Method 对象关联的⽅法;
  • Constructor :可以⽤ Constructor 的 newInstance() 创建新的对象

Java1.6中文文档:《JDK_API_1.6中文文档》—— 提取码:3l1e

二、反射使用场景

利用Java类的这种“自省”能力可以让程序更加灵活。例如:

  • 一些IDE通过反射可以将对象的所有方法列出来供我们选择
  • 框架中反射同注解结合能大大简化开发
  • 可以通过配置文件选择需要加载的类提高程序灵活性
三、常用反射API 1. 获取Class对象
// 通过类名的方式,此方法可能会发生java.lang.ClassNotFoundException因此需要处理异常
Class clazz = Class.forName(全限定类名);

// 通过类名.class方法
Classc2 = Test.class; 

// 通过具体对象
Test t = new Test();
Class clazz = t.getClass();

2. 获取Constructor
// 获取所有public修饰的构造方法数组
Constructor[] constructors = clazz.getConstructors();

// 获取所有的构造方法数组(包含私有private修饰的构造方法)
Constructor[] constructors = clazz.getDeclaredConstructors();

// 获取指定参数的public修饰的构造方法数组
Constructor[] constructors = clazz.getConstructor(Class...initArgumentTypes);

// 获取指定参数的构造方法数组(包含私有private修饰的构造方法)
Constructor[] constructors = clazz.getDeclaredConstructor(Class...initArgumentTypes);

// 通过构造方法创建对象,要求类中提供了一个无参的构造方法才可以用该方法
Object t = clazz.newInstance();

// 开放权限,使得private修饰的方法属性也可以被访问
setAccessible(boolean flag);
3. 获取Method
// 获取类内所有public修饰的成员方法,包括从父类继承而来的public修饰方法
Method[] methods = clazz.getMethods();

// 获取类内所有成员方法,但是不包括从父类继承而来的方法
Method[] methods = clazz.getDeclaredMethods();

// 根据指定的方法名和对应的参数类型,获取对应的public修饰的成员方法,包括从父类继承得来的
Method[] methods = clazz.getMethod(String methodName, Class...parameterTypes);

// 根据指定的方法名和对应的参数类型,获取对应的成员方法,包括私有化成员方法,但是不包括从父类继承而来的方法
Method[] methods = clazz.getDeclaredMethod(String methodName, Class... parameterTypes);

// 通过Method类对象调用,执行对应的方法,需要的参数
Object invoke(Object obj, Object... arguments);

使用invoke动态的调用方法
1.invoke的第一个参数代表要调用该方法的对象
2.传入的参数
3.如果访问的方法是私有的 需要在前面先调用setAccessible(true)获得访问的权限

4. 获取Field
// 获取类内所有public修饰的成员变量
Field[] fields = clazz.getFields();

// 获取类内所有成员变量,包括私有化成员方法
Field[] fields = clazz.getDeclaredFields();

// 获取指定变量名的成员变量对象,要求是public修饰的成员变量
Field field = clazz.getField(String fieldName);

// 获取指定变量名的成员变量对象,包括private私有化修饰的成员变量
Field field = clazz.getDeclaredField(String fieldName);

// 设置指定调用者中对应成员变量的数据
void set(Object instance, Object value);

// 获取指定调用者中指定成员变量的数据
Object get(Object instance);

区别:getFields返回的是申明为public的属性,包括父类中定义,getDeclaredFields返回的指定类定义的所有定义(含私有域)的属性,不包括父类的。

5.其他
//获取访问权限
getModifiers();
String modifier = Modifier.toString(field.getModifiers());

// 获取字段的数据类型对应的Class对象
Class type = field.getType();

// 获取字段的名称
String name = field.getName();

// 获取类注解
Annotation[] annotations = clazz.getAnnotations();

// 获取方法注解
method.getAnnotation(Class<T> annotationClass);

// 获取属性注解
field.getAnnotation(Class<T> annotationClass);

//利用反射来创建类的对象并调用方法 
Demo instance = clazz.newInstance();
Method get = clazz.getDeclaredMethod("get", String.class);
get.setAccessible(true);
get.invoke(instance, "参数");
四、总结
  1. 成员变量和成员方法在通过反射使用时需要提供使用者,因为对象可能不一样。
  2. 暴力反射获取如果要使用就调用setAccessible方法赋值true。
  3. 反射中大多数异常为找不到指定内容或参数类型不匹配等。
  4. 注意get…和getDeclared…方法的区别。
  5. 反射和注解的结合在规模较大的程序中有很大的作用。
  6. 反射在提高程序扩展性和灵活性的同时也降低了性能,切勿滥用。

end

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/737864.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-28
下一篇 2022-04-28

发表评论

登录后才能评论

评论列表(0条)

保存