【Java】Java反射

1、反射Reflect:

非常底层却又非常重要的技术:反射(Reflect),是后期学习各种框架最基础的技能

2、什么是反射Reflect:

反射(Reflect)是在运行时动态访问类与对象的技术

反射是JDK1.2版本后的高级特性,隶属于java.lang.reflect

大多数Java框架都基于反射实现参数配置、动态注入等特性

反射出现原因:在最初编写代码的时候要实例化一个对象,通过new关键字对指定的类进行实例化。但这种方法的缺点是把实例化的工作写死在程序中的,正是因为这种固定写死的代码不够灵活,所以在我们实际工作中会产生各种不便与困扰。

反射的根本目的就是把我们创建对象的时机从原先的程序编译时延长到程序运行时

反射为何如此重要:因为在各种各样Java的高级框架中,这类框架在应用程序启动的时候会动态地去读取这些配置文件的信息,同时利用反射技术在运行的时候去创建不同的对象。正是因为反射的存在,让Java程序变得比以前更加灵活更加容易被管理

Class.forName(包名+类名)是动态反射中最常用的一个方法,用于加载指定的类

Class.forName(包名+类名).newInstance() 表示对这个类进行实例化,相当于常规操作中的new创建对象,而在反射中创建对象就是newInstance

由于newInstance()方法返回的是Object类对象,因此还需要进行强制类型转换

Java反射相比起传统代码,即使增加了新的类,也不需要修改源代码。在运行时动态决定去实例化哪个对象

毫不夸张地说,正是因为Java提供了反射这种灵活的特性才诞生了比如spring,MyBatis等这种Java高级框架。这些高级框架无一例外都是构建在反射这种基础之上的。因此,学好反射对于掌握其他Java框架非常重要。

3、反射的核心类

-Class类

-Constructor构造方法类

-Method方法类

-Field成员变量类

反射就是基于这四个类来完成在运行时动态创建对象,执行方法,访问成员变量这些操作的

3.1 Class类

  • Class是JVM中代表"类和接口"的类,Class在反射中是最基础也是最重要的类
  • Class对象具体包含了某个特定类的结构信息
  • Class对象其本质就是描述其他类的一个对象
  • 通过Class对象可获取对应类的构造方法/方法/成员变量
