什么是反射

反射是指在Java运行状态中  给定一个类对象(Class对象),通过反射获取这个类对象 (Class对象)的所有成员结构;
给定一个具体的对象,能够动态地调用它的方法及对任意 属性值进行获取和赋值
这种动态获取类的内容、创建对象、以及动态调用对象方法及操作属性的机制,就叫做Java的反射机制

反射能做什么?

在运行时判断一个类所具有的成员变量和方法

在运行时判断任意一个对象所属的类

在运行时构造一个类的对象

在运行时调用任意一个对象的方法

优缺点

优点:
增加程序的灵活性,避免将固有的逻辑程序写死到代码里
代码简洁,可读性强,可提高代码的复用率
缺点 :
相较直接调用在创建对象比较多的情景下反射性能下降
内部暴露和安全隐患(破坏单例)

反射性能慢的原因

寻找类Class字节码的过程,比如通过ClassName找到对应的字 节码Class,然后进行加载、解析,也会比较慢,而new的方式 则无需寻找,因为在Linking的解析阶段已经将符号引用转为 了直接引用
安全管理机制的权限验证等等
若需要调用native方法调用时JNI接口的使用
入参校验

具体详细原因参考:

https://www.jianshu.com/p/4e2b49fa8ba1

class对象的组成

image.png

反射的基本操作

获取Class文件的方式

四种方式:
1、类名.class
2、Class.forName //反射
3、对象.getClass()
4、通过类加载器.loadClass()

  1. //1、类名.class获取class文件,这种方式不会对类初始化
  2. Class<Person> clazz01 = Person.class;
  3. System.out.println(clazz01);
  4. //2、会进行类的初始化操作,所以咱们在加载jdbc的MySQL驱动的时候都是用这种方式去加载的
  5. Class<?> clazz02 = Class.forName("com.nx.vip.javacore.reflection.pojo.Person");
  6. System.out.println(clazz02);
  7. //3、已经创建出来的实例对象,可以直接用这种方式,
  8. //既然实例对象都有了,为什么还要获取实例对象所对应的类对象?
  9. //既然实例对象都有了,说明这个类已经被加载到jvm虚拟机中了,可以直接调用getClass从虚拟机中获取到类所定义的类对象
  10. Class<? extends Person> clazz03 = new Person().getClass();
  11. System.out.println(clazz03);
  12. //4、通过类加载器.loadClass()
  13. Class<?> clazz04 = CoreMain.class.getClassLoader().loadClass("com.nx.vip.javacore.reflection.pojo.Person");
  14. System.out.println(clazz04);

class.forName和classLoader的区别?

class.forName:

  • 将类的.class文件加载到jvm中
  • 对类进行解释,执行类中的static块

classLoader:

  • 只干一件事情:将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。

Class.forName(className):内部调用的方法是 Class.forName(className,true,classloader);
第2个boolean参数表示类是否需要初始化, Class.forName(className)默认是需要初始化。
一旦初始化,就会触发目标对象的static块代码执行,static参数也也会被再次初始化。

ClassLoader.loadClass(className):
内部调用的方法是ClassLoader.loadClass(className,false);
第2个 boolean参数,表示目标对象是否进行链接,false表示不进行链接,不进行链接意味着不进行包括初始化等一些列步骤,那么静态块和静态对象就不会得到执行

基本信息操作

  1. int modifier = clazz.getModifiers(); // 获取类修饰符
  2. Package aPackage = clazz.getPackage(); // 获取类包名
  3. String fullClassName = clazz.getName(); // 获取类的全路径名称
  4. String simpleName = clazz.getSimpleName(); // 获取类的简单名称
  5. ClassLoader classLoader = clazz.getClassLoader(); // 获取类加载器
  6. Class[] interfaces = clazz.getInterfaces(); // 获取类实现的接口列表
  7. Class superclass = clazz.getSuperclass(); // 获取类的父类
  8. Annotation[] annotations = clazz.getAnnotations(); // 获取类的注解信息

类的属性操作

  1. Person person = (Person) clazz.newInstance();
  2. // 获取类中所有的共有字段 包含继承的字段
  3. Field[] fields = clazz.getFields();
  4. // 获取类中定义的字段 内部
  5. Field[] declaredFields = clazz.getDeclaredFields();
  6. // 获取指定名称的类中定义的字段
  7. Field nameField = clazz.getDeclaredField("name");
  8. // 获取字段的修饰符
  9. int modifiers = nameField.getModifiers();
  10. // 指定字段强制访问
  11. nameField.setAccessible(true);
  12. // 修改字段你的值
  13. nameField.set(person,“samuel");
  14. // 静态字段赋值
  15. nameField.set(null,"静态字段赋值");

类的方法操作

  1. // 获取类中的所有的共有的方法 继承
  2. Method[] methods = clazz.getMethods();
  3. // 获取类中的定义的方法
  4. Method[] declaredMethods = clazz.getDeclaredMethods();
  5. // 获取类中指定名称和参数的公有方法
  6. Method say = clazz.getMethod("say", String.class);
  7. // 获取类中定义的指定名称和参数的方法
  8. Method say1 = clazz.getDeclaredMethod("say");
  9. // 获取方法的修饰符
  10. int modifiers1 = say.getModifiers();
  11. // 指定对象进行成员方法的调用
  12. Object samuel666 = say.invoke(person, samuel666");
  13. say.setAccessible(true);// 指定方法的强制执行
  14. // 静态方法调用
  15. say.invoke(null);

类的构造器操作

  1. Constructor[] cons = clazz.getConstructors(); //获取类中所有的公有构造器
  2. Constructor[] cons1 = clazz.getDeclaredConstructors(); //获取类中所有的构造器
  3. Constructor conNoParam= clazz.getDeclaredConstructor(); //获取类中无参的构造器
  4. Constructor con= clazz.getDeclaredConstructor(String.class,String.class); //获取类中有参构造
  5. int modifers = con.getModifiers(); //获取构造器的修饰符
  6. conNoParam.newInstance(); //构造器实例对象
  7. con.setAccessible(true); //指定方法的强制访问
  8. con.newInstance("abc","bbb"); //有参构造调用
  9. Person.class.newInstance(); //class直接调用默认无参构造