月黑雁飞高,反射全知道——剖析java反射机制

java 反射机制

反射机制概念

  • 在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。反射被视为动态语言的关键。简单来说反射就是java的各种成分映射成对应的java类。
  • 通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。包括构造方法,属性,方法。

java反射提供的功能

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法;
  • 生成动态代理。

java不是动态语言,但是通过反射机制实现动态机制。

反射的基本使用

java反射的主要组成

  • Class:任何运行在内存中的所有类都是改Class类的实例对象,每个Class类对象内存都包含了本来的所有信息。通过反射干任何事情,都要先去找Class
    • 类内部主要信息:
      • Field:所有属性
      • Constructor:所有构造方法
      • Method:所有方法
  • Field:描述一个类的属性,内部包含了该属性的所有信息,例如数据类型,属性名,访问修饰符
    • 类内部主要信息
      • 标注在属性上的注解
      • 属性名
      • 属性的数据类型(boolean/double/int/String …)
      • 属性访问修饰符(public/private/protected)
  • Constructor:描述一个类的构造方法,内部包含了构造方法的所有信息,例如参数类型、参数名称、访问修饰符
    • 类内部主要信息
      • 构造方法的访问修饰符
      • 构造方法的参数
        • 参数的数据类型
        • 参数的名称
        • 标注在参数上的注解
  • Method:描述一个类的所有方法(包括抽象方法),内部包含了该方法的所有信息,与Constructor类似,不同之处就是Method拥有返回值类型信息,因为构造方法是没有返回值的
    • 类内部主要信息
      • Constructor类内部主要信息
      • 方法返回值类型(Int/double0

反射使用步骤(获取Class对象、调用对象方法)

  • 获取想要操作的类的Class对象,它是反射的核心,通过Class对象我们可以任意调用类的方法
  • 调用Class类中的方法,即就是反射的使用阶段
  • 使用反射API来操作这些信息

反射使用

例子People

public class People {
    
    
    public String name;
    public int age;
    private double weight;
    
    public People(){
    
    }
    
    public People(String name,int age){
    
    
        this.name=name;
        this.age=age;
    }
    
    public void getInfo(){
    
    
        System.out.println(name+"的年龄是:"+age);
    }
}

获取Class的三种方法(获取一个类的字节码对象)

​ 当使用javac编译后,就会产生一个.class文件,当字节码文件被装载进虚拟机执行的时候,会在内存中生成Class对象,包含了该类内部的所有信息,在程序运行阶段可以获取这些信息。

  • 使用对象获取,使用对象的getClass获取

    People people = new People();
    Class clazz=people.getClass();
    
  • 使用静态属性class

    只有在编译前就已经声明了该类的类型才能获取到Class对象

    Class clazz =People.class;
    
  • 使用Class类的静态方法forName(通过类的全限定名获取该类的 Class 对象)

    • 类要写全名

      Class clazz = Class.forName("")
      

    当我们拿到class对象后就可以,获取类信息、调用其方法、获取其属性。

获取一个类的所有信息

​ Class对象中国包含了该类的所有信息,在编译期间我们能看到的信息及时该类的变量、方法、构造器,在运行时最常被获取的也是这些信息

每种功能内部以Declared细分为两类:

  • 有Declared修饰的方法:可以获取该类内部包含的所有变量、方法和构造器,但是无法获取继承下来的信息
  • 无Declared修饰的方法:可以获取该类中public修饰的变量、方法和构造器,以及获取继承下来的信息

如果要想获取类中所有的(包含继承)的变量、方法和构造器,则需要同时调用,并且用set集合存储他们获得的遍历,一方获取到相同的东西。

获取类中的变量(Field)
  • Field[] getField():获取类中所有被public修饰的所有变量
  • Field getField(String name):根据变量名获取类中的一个变量,改变了必须是被public修饰
  • Field[] getDeclaredFields):获取类中所有的变量,但无法获取继承下来的变量
  • Field getDeclaredField(String name):根据名称获取类中的变量,也无法获取继承下来的变量
获取类中的方法(Method)
  • Method[] getMethods():获取类中被public修饰的所有方法
  • Method getMethod(String name, Class…<?> paramTypes):根据名字和参数类型获取对应方法,该方法必须被public修饰
  • Method[] getDeclaredMethods():获取所有方法,但无法获取继承下来的方法
  • Method getDeclaredMethod(String name, Class…<?> paramTypes):根据名字和参数类型获取对应方法,无法获取继承下来的方法
