Java —— 反射

反射

1. 反射的概述(熟悉)

  • Java给我们提供了一套API,使用这套API我们可以在运行时动态的获取指定对象所属的类,创建运行时类的对象,调用指定的结构(属性、方法)等。

  • API:

    • java.lang.Class:代表一个类
    • java.lang.reflect.Method:代表类的方法
    • java.lang.reflect.Field:代表类的成员变量
    • java.lang.reflect.Constructor:代表类的构造器
    • ……
  • 反射的优点和缺点

    • 优点:
      • 提高了Java程序的灵活性和扩展性,降低了耦合性,提高自适应能力

      • 允许程序创建和控制任何类的对象,无需提前硬编码目标类

    • 缺点:
      • 反射的性能较低
        • 反射机制主要应用在对灵活性和扩展性要求很高的系统框架上
      • 反射会模糊程序内部逻辑,可读性较差。
  • 创建对象并调用与反射创建对象并调用使用场景

    • 从程序员开发者角度看:开发中主要完成业务代码,对相关的对象、方法的调用都是确定的,所以使用非反射的方式多一些

    • 反射体现了动态性(可以在运行时动态的获取对象所属的类,多态的调用相关的方法),所以在设计框架的时候会大量使用反射。
      意味着,如果需要学习框架源码,那么就需要学习反射
      框架 = 注解 + 反射 + 设计模式

2. Class类:反射的源头

  • Class类的理解

    • 针对于编写好的.java源文件进行编译(javac.exe),会生成一个或多个.class字节码文件。
      然后使用java.exe命运对指定的.class文件进行解释运行(这个解释运行的过程中,我们需要将.class字节码文件加载(类的加载器)到内存中(存放在方法区))。
      加载到内存中的.class文件对应的结构即为Class的一个实例
      如:加载到内存中的Person类或String类,都作为Class的一个实例

      • Class classPerson = Person.class; //运行时类(类.class,运行时加载到内存)
      • Class classString = String.class;
      • Class classComparable = Comparable.class;
    • 说明:运行时类在内存中(方法区)会缓存起来,在整个执行期间,只会加载一次。

    • 同一个加载器可以加载一次,不同的加载器可以加载多次

  • 获取Class实例的几种方式

    • 1.调用运行时类的静态属性:class

    • 2.调用运行时类的对象的getClass()

    • 3.调用Class的静态方法forName(String className) 此种更好的体现动态性

    • 4.使用类的加载器的方式

    • 代码演示:

    //1.调用运行时类的静态属性:class
    Class classU1 = User.class;
    System.out.println("classU1 = " + classU1);
    
    //2.调用运行时类的对象的getClass()
    User u1 = new User();
    Class classU2 = u1.getClass();
    System.out.println("classU2 = " + classU2);
    
    System.out.println(classU1 == classU2);  //true
    
    
    //3.调用Class的静态方法forName(String className)   此种更好的**体现动态性**
    String className = "com.atguigu02._class.User";  //全类名
    Class classU3 = Class.forName(className);
    System.out.println("classU3 = " + classU3);
    
    System.out.println(classU3 == classU2);  //true
    
    //4.使用类的加载器的方式
    Class classU4 = ClassLoader.getSystemClassLoader().loadClass("com.atguigu02._class.User");
    System.out.println("classU4 = " + classU4);
    
    System.out.println(classU1 == classU4);  //true
    
    
    • 运行结果
    classU1 = class com.atguigu02._class.User
    classU2 = class com.atguigu02._class.User
    true
    classU3 = class com.atguigu02._class.User
    true
    classU4 = class com.atguigu02._class.User
    true
    

3. 反射的应用(理解)

通过反射调用指定的结构:指定的属性、方法、构造器

  • getXxx():获取到运行时类本身及其所有的父类中声明为public权限的属性/方法/构造器

  • getDeclaredXxx():获取当前运行时类中声明的所有属性/方法/构造器(还要允许访问setAccessible(true))

  • 调用属性:
    步骤:
    ①通过Class实例调用getDeclaredField(String fieldName),获取运行时类指定名的属性
    ②setAccessible(true):确保此属性名可以被访问
    ③通过Field类的实例调用get(Object obj)(获取属性) / set(Object obj,Object name)(设置属性)

  • 调用方法:
    步骤:

    Class<Person> personClass = Person.class;  //获取运行时类
      Person person = personClass.newInstance();  //创建对象
      // ①class实例调用getDeclaredMethod(String methodName,Class...args),获取该实例指定的方法
      Method showNationMethod = personClass.getDeclaredMethod("showNation", String.class,int.class);
      // ②允许此方法访问
      showNationMethod.setAccessible(true);
      // ③通过Method实例调用invoke(Object obj,Object...objs),即调用Method对应的方法。
      // invoke()的返回值即为Method对应的方法的返回值,若为void,则返回null
      String china = (String) showNationMethod.invoke(person, "CHINA", 28);
      System.out.println("showNation = " + china);
    
  • 调用构造器
    步骤:

      //①获取运行时类
      Class personClass = Class.forName("com.atguigu03.reflectapply.data.Person");
      //②创建运行时类的构造器:通过Class的实例调用getDeclaredConstructor(Class...agrs),获取指定参数类型的构造器。
      Constructor personConstructor = personClass.getDeclaredConstructor(String.class, int.class);
      //③允许访问
      personConstructor.setAccessible(true);
      //④使用构造器创建实例对象:通过Constructor实例调用newInstance(Object...objs),返回一个运行时类的实例
      Person person = (Person) personConstructor.newInstance("ljl", 18);
      System.out.println("person = " + person);
    

4. 反射的动态性

public class ReflectTest {

    //体会:静态性
    public Person getInstance(){
        return new Person();
    }


    //体会:反射的动态性

    //举例1:根据传入的全类名去创建不同的类的实例
    public <T> T getInstance(String className) throws Exception {

        //获取大的Class实例
        Class aClass = Class.forName(className);

        //获取指定构造器
        Constructor constructor = aClass.getDeclaredConstructor();

        constructor.setAccessible(true);

        //返回构造的实例对象
        return (T) constructor.newInstance();

    }

    @Test
    public void test1() throws Exception {
        Person p1 = getInstance();  //不能改变
        System.out.println("p1 = " + p1);

        String className = "com.atguigu04.other.dynamic.Person";
        Person p2 = getInstance(className);  //随着className动态变化去加载实例

        System.out.println("p2 = " + p2);
    }


    //体会:反射的动态性
    //类似:todo:前后端分离:如login、register等,前端传入数据之后再创建实例,体现为动态性(调用的时候再创建对象),否则如果先创建实例,会造成大量内存泄露
    //举例2:
    public Object invoke(String className,String methodName) throws Exception {

        //1.创建全类名对应的运行时类的对象
        Class aClass = Class.forName(className);

        Constructor constructor = aClass.getDeclaredConstructor();

        constructor.setAccessible(true);

        Object obj = constructor.newInstance();

        //2.获取运行时类中指定的方法,并调用
        Method method = aClass.getDeclaredMethod(methodName);

        method.setAccessible(true);

        return method.invoke(obj);

    }

    @Test
    public void test2() throws Exception {
        String className = "com.atguigu04.other.dynamic.Person";
        String methodName = "show";

        Person invoke = (Person) invoke(className, methodName);
        System.out.println("invoke = " + invoke);

    }

}
  • 8
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值