public class ClassExample {
    public static void main(String[] args) {
        try {
            // 1、Class.forName()方法将指定的类加载到jvm,并返回对应的class对象(字节码文件加载到jvm中)
            Class<?> employeeClass = Class.forName("com.example.demo01.entity.Employee");
            // 2、newInstance 通过默认的构造方法创建对象
            Employee employee = (Employee)employeeClass.newInstance();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

3.2 Constructor构造方法类

public class ConstructorExample {
    public static void main(String[] args) {
        try {
            // 1、Class.forName()方法将指定的类加载到jvm,并返回对应的class对象(字节码文件加载到jvm中)
            Class<?> employeeClass = Class.forName("com.example.demo01.entity.Employee");
            // 2.1 获取到constructor构造方法类 得到指定格式到构造方法 Class数组
            Constructor<?> constructor = employeeClass.getConstructor(new Class[]{
                    Integer.class,
                    String.class,
                    Float.class,
                    String.class
            });
            // 2.2 newInstance 传入对应的方法创建对象
            Employee employee = (Employee)constructor.newInstance(new Object[]{
                    100,
                    "zhangsan",
                    8000f,
                    "开发"
            });
            System.out.println(employee);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            // 当被调用的方法的内部抛出了异常而没有被捕获时就会抛出该异常
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

Constructor类的特点

  • Constructor类是对Java类中的构造方法的抽象。Java中所有的构造方法都可以用Constructor来进行指代。Constructor是一个类,自然是能够实例化出Constructor对象
  • Constructor对象包含了具体类的某个具体构造方法的声明。通过Constructor对象我们可以得到这个具体构造方法它有几个参数,参数的类型是什么。
  • 通过Constructor对象调用带参构造方法创建对象
  • 调用带参构造方法,需要在getConstructor()的参数中传入新的Class数组,在这个Class数组中描述构造方法参数的数量以及它所对应的类型,通过这些标识我们就可以确定使用哪一个构造方法了。因为是Class对象的数组,所以在new Class[] { }的{ }里面我们用逗号隔开,并用类型.class表示对应的类对象。
  • ** InvocationTargetException 当被调用的方法的内部抛出了异常而没有被捕获时就会抛出该异常**

3.3 Method方法类

Method对象指代某个类中的方法的描述。作为Method,它说明的是我们要调用的某个类的方法里面包含了哪些返回值,参数有几个,各是什么类型都是能通过Method对象来获取的

  • Method对象使用class.Obj.getMethod()方法获取
  • 通过Method对象调用指定对象的方法
Method类核心方法类
public class MethodExample {
    public static void main(String[] args) {
        try {
            Class<?> employeeClass = Class.forName("com.example.demo01.entity.Employee");
            Constructor<?> constructor = employeeClass.getConstructor(new Class[]{
                    Integer.class,
                    String.class,
                    Float.class,
                    String.class
            });
            Employee employee = (Employee) constructor.newInstance(new Object[]{
                    100,
                    "zhangsan",
                    8000f,
                    "开发"
            });
            // 3、根据方法名和方法参数类 获取到方法对象
            Method updateSalary = employeeClass.getMethod("updateSalary", new Class[]{Float.class});
            // 3.1 执行对应方法:哪个对象、方法参数值
            Employee invoke = (Employee)updateSalary.invoke(employee, new Object[]{1000f});
            System.out.println(invoke);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            // 当被调用的方法的内部抛出了异常而没有被捕获时就会抛出该异常
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
}

  • classObject.getMethod() 获取指定public修饰的方法对象
    getMethod() 方法传入的第一个参数是方法名第二个是Class对象数组,Class数组{}中的参数是带参构造方法的参数类型的Class对象。倘若涉及到方法重载,光有方法名是不能区分的,所以才有了第二个参数。
  • methodObj.invoke() 调用指定对象的对应方法
    Method对象调用invoke()方法传入两个参数,第一个是执行的对象,第二个参数是Object类型数组,{ }里面是调用带参方法需要传入的参数数值。invoke()方法默认返回的是一个Object对象,需要进行强制类型转换。

4、Field类

  • Field对应某个具体类中的成员变量的声明
  • Field对象使用classObj.getField()方法获取
  • 通过Field对象可为某对象成员变量赋值/取值
public static void main(String[] args) {
        try {
            Class<?> employeeClass = Class.forName("com.example.demo01.entity.Employee");
            Constructor<?> constructor = employeeClass.getConstructor(Integer.class, String.class, Float.class, String.class);
            Employee employee = (Employee) constructor.newInstance(new Object[]{100, "zhangsan", 8000f, "开发"});
            // 4.1 根据classObj获取字段对象Field
            Field enameFiled = employeeClass.getField("ename");
            // 4.2 获取 这个实例employee的字段值
            String ename = (String) enameFiled.get(employee);
            // 4.3 给字段值赋值
            enameFiled.set(employee, "zhangsa");
            System.out.println(employee);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            // 当被调用的方法的内部抛出了异常而没有被捕获时就会抛出该异常
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }
    }

5、getDeclared系列方法

  • getDeclaredConstructor(s)|Method(s)|Field(s)获取对应对象。
  • getDeclaredConstructor表示获取单个构造方法,加上s表示获取当前类所有构造方法;getDeclaredMethod表示获取当前对象的单个方法,加s则获取所有方法;getDeclaredField表示获取当前类的单个成员变量,加s则表示获取所有成员变量。
  • getConstructor(s)|Method(s)|Field(s) 只能获取public修饰的对象,而getDeclared系列方法则不管是什么范围修饰符都能获取
  • 通常getDeclared方法获取到的对象可能是私有的,倘若访问或者调用非作用域内构造方法、方法、成员变量,就会抛出异常
  • 通过classObj.getDeclaredFields()获得当前类的所有成员变量,Field对象调用getName()可以得到变量名称
  • getModifiers() 用于判断修饰符 返回整型数值若为1,则是public。若为2,则是private。
  • 获取当前对象的所有成员变量,如果是public修饰的成员变量,可以用Field对象调用get()方法获取。如果是pravite修饰的成员变量,则可以通过public修饰的getter方法获取。通过创建Method对象传参getter方法名生成Method对象。再调用invoke()获取成员变量。
  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值