• 反射与注解


    【今日】

         人生只有一次   不妨大胆一点

    目录

    一   反射 

     1.访问构造方法

    1.Constructor的使用

    2.反射一个类中的所有构造方法

    3.用Constructor创建并返回实列对象 

    2.访问成员变量

    1Field的使用方法

     2.反射一个类中的所有成员变量

    3.获取修改成员变量的值 

    3.访问成员方法 

     1.Method类方法

    2.反射一个类中所有的成员方法

    3.用反射调用方法 


     今日说法:反射

         通过Java的反射机制,程序员可以更深入地控制程序的运行过程。例如,可在程序运行时对用户输入的信息进行验证,还可以逆向控制程序的执行过程。另外,Java还提供了Annotation注解功能,该功能建立在反射机制的基础上。

    一   反射 

     通过Java反射机制,可以在程序中访问已经装载到JVM中的Java对象的描述,实现访问、检测和修改描述Java对象本身信息的功能。Java反射机制的功能十分强大,在java.lang.reflect包中提供了对该功能的支持。
    众所周知,所有Java类均继承了Object类,在Object类中定义了一个getClass方法,该方法返回一个类型为Class的对象。例如下面的代码:

    1. JTexFileld texFild = new JTexFileld(); //创建JTexFileld对象
    2. Class textFiekdC = textFiled.getClass(); //获取Class对象

    利用Class类的对象textFieldC,可以访问用来返回该对象的描述信息。可以访问的主要描述信息如下表:

    在讲具体的反射之前,我们要对构造方法,成员变量,成员方法有一定了解,可以复习一下之前内容:http://t.csdn.cn/68acc

     1.访问构造方法

         在通过下列一组方法访问构造方法时,将返回Constructor类型的对象或数组。每个Constructor对象代表一个构造方法,利用Constructor对象可以操纵相应的构造方法:

    ▣getConstructors()                                   

    ▣getConstructor()

    ▣getDeclaredConstructors()

    ▣getDeclaredConstructor()

    因此我们要先了解Constructor的使用,以及Constructor类中提供的常用的方法。

    1.Constructor的使用

    在Java中,java.lang.reflect.Constructor类是Java反射API中的一个类,用于表示类的构造函数。通过Constructor类,我们可以获取、操作和调用类的构造函数。

    下面是一些使用Constructor类的示例:

    1.获取构造函数:

    1. // 获取指定类的所有公共构造函数
    2. Constructor[] constructors = SomeClass.class.getConstructors();
    3. // 获取指定类的所有构造函数(包括非公共构造函数)
    4. Constructor[] constructors = SomeClass.class.getDeclaredConstructors();
    5. // 获取单个指定的构造函数
    6. Constructor constructor = SomeClass.class.getDeclaredConstructor(paramTypes);

    2.创建对象实例:

    1. // 创建无参构造函数的对象实例
    2. SomeClass obj = constructor.newInstance();
    3. // 创建带有参数的构造函数的对象实例
    4. SomeClass obj = constructor.newInstance(arg1, arg2, ...);

    3.设置可访问性:

    1. // 设置构造函数为可访问(用于访问非公共构造函数)
    2. constructor.setAccessible(true);

    4.获取构造函数信息:

    1. // 获取构造函数的修饰符(public, private, protected, 等)
    2. int modifiers = constructor.getModifiers();
    3. // 获取构造函数的参数类型
    4. Class<?>[] parameterTypes = constructor.getParameterTypes();

    通过Constructor类,我们可以动态地获取、创建和调用类的构造函数,这在某些情况下非常有用,例如需要在运行时根据不同的条件创建对象或者调用非公共构造函数。但是请注意,反射操作通常会带来一些性能上的开销,因此只有在必要时才应该使用。

    Constructor类的常用方法

    在此之前我们先回顾一下构造方法的基本常识:

    2.反射一个类中的所有构造方法

    【代码实列】

    1. public class Example {
    2. int id;
    3. String name;
    4. double price;
    5. public Example() {//无参的构造方法
    6. super();
    7. }
    8. public Example(int id) {//一个参数的构造方法
    9. super();
    10. this.id = id;
    11. }
    12. private Example(int id, String name, double price) {//三个参数的私有构造方法
    13. super();
    14. this.id = id;
    15. this.name = name;
    16. this.price = price;
    17. }
    18. }
    19. /*
    20. *这里创建了一个Example类
    21. *
    22. */
    23. import java.lang.reflect.Constructor;
    24. import java.lang.reflect.Modifier;
    25. public class Demo {
    26. public static void main(String[] args) {
    27. try {
    28. Class c = Class.forName("Example");//传入string类型参数”Example",要求jvm查找并加载指定的类,返回的是一个class对象的引用。
    29. Constructor cons[]=c.getConstructors();//获取权限为public的构造方法
    30. for(Constructor con:cons) {
    31. System.out.print(Modifier.toString(con.getModifiers())+" ");//获取修饰符的信息
    32. System.out.print(con.getName()+"(");//获得方法名
    33. Class paras[] = con.getParameterTypes();//参数的类型
    34. for(int i=0;i
    35. System.out.print(paras[i].getSimpleName()+" args ");//获得参数类型的简化名字
    36. if(i!=paras.length-1) {
    37. System.out.print(",");
    38. }
    39. }
    40. System.out.println("){}");
    41. }
    42. } catch (ClassNotFoundException e) {
    43. e.printStackTrace();
    44. }
    45. }
    46. }

    【运行结果】

    注意:我们可以发现运行结果只输出了两种构造方法,这是为什么呢?

     从代码中我们可以看到有以下三种构造方法:

    之所以输出了两种是因为第三种构造方法是private是私密的, 而我们用的方法是c.getConstructors();获取的是公开的构造方法,要想获得所有的构造方法要用到方法c.getDeclaredConstructors()。改完后便可以得到三种构造方法:

    3.用Constructor创建并返回实列对象 

    创建一个类的对象需要使用到方法newInstance(Object...initargs)方法,下面详细介绍一下该方法:

    newInstance() 方法是 Java 反射 API 中 java.lang.reflect.Constructor 类的一个方法。它用于创建并返回类的实例对象。通过 newInstance() 方法,可以通过不知道类名的情况下,根据类的构造函数创建实例对象。

    newInstance() 方法有以下几种重载形式:

    1. T newInstance():如果类有一个可访问的无参数构造函数,那么将使用该构造函数创建类的实例。如果没有可访问的无参数构造函数,则会抛出 InstantiationException 异常。
      1. Class<?> cls = MyClass.class;
      2. Object obj = cls.newInstance();

    2. T newInstance(Object... initargs):如果类有一个匹配的构造函数,即参数类型与提供的参数相匹配,那么将使用该构造函数创建类的实例。如果没有匹配的构造函数,则会抛出 InstantiationException 异常。
      1. Class<?> cls = MyClass.class;
      2. Constructor<?> constructor = cls.getDeclaredConstructor(String.class, int.class);
      3. Object obj = constructor.newInstance("example", 10);

      通过 newInstance() 方法,我们可以动态地创建对象实例,而不需要直接调用类的构造函数。这在某些情况下非常有用,例如需要在运行时根据用户输入的类名创建对象等。需要注意的是,使用 newInstance() 方法时,类必须具有可访问的构造函数,并且参数类型必须与提供的参数匹配,否则会抛出异常。

    还是以上面的代码为例来创建实列对象:

    【代码】

    1. public class Example {
    2. int id;
    3. String name;
    4. double price;
    5. public Example() {//无参的构造方法
    6. super();
    7. }
    8. public Example(int id) {//一个参数的构造方法
    9. super();
    10. this.id = id;
    11. }
    12. private Example(int id, String name, double price) {//三个参数的私有构造方法
    13. super();
    14. this.id = id;
    15. this.name = name;
    16. this.price = price;
    17. }
    18. @Override
    19. public String toString() {
    20. return "Example [id=" + id + ", name=" + name + ", price=" + price + "]";
    21. }
    22. }
    23. //先创建了一个Example类
    24. import java.lang.reflect.Constructor;
    25. import java.lang.reflect.InvocationTargetException;
    26. import java.lang.reflect.Modifier;
    27. public class Demo {
    28. public static void main(String[] args) {
    29. try {
    30. Class c = Class.forName("Example");//传入string类型参数”Example",要求jvm查找并加载指定的类,返回的是一个class对象的引用。
    31. Constructor cons[]=c.getDeclaredConstructors();//获取权限为public的构造方法
    32. for(Constructor con:cons) {
    33. System.out.print(Modifier.toString(con.getModifiers())+" ");//获取修饰符的信息
    34. System.out.print(con.getName()+"(");//获得方法名
    35. Class paras[] = con.getParameterTypes();//参数的类型
    36. for(int i=0;i
    37. System.out.print(paras[i].getSimpleName()+" args ");
    38. if(i!=paras.length-1) {
    39. System.out.print(",");
    40. }
    41. }
    42. System.out.println("){}");
    43. }
    44. //创建实列对象
    45. System.out.println("无参的实列对象:");
    46. Constructor cs1 = c.getDeclaredConstructor();//无参数的构造方法
    47. Object obj = cs1.newInstance();
    48. System.out.println(obj.toString());
    49. System.out.println("一个参的实列对象:");
    50. Constructor cs2 = c.getDeclaredConstructor(int.class);//一个参数的构造方法
    51. obj = cs2.newInstance(1024);
    52. System.out.println(obj.toString());
    53. System.out.println("三个参的实列对象:");
    54. Constructor cs3 = c.getDeclaredConstructor(int.class,String.class,double.class);//三个参数的构造方法
    55. obj = cs3.newInstance(123,"牛马",3.14);
    56. System.out.println(obj.toString());
    57. } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
    58. e.printStackTrace();
    59. }
    60. }
    61. }

    【运行结果】

     注意:我们可以发现创建三个参的实列对象时,出现了报错!

    这是因为第三个构造方法是私密的这时我们就要用到前面Constructor中所提到的设置可访问性:

    // 设置构造函数为可访问(用于访问非公共构造函数)
    constructor.setAccessible(true);

    添加位置如下:

    运行结果:

    2.访问成员变量

    在通过下列一组方法访问成员变量时,将返回Field类型的对象或数组。每个 Field对象代表一个成员变量,利用Field对象可以操纵相应的成员变量:
    😶‍🌫️getFields()
    😶‍🌫️getField(String name)
    😶‍🌫️getDeclaredFields()
    😶‍🌫️getDeclaredField(String name)
    如果是访问指定的成员变量,可以通过该成员变量的名称来访问。例如,访问一个名称为birthday的成员变量,访问方法如下:

    object. getDeclaredField("birthday");

     

    1Field的使用方法

    在Java中,反射API提供了java.lang.reflect.Field类来操作和访问类的字段。下面是Field类的一些常用方法的使用示例 :

    1. 获取字段对象:
      1. // 获取指定类的公共字段
      2. Field[] fields = SomeClass.class.getFields();
      3. // 获取指定类的所有字段(包括非公共字段)
      4. Field[] fields = SomeClass.class.getDeclaredFields();
      5. // 获取单个指定字段
      6. Field field = SomeClass.class.getDeclaredField("fieldName");

      以上代码中,我们使用getFields()方法获取指定类的公共字段,使用getDeclaredFields()方法获取指定类的所有字段(包括私有字段),使用getDeclaredField("fieldName")方法获取指定类中名为"fieldName"的字段。

    2. 获取和设置字段值:
      1. Object obj = new SomeClass();
      2. field.set(obj, value); // 设置字段值
      3. Object fieldValue = field.get(obj); // 获取字段值
    3. 获取字段的信息:
      1. String fieldName = field.getName(); // 获取字段名字
      2. Class<?> fieldType = field.getType(); // 获取字段类型
      3. int modifiers = field.getModifiers(); // 获取字段修饰符

      可以使用field.getName()方法获取字段的名称,使用field.getType()方法获取字段的类型,使用field.getModifiers()方法获取字段的修饰符信息,比如publicprivate等。

      通过使用Field类,我们可以在运行时动态地操作和访问类的字段,这对于需要在运行时通过反射来操作字段的值、类型和访问修饰符的情况非常有用。请注意,在使用反射时要小心处理异常,同时需要注意反射操作可能带来的性能开销。

    Field类的常用方法 

     2.反射一个类中的所有成员变量

    【代码实列】

    1. public class Example {
    2. public int id=-1;
    3. public String name="XXX";
    4. private double price=3.14;
    5. }
    6. //先创建一个Example类
    7. import java.lang.reflect.Constructor;
    8. import java.lang.reflect.Field;
    9. import java.lang.reflect.InvocationTargetException;
    10. import java.lang.reflect.Modifier;
    11. public class Demo {
    12. public static void main(String[] args) {
    13. try {
    14. Class c = Class.forName("Example");
    15. //Field[] fs = c.getFields();//返回所有公有属性
    16. Field[] fs = c.getDeclaredFields();//返回所有属性
    17. for(Field f:fs) {
    18. System.out.print(Modifier.toString(f.getModifiers())+" ");
    19. //f.getModifiers()获得成员变量采用的修饰符的整数,采用Modifier.toString()方法解析成为String类型
    20. System.out.print(f.getType().getSimpleName()+" ");//获得成员变量类型
    21. System.out.print(f.getName());
    22. System.out.println();
    23. }
    24. } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchFieldException e) {
    25. e.printStackTrace();
    26. }
    27. }
    28. }

     【运行结果】

    3.获取修改成员变量的值 

    我们来获取对应成员变量的属性:

    1.id

     2.name

    3.price

     这里报错是因为price是私密的没有权限我们要设置权限:

    运行结果:

    修改成员变量的属性:

    添加代码如下:


    运行结果:

    3.访问成员方法 

       获取成员方法我们要用到Menthod方法。在通过下列一组方法访问成员方法时,将返回Method类型的对象或数组。每个Method对象代表一个方法,利用Method对象可以操纵相应的方法:
    getMethods()
    getMethod(String name, Class...parameterTypes)
    getDeclaredMethods()
    getDeclaredMethod(String name, Class..parameterTypes)。
    如果是访问指定的方法,需要根据该方法的名称和入口参数的类型来访问。

     

     1.Method类方法

    在Java中,java.lang.reflect.Method类用于表示类的方法。通过Method类,我们可以获取、调用和操作类的方法。

    以下是Method类的一些常用方法的使用示例:

    1. 获取方法对象:
      1. // 获取指定类的公共方法(包括从父类继承的方法)
      2. Method[] methods = SomeClass.class.getMethods();
      3. // 获取指定类的所有方法(包括非公共方法)
      4. Method[] methods = SomeClass.class.getDeclaredMethods();
      5. // 获取单个指定方法
      6. Method method = SomeClass.class.getDeclaredMethod("methodName", parameterTypes);
    2. 调用方法:
      1. Object obj = new SomeClass();
      2. method.invoke(obj, args);
    3. 获取方法的信息:
      1. String methodName = method.getName(); // 获取方法名字
      2. Class<?> returnType = method.getReturnType(); // 获取方法返回类型
      3. Class<?>[] parameterTypes = method.getParameterTypes(); // 获取方法参数类型数组
      4. int modifiers = method.getModifiers(); // 获取方法的修饰符

      使用getName()方法获取方法的名称,使用getReturnType()方法获取方法的返回类型,使用getParameterTypes()方法获取方法的参数类型数组,使用getModifiers()方法获取方法的修饰符信息,比如publicprivate等。

    4. 通过Method类,我们可以在运行时动态地获取和调用类的方法,这对于需要在运行时通过反射来操作方法的返回值、参数和修饰符的情况非常有用。需要注意,在使用反射时要小心处理异常,同时需要注意反射操作可能带来的性能开销。

    2.反射一个类中所有的成员方法

    代码如下:
     

    1. import java.lang.reflect.Method;
    2. import java.lang.reflect.Modifier;
    3. public class Demo {
    4. public static void main(String[] args) {
    5. try {
    6. Class c = Class.forName("java.lang.String");
    7. Method ms[] = c.getDeclaredMethods();
    8. for(Method m: ms) {
    9. System.out.print(Modifier.toString(m.getModifiers())+" ");//获取修饰符
    10. System.out.print(m.getReturnType().getSimpleName()+" ");//返回值
    11. System.out.print(m.getName()+"(");//方法名字
    12. Class para[] = m.getParameterTypes();
    13. for(int i = 0;i<para.length;i++ ) {
    14. System.out.print(para[i].getSimpleName()+" arg ");
    15. if(i!=para.length-1) {
    16. System.out.print(",");
    17. }
    18. }
    19. System.out.println(")");
    20. }
    21. } catch (ClassNotFoundException e) {
    22. e.printStackTrace();
    23. }
    24. }
    25. }

    【运行结果】

    3.用反射调用方法 

    【代码】

    1. public class Example {
    2. public void add(int a,int b) {
    3. System.out.println("a+b="+(a+b));
    4. }
    5. }
    6. import java.lang.reflect.Constructor;
    7. import java.lang.reflect.InvocationTargetException;
    8. import java.lang.reflect.Method;
    9. import java.lang.reflect.Modifier;
    10. public class Demo {
    11. public static void main(String[] args) {
    12. try {
    13. Class c = Class.forName("Example");
    14. Method ms[] = c.getDeclaredMethods();
    15. for(Method m: ms) {
    16. System.out.print(Modifier.toString(m.getModifiers())+" ");//获取修饰符
    17. System.out.print(m.getReturnType().getSimpleName()+" ");//返回值
    18. System.out.print(m.getName()+"(");//方法名字
    19. Class para[] = m.getParameterTypes();
    20. for(int i = 0;i<para.length;i++ ) {
    21. System.out.print(para[i].getSimpleName()+" arg ");
    22. if(i!=para.length-1) {
    23. System.out.print(",");
    24. }
    25. }
    26. System.out.println(")");
    27. }
    28. Constructor cs = c.getConstructor();
    29. Object obj = cs.newInstance();
    30. Method m = c.getDeclaredMethod("add",int.class,int.class);
    31. m.invoke(obj,265,236);
    32. } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
    33. e.printStackTrace();
    34. }
    35. }
    36. }

    【运行结果】

    @over! 

  • 相关阅读:
    适合跑步的无线蓝牙耳机有哪些?跑步运动耳机推荐
    北理工嵩天Python语言程序设计笔记(2 Python基本语法元素)
    算法通关村-----归并排序
    实现数组的扁平化
    《痞子衡嵌入式半月刊》 第 31 期
    细胞分离研究丨Worthington梭菌蛋白酶方案
    CAN接口的PCB Layout规则要求汇总
    springbboot配置druid多数据源
    2021 华数杯全国大学生数学建模竞赛A题-电动汽车无线充电优化匹配研究(附带赛题解析&获奖论文及MATLAB代码)
    【单片机入门】(三)应用层软件开发的单片机学习之路-----UART串口通讯和c#交互
  • 原文地址:https://blog.csdn.net/2301_77599154/article/details/132767290