• Java---反射机制


    概念

    • Reflection也就是反射 是Java被视为动态(或准动态)语言的一个关键性质
    • 反射机制指的是程序在运行时能够获取任何类的内部所有信息

    反射机制实现功能概述

    • 只要给定类的全名,即可通过反射获取类的所有信息。
    • 反射可以在程序运行时获取任意一个对象所属的类对象。
    • 在运行时可以获取到类中所有属性对象,并对其操作(包括私有属性)。
    • 在运行时可以获取到类中、父类中所有方法,并调用。
    • 目前主流的应用框架如Struts2、Hibernate、Spring、SpringMVC等框架的核心全部是利用Java的反射机制来实现的。

    Class反射对象描述类语义结构,可以从Class对象中获取构造函数,成员变量,方法等类元素的反射对象,并以编程的方式通过这些反射对象对目标类对象进行操作。这些反射对象类在java.reflect包中定义,下面是最主要的三个反射类

    • Constructor
    • Method

        1.Class getReturnType()

       2.Class[] getParameterTypes()

       3.Class[] getExceptionTypes()

       4.Annotation[][] getParameterAnnotations()

    • Field

    Class对象的机制与实现

    反射机制中class对象的常用方法介绍

    方法名 释义
    getName() 获得类中完整名称
    getDeclaredFields() 获得类中的所有属性
    getDeclaredMethods() 获得类中所有的方法
    getConstructors() 获得类构造方法
    newInstance() 实例化类对象(无参构造函数)

     

     

    获取class对象的三种方式

    //第一种方式
    try {
      demo1 = Class.forName("com.jikexueyuan.bean.Book");
    } catch (Exception e) {
      e.printStackTrace();
    }
    System.out.println(demo1);
            
    Book bo = new Book();
    Object ob = bo;
    System.out.println("第二种:"+ob.getClass());
            
    demo3 = Book.class;
    System.out.println("第三种:"+demo3);
    try {
      Book bo1 = (Book)demo3.newInstance();
      System.out.println("实例化后的类对象:"+bo1);
    } catch (InstantiationException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }

    Field对象的机制与实现

    Java.lang.reflect.Field类,可以操作类/接口中全部属性和属性的信息

    Field对象常用方法

    方法名 释义
    getName() 获得属性名称
    getType() 获得属性类型
    get(Object obj) 取得obj对象中这个属性的值
    set(Object obj, Object value) 向obj对象中这个属性赋值value
    setAccessible(true) 启用/禁用访问控制权限(需要操作类中私有属性时使用)

    Field对象的操作

    //该方法用于使用传递过来的Class对象获取类中的属性
    public void show(Class cl){
      Field[] fi = cl.getDeclaredFields();//可以讲私有属性获取到
      for(Field ff : fi){
        System.out.println(ff.getName());
        System.out.println(ff.getType());
      }
      System.out.println("~~~~~~~~~~~~~~~~~~~");
    
      Field[] fi1 = cl.getFields();//只可以获取到公有属性
      for(Field ff : fi1){
        System.out.println(ff.getName());
        System.out.println(ff.getType());
      }
    }
    //该方法用于使用传递过来的实体类对象 获取属性 以及属性的值 public void show(Object ob){   Class cl = ob.getClass();   Field[] fi = cl.getDeclaredFields();   try {     for(Field ff : fi){       ff.setAccessible(true);//设置启用       System.out.println(ff.getName()+"值"+ff.get(ob));     }   } catch (Exception e) {     e.printStackTrace();   } }

    Method对象的机制与实现

    java.lang.reflect.Method类,可以操作类中全部方法

    Method对象常用方法

    方法名 释义
    getName() 获得方法名称
    getReturnType() 获得方法返回值类型
    invoke(Object obj, Object... args) 利用obj对象调用该方法
    getParameterTypes() 获得方法所有参数类型,按照顺序返回Class数组
    getDeclaredAnnotations() 获取方法的全部注解

    Method对象操作

    //该方法用于获取对象的所有方法名称、返回值类型、以及参数信息
    public void show(Object ob){
      Class cl = ob.getClass();
      Method[] me = cl.getDeclaredMethods();
      for(Method mm : me){
        System.out.println("方法名称:"+mm.getName());
        System.out.println("方法修饰符:" + Modifier.toString(mm.getModifiers()    ));
        System.out.println("方法返回值类型:"+mm.getReturnType());
        Class[] preType = mm.getParameterTypes();
        System.out.println("方法参数列表:");
        for(Class cll : preType){
          System.out.println(cll.getName());
        }
      }
    }
    //该方法用于使用传递过来的实体对象 获取其中指定的方法 并调用
    public void showUse(Object ob){
      Class cl = ob.getClass();
      try {
        Method me = cl.getMethod("getName", null);
        me.invoke(ob, new Object[0]);
        //方法只有一个参数     Method me1
    = cl.getMethod("setName", String.class);     me1.invoke(ob, "西游记");
        //方法有两个及以上参数     Class[] cll
    = {String.class,int.class};     Method me2 = cl.getMethod("test", cll);     Object[] obb = {"哈哈",12};     me2.invoke(ob, obb);   } catch (Exception e) {     e.printStackTrace();   } }

    示例讲解

    编写一个简单示例开始探访Java反射机制的征程,通过比较传统方法以及反射机制创建类实例的不同,来介绍Java反射机制的原理:

    • Car:拥有两个构造函数,一个方法以及三个属性
    public class Car {
        private String brand;
    
        private String color;
    
        private int maxSpeed;
    
        //1.默认构造函数
        public Car(){}
        
        //2.带参构造函数
        public Car(String brand,String color,int maxSpeed){
            this.brand = brand;
            this.color = color;
            this.maxSpeed = maxSpeed;
        }
        
        //3.输出文字
        public void introduce() {
           System.out.println("brand:"+brand+";color:"+color+";maxSpeed:"+maxSpeed);
        }
    
        public String getBrand() {
            return brand;
        }
    
        public void setBrand(String brand) {
            this.brand = brand;
        }
    
        public String getColor() {
            return color;
        }
    
        public void setColor(String color) {
            this.color = color;
        }
    
        public int getMaxSpeed() {
            return maxSpeed;
        }
    
        public void setMaxSpeed(int maxSpeed) {
            this.maxSpeed = maxSpeed;
        }
    }
    • 传统调用方法,使用构造函数设置属性或者set方法设置属性

        1. 构造函数方法:Car car = new Car(“红旗轿车”, “黑色”, 180);

        2. Set方法:Car car = new Car(); car.setBrand(“红旗轿车”);

    • Java反射机制,以一种更加通用的方式间接地操作目标类

    ReflectTest类

    public class ReflectTest {
        
        public static Car  initByDefaultConst() throws Throwable {
            //1.通过类装载器获取程序运行时Car类对象
            ClassLoader loader = Thread.currentThread().getContextClassLoader();        
            Class clazz = loader.loadClass("com.jike.spring.chapter03.reflect.Car");
        
            //2.获取类的默认构造器对象并实例化Car
            Constructor cons = clazz.getDeclaredConstructor((Class[])null);
            Car car = (Car)cons.newInstance();
            
            //3.通过反射方法设置属性
            Method setBrand = clazz.getMethod("setBrand",String.class);        
            setBrand.invoke(car,"奔驰");        
            Method setColor = clazz.getMethod("setColor",String.class);
            setColor.invoke(car,"黑色");        
            Method setMaxSpeed = clazz.getMethod("setMaxSpeed",int.class);
            setMaxSpeed.invoke(car,200);        
            return car;
        }
        
        public static Car initByParamConst()  throws Throwable{
            //1.通过类装载器获取Car类对象
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            Class clazz = loader.loadClass("com.jike.spring.chapter03.reflect.Car");
            
            //2.获取类的带有参数的构造器对象
            Constructor cons = clazz.getDeclaredConstructor(new Class[]{String.class,String.class,int.class});
            
            //3.使参数的构造器对象实例化Car
            Car car = (Car)cons.newInstance(new Object[]{"宝马","红色",180});
            return car;    
        }
        
        public static void main(String[] args) throws Throwable {
            Car car1 = initByDefaultConst();
            Car car2 = initByParamConst();
            car1.introduce();
            car2.introduce();
        }
    }

    以上便是利用java反射机制来获取class类以及方法。

    ClassLoade

    类装载器就是寻找类的字节码文件并构造出类在JVM内部表示的对象组件,主要工作由ClassLoader及其子类负责,ClassLoader是一个重要的Java运行时系统组件,它负责在运行时查找和装入Class字节码文件。

    工作机制

    java中把一个类装载到java虚拟机中需要经过以下步骤

    • 装载:查找和导入Class文件

    链接:执行校验,准备和解析步骤。校验主要是检查载入class文件数据的正确性;准备主要是给类的静态变量分配存储空间;解析主要是将符号引用转换成直接引用

    • 初始化:对类的静态变量、静态代码块执行初始化工作。类装载器工作主要是由classLoader及其子类来负责的。Classloader又是一个重要的java运行时系统组件,负责在运行时查找和装入class字节码文件。

      Jvm在运行时会产生三个classloader:根装载器、扩展类装载器(EXTClassloader)、系统类装载器(APPClassloader),其中根装载器不是classloader的子类,由于是使用c++来编写的,所以在java中看不到。根装载器来负责加载JRE的核心类库。ExtclassloaderAPPclassloader都是classloader的子类。extclassLoader负责装载JRE扩展目录ext中的jar类包,而APPClassloader则负责装载classPath路径下的类包。根装载器是extclassloader的父装载器,而extclassloaderAPPclassloader的父装载器。在默认情况下,使用APPclassloader装载应用程序。

    重要方法

    Class loadClassString name

    Name:指定类装载器需要装载类的名字,必须要使用全限定名。在初始化类之前,应考虑进行类解析。但并不是所有的类都需要解析,如果java虚拟机只需要知道该类是否存在,或找出该类的超类,则不需要解析

    Class defineClass(String name, byte[]b, int off,int len)

    将类文件的字节数组来转换成java虚拟机内部的java.lang.class对象。字节数组可从本地文件系统,或者是远程网络获取;name为完全限定名

    Class findSystemClass(String name)

    从本地文件系统载入class文件,若本地没有该class文件,则会抛出异常,这个方法是java虚拟机默认使用的装载机制

    Class findLoadedClass(String name)

    调用该方法来查看classloader是否已装入到某个类当中,如果已经装入,则返回java.lang.class对象,否则返回null值,如果强行装载已经存在的类,则会抛出链接错误。

    ClassLoader getParent()

    获取类装载器的父装载器,除根装载器外,所有的类装载器都有且仅有一个父装载器。可以编写自己的第三方装载器,以实现特殊的需求。类文件被装载并解析之后,在java虚拟机内相应拥有一个对应的java.lang.class类型属性。该类的对象实例则拥有指向这个类描述对象的引用,而类描述对象又拥有指向关联classloader类。每一个类在java虚拟机中都拥有一个对应的java.lang.class对象,它提供了对结构信息的描述。数组、枚举、注解以及基本的java类型。

    与IOC关系

    Spring中,通过IOC可以将实现类、参数信息等配置在其对应的配置文件中,那么当需要更改实现类或参数信息时,只需要修改配置文件即可,我们还可以对某对象所需要的其它对象进行注入,这种注入都是在配置文件中做的。

    SpringIOC的实现原理利用的就是Java的反射机制,Spring的工厂类会帮我们完成配置文件的读取、利用反射机制注入对象等工作,我们可以通过bean的名称获取对应的对象。

  • 相关阅读:
    Oracle存储过程和自定义函数笔记
    怎样将一个Long类型的数据转换成字节数组
    Java集合框架整理
    有了这些,java IO就不愁了
    java生成Excel文件,下载
    ajax使用
    java transient关键字
    Mysql乐观锁与悲观锁
    Spring(六)Spring执行流程
    Spring(五)AOP
  • 原文地址:https://www.cnblogs.com/xiaobaizhiqian/p/7755293.html
Copyright © 2020-2023  润新知