使用反射机制

一、认识反射

1.反射机制

  • 是 Java 的特性之一
  • 是构建框架技术的基础所在
  • 指在运行状态中,动态获取信息以及动态调用对象方法的功能
  • 有3个动态性质
    • 运行时生成对象实例
    • 运行期间调用方法
    • 运行时更改属性
  • 在编译时不确定是哪个类被加载了,而是在程序运行时才加载、探知和使用
  • 在计算机科学领域,反射是指一类应用,它们能够自描述和自控制
  • 能够知道类的基本结构,称为Java 类的自审
  • 如 代码的自动提示功能,就是利用此反射的原理
  • 使用反射,可以实现以下功能:
    • 在运行时判断任意一个对象所属的类
    • 在运行时构造任意一个类的对象
    • 在运行时判断任意一个类所具有的方法和属性
    • 在运行时调用任意一个对象的方法

2.Java 反射常用API

使用 Java 反射技术常用的类如下:
  • class 类:反射的核心类
    • 反射所有的操作都是围绕该类来生成的
    • 通过此类可以获得类的属性、方法等内容信息
  • Field 类:表示类的属性
    • 可以获取和设置类中属性的值
  • Method 类:表示类的方法
    • 可以用来获取类中方法的信息、执行方法
  • Constructor 类:表示类的构造方法
在 Java 程序中使用反射的基本步骤如下
  • 导入 java.lang.reflect.*
  • 获得需要操作的类的 Java.lang.Class 对象
  • 调用 Class 的方法获取 Field,Method 等对象
  • 使用反射API进行操作

二、反射的应用

1.获取类的信息

  • 获取 Class 对象
    • 调用对象的 getClass() 方法
      • 是 java.lang.Object 类中的一个方法
      • 该方法返回该对象所属类对应的 Class 对象
      • Student stu = new Student();
        Class cla = stu.getClass();
    • 调用类的 class 属性
      • 可获取该类对应的 Class 对象
      • 需要在编译期间就知道类的名称
      • Class cla = Student.class;
    • 使用 Class 类的 forName() 静态方法
      • 需要传入字符串参数
      • 该字符串参数的值是某个类的全名
      • 即要在类名前添加完整的包名
      • 如果传入的字符串不是类的全名,就会抛出一个 ClassNotFoundException
      • Class cla = Class.forName(“com.tttest.reflection.Student”);
    • 调用某个类的 class 属性来获取该类对应的 Class 对象这种方式更有优势
      • 代码更安全 :在编译阶段就可以检查出需要访问的 class 对象是否存在
      • 程序性能更高 :无需调用方法,性能更好
  • 通过 Class 对象获取信息
    • 在获得某个类所对应的 Class 对象之后
    • 程序就可以调用 Class 对象的方法类获取该类的详细信息
    • Class 类提供了大量实例方法来获取 Class 对象所对应的详细信息
      • 访问 Class 对应的类所包含的构造方法
方法说明
Constructor getConstructor(Class[] params)返回此 Class 对象所包含的类的指定的 public 构造方法,params 参数是按声明顺寻指定该方法参数类型的 Class 对象的一个数组。构造方法的参数类型与 params 所指定的参数类型匹配
Constructor[] get Constructors()返回此 Class 对象所包含的类的所有 public 构造方法
Constructor getDeclaredConstructor(Class[] params)返回此 Class 对象所包含的类的指定构造方法,与构造方法的访问级别无关
Constructor[] getDeclaredConstructors()返回此 Class 对象所包含的类的所有构造方法,与构造方法访问级别无关
  • 访问 Class 对应的类所包含的方法
方法说明
Method getMethod(String name, Class[] params)返回此 Class 对象所包含的类的指定的 public 方法, name 参数用于指定方法名称, params 参数是按声明顺序标志该方法参数类型的 Class 对象的一个数组
Method[] getMethods返回此 Class 对象所包含的类的所有 public 方法
Method getDeclaredMethod(String name,Class[] params)返回此 Class 对象所包含的类的指定方法,与方法的访问级别无关
Method[] getDeclaredMethods()返回此 Class 对象所包含的类的全部方法,与方法的访问级别无关
  • 访问 Class 对应的类所包含的属性
方法说明
Field getField(String name)返回此 Class 对象所包含的类的指定的 public 属性,name 参数用于指定的属性名称
Field[] getFields()返回此 Class 对象所包含的类的所有 public 属性
Field getDeclaredField(String name)返回此 Class 对象所包含的类的指定属性,与属性的访问级别无关,name 表示属性名称
Field[] getDeclaredFields()返回此 Class 对象所包含的类的全部属性,与属性的访问级别无关
  • 访问 Class 对应的类所包含的注解
方法说明
<A extends Annotation>A getAnnotation(Class<A> annotationClass)试图获取该 Class 对象所表示类上指定类型的注解,如果该类型的注解不存在则返回 null ,其中 annotationClass 参数到底英语注解类型的 Class 对象
Annotation[] getAnnotations()返回此类上存在的所有注解
Annotation[] getDeclaredAnnotations()返回直接存在于此类上的所有注解
  • 访问 Class 对应的类的其他信息
