反射的作用与使用

引言: 在Java编程中,反射机制是一种强大的工具,它允许程序在运行时动态地获取和操作类的信息。通过反射,我们可以在运行时检查类、调用方法、访问属性,甚至可以在没有源代码的情况下创建并操作对象。本文将详细解释Java中的反射的用途以及使用步骤。

1.反射的用途:

  1. 动态加载类:反射允许程序在运行时动态加载需要使用的类,而不需要在编译时就确定类的名称。这对于开发框架和插件系统非常有用,可以根据配置文件或用户输入加载相应的类。

  2. 运行时检查类的结构:通过反射,我们可以在运行时检查类的成员变量、方法和构造函数等信息。这样可以避免硬编码类的结构,使代码更加灵活和可扩展。

  3. 调用方法和访问属性:反射机制可以使我们在运行时调用方法和访问属性,即使它们是私有的。这对于某些特定场景,如单元测试、动态代理等非常有用。

  4. 创建对象:通过反射,我们可以在运行时创建对象,即使我们没有类的源代码。这在某些情况下很有用,例如根据配置文件创建不同的对象实例。

2. 反射相关的类以及方法 

类名用途
Class类代表类的实体,在运行的Java应用程序中表示类和接口
Field类代表类的成员变量/类的属性
Method类代表类的方法
Constructor类代表类的构造方法
方法用途
getClassLoader()获得类的加载器
getDeclaredClasses()返回一个数组,数组中包含该类中所有类和接口类的对象(包括私有的)
forName(String className)根据类名返回类的对象
newInstance()创建类的实例
getName()获得类的完整路径名字
方法用途
getField(String name)获得某个公有的属性对象
getFields()获得所有公有的属性对象
getDeclaredField(String name)获得某个属性对象
getDeclaredFields()获得所有属性对象
方法用途
getAnnotation(Class annotationClass)返回该类中与参数类型匹配的公有注解对象
getAnnotations()返回该类所有的公有注解对象
getDeclaredAnnotation(Class annotationClass)返回该类中与参数类型匹配的所有注解对象
getDeclaredAnnotations()返回该类所有的注解对象
方法用途
getConstructor(Class...<?> parameterTypes)获得该类中与参数类型匹配的公有构造方法
getConstructors()获得该类的所有公有构造方法
getDeclaredConstructor(Class...<?> parameterTypes)获得该类中与参数类型匹配的构造方法
getDeclaredConstructors()获得该类所有构造方法
方法用途
getMethod(String name, Class...<?> parameterTypes)获得该类某个公有的方法
getMethods()获得该类所有公有的方法
getDeclaredMethod(String name, Class...<?> parameterTypes)获得该类某个方法
getDeclaredMethods()获得该类所有方法

3.使用步骤:

  1. 获取Class对象:要使用反射,首先需要获取对应类的Class对象。可以通过以下方式获取Class对象:

    • 对象.getClass()方法:通过已有对象获取Class对象。
    • 类名.class属性:直接使用类的class属性获取Class对象。
    • Class.forName()方法:通过类的全限定名获取Class对象。
      public class Test {
          public static void main(String[] args) {
              Class<?> c1;
              try {
                  // 使用Class.forName方法获取类的Class对象
                  //该方法可能会抛出ClassNotFoundException异常。
                  c1 = Class.forName("demo1.Student");
              } catch (ClassNotFoundException e) {
                  throw new RuntimeException(e);
              }
              
              // 使用类名.class获取类的Class对象
              Class<?> c2 = Student.class;
              
              // 使用实例对象的getClass方法获取类的Class对象
              Student s = new Student();
              Class<?> c3 = s.getClass();
      
              //class对象只能存在一个!!!
              
              // 判断c1和c2是否是同一个对象
              System.out.println(c1 == c2); // 输出:true
              
              // 判断c1和c3是否是同一个对象
              System.out.println(c1 == c3); // 输出:true
          }
      }
      
      class Student {
          // 私有属性name
          private String name = "bit";
          // 公共属性age
          public int age = 18;
          
          // 不带参数的公共构造方法
          public Student() {
              System.out.println("这是一个构造方法");
          }
          
          // 私有构造方法
          private Student(String name, int age) {
              this.name = name;
              this.age = age;
              System.out.println("Student(String,name)");
          }
          
          // 私有方法
          private void eat() {
              System.out.println("i am eating");
          }
          
          // 公共方法
          public void sleep() {
              System.out.println("i am pig");
          }
          
          // 私有方法
          private void function(String str) {
              System.out.println(str);
          }
          
          // 重写toString方法
          @Override
          public String toString() {
              return "Student{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      '}';
          }
      }
      
  2. 获取类的成员:通过Class对象,可以获取类的构造函数、方法和字段等成员信息。可以使用以下方法获取类的成员:

    • getConstructors()、getDeclaredConstructors():获取类的构造函数。
    • getMethods()、getDeclaredMethods():获取类的方法。
    • getFields()、getDeclaredFields():获取类的字段。
  3. 调用方法和访问属性:通过成员对象,可以调用方法和访问属性。如果成员是私有的,需要设置setAccessible(true)来取消访问限制。

  4. 创建对象:通过Class对象,可以使用newInstance()方法创建对象。如果类有参数化的构造函数,可以使用getConstructor()获取特定的构造函数,然后使用newInstance()方法创建对象。

  5. 性能考虑:虽然反射机制很灵活,但它相对于直接调用方法或访问属性来说效率较低,因为它需要在运行时进行动态解析。因此,在性能要求较高的场景中,应尽量避免过多地使用反射。