获取类的构造器(Constructor)
  • Constuctor[] getConstructors():获取类中所有被public修饰的构造器
  • Constructor getConstructor(Class…<?> paramTypes):根据参数类型获取类中某个构造器,该构造器必须被public修饰
  • Constructor[] getDeclaredConstructors():获取类中所有构造器
  • Constructor getDeclaredConstructor(class…<?> paramTypes):根据参数类型获取对应的构造器

如果父类的属性用protected修饰,利用反射是无法获取到的

获取注解
  • Annotation[] getAnnotations():获取该对象上的所有注解
  • Annotation getAnnotation(Class annotaionClass):传入注解类型,获取该对象上的特定一个注解
  • Annotation[] getDeclaredAnnotations():获取该对象上的显式标注的所有注解,无法获取继承下来的注解
  • Annotation getDeclaredAnnotation(Class annotationClass):根据注解类型,获取该对象上的特定一个注解,无法获取继承下来的注解

​ 只有注解的@Retension标注为RUNTIME时,才能通过反射获取到该注解

​ 详情请点击:注解详情

通过反射调用方法

​ 通过反射获取到某个Method类对象后,可以通过调用invoke方法执行

  • invoke(Oject obj, Object... args):参数``1指定调用该方法的**对象**,参数2`是方法的参数列表值。
  • 如果调用的方法是静态方法,参数1只需要传入null,因为静态方法不与某个对象有关,只与某个类有关。

反射的应用场景

常见场景有三种:

  • Spring实例化对象:当程序启动时,Spring会读取配置文件applicationContext.xml并解析出里面所有的标签实例化到IOC容器中
  • 反射+工程模式:通过反射消除工厂中的多个分支,如果需要生产新的类,无序关注工厂类,工厂类可以应对各种新增类,反射可以使得程序更加健壮
  • JDBC连接数据库:使用JDBC连接数据库时,指定连接数据库的驱动类时用到反射加载启动类

Spring的IOC容器

​ 在Spring中,经常会用到一个上下文配置文件applicationContext.xml里面就是关于bean的配置,查询启动时会读取该xml文件,解析出所有的标签,并实例化对象放入IOC容器中,IOC容器本质上就是一个工厂,通过工厂传入标签的id属性获取对应的实例。

​ Spring在实例化对象的过程经过简化后,可以理解为反射实例化对象的步骤

  • 获取Class对象的构造器
  • 通过构造器调用newInstance()实例化对象

当然Spring在实例化对象时,做了非常多额外的操作,才能让现在的开发便捷且稳定

反射+抽象工程模式

​ 传统的工程模式,如果需要生产新的子类,就需要修改工厂类,在工厂类中增加新的分支。

​ 利用反射和工厂模式相结合,在生产新子类时,工厂类不需要修改任何东西,可以专注于子类的实现,当子类确定下来时,工厂也就可以生成该子类了、

反射+抽象工厂的核心思想是:

  • 在运行时通过参数传入不同子类的全限定名获取到不同的Class对象,调用newInstance()方法返回不同的子类。

JDBC加载数据库驱动类

​ 在导入第三方库时,JVM不会主动去加载外部导入的类,而是等到真正使用时,才会去加载需要的类,正因为如此,我们可以在获取数据库连接时传入驱动类的全限定名,交给JVM加载该类

反射的优缺点

反射的优点:

  • 增加程序灵活性:面对需求变更时,可以灵活地实例化不同的对象

反射的缺点:

  • 破坏类的封装性:可以强制访问private修饰的信息
  • 性能损耗:反射相比直接实例化对象、调用方法、访问变量、中间需要很多检查步骤以及解析步骤,JVM无法对其进行优化

反射总结

  • 反射的思想:反射就想一面镜子,只有在运行时才能看到自己是谁,可以获取到自己的信息,甚至实例化对象
  • 反射的作用:在运行时才确定实例化对象,使得程序更加的健壮,面对需求变更时,可以最大程度地做到不修改程序源码的情况下,应对不同的场景,实例化不同类型的对象。
  • 反射的应用场景:Spring的IOC容器;反射+工厂模式,使得工厂类更加稳定;JDBC连接数据库时加载驱动类
  • 反射的三个特点:增加程序的灵活性、破坏类的封装性以及性能损耗

最后

  • 如果觉得看完有收获,希望能给我点个赞,这将会是我更新的最大动力,感谢各位的支持
  • 欢迎各位关注我的公众号【java冢狐】,专注于java和计算机基础知识,保证让你看完有所收获,不信你打我
  • 如果看完有不同的意见或者建议,欢迎多多评论一起交流。感谢各位的支持以及厚爱。

猜你喜欢

转载自blog.csdn.net/issunmingzhi/article/details/108342487