方法说明
Class[] getDeclaredClasses()返回该 Class 对象所对应类里包含的全部内部类
Class[] getDeclaredClass()返回 Class 对应的类所在的外部类
Class[] getInterfaces()返回该 Class 对象对应类所实现的全部接口
int getModifiers()返回此类或接口的所有修饰符,返回的修饰符由 public、protected、private、final、static、abstract等对应的常量组成,返回的整数应使用 Modifer 工具类的方法来解码,才可以获取真实的修饰符
Package getPackage()获取此类的包
String getName()以字符串形式返回此 Class 对象所表示的类的名称
String getSimpleName()以字符串形式返回此 Class 对象所表示的类的简称
Class getSuperclass()返回该 Class 所表示的类的超类对应的 Class 对象
  • Class 对象可以获得该类里的成员,包括方法,构造方法及属性。
  • Method\Constructor\Field这3个类都定义在 java.lang.reflect 包下
  • 实现了 java.lang.reflect.Member 接口
    • 通过 Method 对象来执行对应的方法
    • 通过 Constructor 对象来调用相应的构造方法创建对象
    • 通过 Field 对象直接访问并修改对象的属性值

2.创建对象

通过反射来创建对象有如下两种模式:

  • 使用 Class 对象的 newInstance() 方法创建对象
    • 要求该 Class 对象的对应类由默认构造方法
    • 而执行 newInstance() 方法时实际上是利用默认构造方法来创建该类的实例
import java.util.Date;
public class ReflectTest {
    public static void main(String[] args){
        Class cla=Date.class;
        try{
            Date d=(Date)cla.newInstance();
            System.out.println(d.toString());
        }catch (Exception e){
            e.getStackTrace();
        }
    }
}
  • 使用 Constructor 对象创建对象
    • 先使用 Class 对象获取指定的 Constructor 对象
    • 再调用 Constructor 对象的 newInstance() 方法创建该 Class 对象对应类的示例
    • 可以选择使用某个类的指定构造方法来创建实例
import java.lang.reflect.Constructor;
import java.util.Date;
public class ReflectTest {
    public static void main(String[] args){
        //获取 Date 对应的 Class 对象
        Class cla=Date.class;
        try{
            //获取 Date 中带一个长整型参数的构造方法
            Constructor cu=cla.getConstructor(long.class);
            //调用Constructor的newInstance()方法构建对象
            Date d=(Date)cu.newInstance(1997);
            System.out.println(d.toString());
        }catch (Exception e){
            e.getStackTrace();
        }
    }
}

3.访问类的属性

使用 Field 对象可以获取对象的属性
通过 Field 对象可以对属性进行取值或赋值操作

方法说明
Xxx getXxx(Object obj)该方法中 Xxx 对应8个基本数据类型,obj为该属性所在的对象
Object get(Object obj)得到引用类型属性值
void setXxx(Object obj,Xxx val)将 obj 对象的该属性设置成 val 值,此处的 Xxx 对应8个基本数据类型
void set(Object obj, object val)将 obj 对象的该属性设置成 val 值,针对引用类型赋值
void setAccessible(bool flag)对获取到的属性设置访问权限,参数为true,可以对私有属性取值和赋值

4.访问类的方法

使用Method 对象可以调用对象的方法

  • Object invoke(Object obj,Object args)
    • obj 是执行该方法的对象
    • args 是执行该方法时传入该方法的参数

import java.lang.reflect.*;
public class ReflectTest {
    public static void main(String[] args){
      Class cla=Student.class;
       //创建Student对象
        Student stu=new Student();
        //得到setName方法
        try{
            Method met1=cla.getMethod("setName",String.class);
            //调用setName方法,为name赋值
            met1.invoke(stu,"Bobby");
            //得到getName方法
            Method met2=cla.getMethod("getName",null);
            //调用getName方法,获取name的值
            Object obj=met2.invoke(stu,null);
            System.out.println(obj);
        }catch (Exception e){
            e.getStackTrace();
        }
    }
}
  • 当通过 Method 的 invoke()方法调用对应的方法时,Java 会要求程序必须有调用该方法的权限
  • 如果程序确实需要调用某个对象的 private 方法,可以先调用 setAccessible() 方法
    • 将 Method 对象的 accessible 标志设置为指示的布尔值
    • 值为 true 则表示该 Method 在使用时应该取消 Java 语言访问权限检查
    • 值为 false 则表示该 Method 在使用时应该进行 Java 语言访问权限检查

5.使用 Array 类动态创建和访问数组

程序可以通过使用 Array 类来动态地创建数组、操作数组元素等

import java.lang.reflect.*;
public class ReflectTest {
    public static void main(String[] args){
   
      //创建一个元素类型为String ,长度为5的数组
      Object arr=Array.newInstance(String.class,5);
      
      //依次为数组中index为2,3的元素赋值
        Array.set(arr,2,"Jack");
        Array.set(arr,3,"Rose");
        
        //依次取出数组中index为2,3的元素的值
        Object o1=Array.get(arr,2);
        Object o2=Array.get(arr,3);
        
        System.out.println(o1);
        System.out.println(o2);
    }
}

注意:

  • 通过反射创建对象时性能要稍微低一些
  • 只有当程序需要动态创建某个类的对象时才会考虑使用反射
  • 通常在开发通用性比较广的框架、基础平台时可能回大量使用反射
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值