3.1 反射示例 

 1.使用反射创建对象
public static void main(String[] args) {
    Class<?> c1;
    try {
        // 使用Class.forName方法获取类的Class对象
        c1 = Class.forName("demo1.Student");
        
        // 使用Class对象的newInstance方法创建类的实例
        Student s = (Student) c1.newInstance();
        
        // 打印实例的字符串表示形式
        System.out.println(s);
    } catch (ClassNotFoundException e) {
        // 如果指定的类名不存在或无法加载,抛出ClassNotFoundException异常
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        // 如果无法实例化该类,抛出InstantiationException异常
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        // 如果无法访问该类的构造方法,抛出IllegalAccessException异常
        throw new RuntimeException(e);
    }
}
 2. 获得带参数的构造方法

如果类没有不带参数的构造方法,就无法使用Class对象的newInstance方法来创建该类的实例对象。因为在调用newInstance方法时,会自动调用类的不带参数的构造方法来创建对象。如果该类没有不带参数的构造方法,则会抛出InstantiationException异常

public static void main(String[] args) {
    Class<?> c1;
    try {
        // 使用Class.forName方法获取类的Class对象
        c1 = Class.forName("demo1.Student");
        
        // 获取声明的带参数的构造方法
        Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class);
        
        // 设置构造方法可访问(即使是私有构造方法)
        constructor.setAccessible(true);
        
        // 使用构造方法创建类的实例
        Student s = (Student) constructor.newInstance("zhang", 19);
        
        // 打印实例的字符串表示形式
        System.out.println(s);
    } catch (ClassNotFoundException e) {
        // 如果指定的类名不存在或无法加载,抛出ClassNotFoundException异常
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        // 如果无法实例化该类,抛出InstantiationException异常
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        // 如果无法访问该类的构造方法,抛出IllegalAccessException异常
        throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
        // 如果指定的构造方法不存在,抛出NoSuchMethodException异常
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        // 如果调用构造方法时发生错误,抛出InvocationTargetException异常
        throw new RuntimeException(e);
    }
}
 3. 获取属性
public static void main(String[] args) {
    Class<?> c1;
    try {
        // 使用Class.forName方法获取类的Class对象
        c1 = Class.forName("demo1.Student");
        
        // 获取声明的私有属性
        Field field = c1.getDeclaredField("name");
        
        // 使用默认构造函数创建类的实例
        Student s = (Student) c1.newInstance();
        
        // 设置属性可访问(即使是私有属性)
        field.setAccessible(true);
        
        // 设置属性值
        field.set(s, "Ting");
        
        // 打印实例的字符串表示形式
        System.out.println(s);
    } catch (ClassNotFoundException e) {
        // 如果指定的类名不存在或无法加载,抛出ClassNotFoundException异常
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        // 如果无法实例化该类,抛出InstantiationException异常
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        // 如果无法访问该属性,抛出IllegalAccessException异常
        throw new RuntimeException(e);
    } catch (NoSuchFieldException e) {
        // 如果指定的属性不存在,抛出NoSuchFieldException异常
        throw new RuntimeException(e);
    }
}
4. 获得方法
public static void main4(String[] args) {
    Class<?> c1;
    try {
        // 使用Class.forName方法获取类的Class对象
        c1 = Class.forName("demo1.Student");
        
        // 获取声明的私有方法
        Method m = c1.getDeclaredMethod("function", String.class);
        
        // 使用默认构造函数创建类的实例
        Student s = (Student) c1.newInstance();
        
        // 设置方法可访问(即使是私有方法)
        m.setAccessible(true);
        
        // 调用方法,并传入参数
        m.invoke(s, "这是一个反射的方法");
        
        // 打印实例的字符串表示形式
        System.out.println(s);
    } catch (ClassNotFoundException e) {
        // 如果指定的类名不存在或无法加载,抛出ClassNotFoundException异常
        throw new RuntimeException(e);
    } catch (InstantiationException e) {
        // 如果无法实例化该类,抛出InstantiationException异常
        throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
        // 如果无法访问该方法,抛出IllegalAccessException异常
        throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
        // 如果指定的方法不存在,抛出NoSuchMethodException异常
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        // 如果调用方法时发生异常,抛出InvocationTargetException异常
        throw new RuntimeException(e);
    }
}

 

结论: 反射机制是Java编程中非常有用的工具,它允许程序在运行时动态地获取和操作类的信息。通过反射,我们可以实现动态加载类、运行时检查类的结构、调用方法和访问属性,甚至可以在没有源代码的情况下创建并操作对象。但是在使用反射时,需要注意性能问题,并谨慎处理访问权限。熟练掌握反射机制,将有助于提升代码的灵活性和可扩展性

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ting-yu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值