Java进阶之反射

简介: 【7月更文挑战第14天】Java反射机制允许在运行时动态获取类信息、创建对象及调用其方法。它基于`Class`类,让我们能访问类的属性、方法、构造器。例如,通过`Class.forName()`加载类,`Class.newInstance()`创建对象,`Method.invoke()`执行方法。反射广泛应用于动态代理、单元测试、序列化及框架中,提供灵活性但牺牲了性能,且可破坏封装性。IDE的代码补全也是反射的应用之一。在使用时需谨慎,避免对私有成员的不当访问。

Java进阶之反射
我们知道Java在编译期的时候,代码中new对象时候,对象的类型就确定了下来,然后编译后的代码进行运行。而要在代码运行期的时候动态的去操作对象,就需要用Java的反射。
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。这种在代码运行中动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制。
那么我们今天来细细研究下到底什么是反射呢?首先先看名字,反射,有反肯定对应就有正。
正就是我们正常的Java代码开发过程:导入类所在的包、通过类中的构造方法实例化对象、再使用实例化的对象去调用类中的方法。
那反的过程就是通过实例化的对象,反向推算出该对象的类型。
正: 类---new--->对象
反: 对象-getClass()->类
这里的getClass()方法是在Object中存在,要知道万物皆是对象,Java中的所有类都是默认继承自Object的,所以Object是所有类的父类,所有类都有这个方法。

  Object类位于java.lang包中,这个包在编译Java程序时会自动导入。因此,Object类的方法可以被所有的Java类使用。
  Object类提供了一些基本的方法,包括:
  hashCode():返回对象的哈希码值,这个值通常用于哈希表中,比如 HashMap。
  equals(Object obj):用于比较两个对象是否相等。
  toString():返回对象的字符串表示形式。
  getClass():返回对象的运行时类。
  clone():创建并返回对象的一个副本。
  finalize():当垃圾收集器确定不再有对该对象的引用时,由垃圾收集器在对象上调用此方法。
  这些方法可以被其他类覆盖,以提供特定的实现。

  所以这里我们就可以通过getClass()来反射获取到对象的运行时类。
  反射所有的技术支持都来源于Class类
  Java反射机制可以让我们在代码运行时动态地获取类的信息,创建对象,调用方法,访问和修改字段等。也就是说不需要new关键字,也能实例化类对象!
  反射的原理
  Java反射机制的实现主要依赖于Java类库中的一组类和接口,这些类和接口位于java.lang.reflect包中:
  Class类:反射的核心类,可以获取对象的属性、方法等信息。
  Field类:表示类的成员变量,可以用来获取和设置类之中的字段信息。
  Method类:代表类的方法,可以用来获取类中的方法信息或者执行类和对象的方法。
  Constructor类:代表类的构造方法。
  当我们编写并编译一个Java程序时,Java编译器会将.java文件转换成.class文件,这些.class文件包含了类的所有信息,如字段、方法、构造函数等。
  在运行时,JVM会加载这些.class文件,并通过反射机制将这些信息暴露给我们。
  Java反射机制主要提供以下功能:
  获得Class对象
  方法有三种:
  1.使用Class类的forName静态方法:
  Class<?> clazz = Class.forName("java.lang.String");
  Object obj = clazz.newInstance();
  2.直接获取某一个对象的 class
  Class<?> clazz = String.class;
  Object obj = clazz.newInstance();
  3.调用某个对象的getClass()方法,比如:
  StringBuilder str = new StringBuilder("123");
  Class<?> clazz = str.getClass();
  Object obj = clazz.newInstance();
  在运行时判断任意一个对象所属的类。
  Object obj = new String("Hello, World!");
  Class<?> clazz = obj.getClass();
  System.out.println("输出类名: " + clazz.getName());
  // 使用isInstance()方法判断obj是否是String类的实例
  boolean isInstanceOfString = clazz.isInstance(obj);
  在运行时构造任意一个类的对象。
  创建对象有​两种方法:
  1.使用Class对象的newInstance()方法
  Class<?> clazz = Class.forName("java.lang.String");
  Object obj = clazz.newInstance();
  2.先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来构造
  //获取String所对应的Class对象
  Class<?> c = String.class;
  //获取String类带一个String参数的构造器
  Constructor constructor = c.getConstructor(String.class);
  //根据构造器创建实例
  Object obj = constructor.newInstance("创建对象");
  System.out.println(obj);
  在运行时判断任意一个类所具有的成员变量和方法。
  Class<?> clazz = Class.forName("java.lang.String");
  Field[] fields = clazz.getDeclaredFields();
  Method[] methods = clazz.getDeclaredMethods();
  System.out.println("Fields:");
  for (Field field : fields) {
      System.out.println(field);
  }
  System.out.println("Methods:");
  for (Method method : methods) {
      System.out.println(method);
  }
  在运行时获取泛型信息。
  Class<?> clazz = Class.forName("java.util.ArrayList");
  Type genericType = clazz.getGenericSuperclass();
  if (genericType instanceof ParameterizedType) {
      Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
      System.out.println("The actual type argument is: " + actualTypeArguments[0]);
  }
  在运行时调用任意一个对象的成员变量和方法。
  Object obj = new String("Hello, World!");
  Class<?> clazz = obj.getClass();
  // 调用方法
  Method method = clazz.getDeclaredMethod("toUpperCase");
  String result = (String) method.invoke(obj);
  System.out.println("Uppercase: " + result);
  // 访问字段
  Field field = clazz.getDeclaredField("value");
  field.setAccessible(true);
  char[] value = (char[]) field.get(obj);
  System.out.println("Value: " + new String(value));
  在运行时处理注解。
  Class<?> clazz = Class.forName("MyClass");
  Annotation[] annotations = clazz.getAnnotations();
  for (Annotation annotation : annotations) {
      if (annotation instanceof MyCustomAnnotation) {
          MyCustomAnnotation myAnnotation = (MyCustomAnnotation) annotation;
          System.out.println("Name: " + myAnnotation.name());
          System.out.println("Value: " + myAnnotation.value());
      }
  }
  生成动态代理。
  InvocationHandler handler = new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          // 在这里添加预处理和后处理逻辑
          System.out.println("Method called: " + method.getName());
          return method.invoke(target, args);
      }
  };
  MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
      MyInterface.class.getClassLoader(),
      new Class<?>[]{MyInterface.class},
      handler
  );
  proxy.myMethod();
  上面示例创建一个实现了MyInterface接口的代理对象,当调用myMethod方法时,会执行InvocationHandler中的逻辑。
  单元测试
  在单元测试中,通过反射模拟外部依赖的行为,从而实现单元测试。
  // 使用反射模拟外部依赖的方法
  Class<?> clazz = Class.forName("MyClass");
  Method method = clazz.getDeclaredMethod("externalMethod");
  method.setAccessible(true);
  method.invoke(obj);

  序列化和反序列化
  反射也可以访问和修改对象的私有字段,比如在序列化和反序列化过程中。
  ObjectInputStream in = new ObjectInputStream(new FileInputStream("obj.ser"));
  Class<?> clazz = in.readObject().getClass();
  Field field = clazz.getDeclaredField("name");
  field.setAccessible(true);
  Object obj = in.readObject();
  String name = (String) field.get(obj);
  如果全类名或者方法错误,可能出现ClassNotFoundException、NoSuchMethodException等异常,注意捕捉处理。
  私有字段和方法尽量不访问和修改。而且反射会影响性能。
  通过反射,我们可以突破Java语言的封装性,实现一些常规编程无法实现的功能。但是还是优先使用常规编程,最终不行再考虑反射!

  日常开发中,ide软件通过.就可以提示出可调用的方法,这也是反射的应用。
  以后web框架中的通过配置文件去定义Java bean,根据不同的配置文件就可以构建出不同的对象,调用对应的方法等等,都是反射的应用。
  还有其他一些运行时动态加载需要加载的对象,都是反射实现的。

  END
目录
相关文章
|
1天前
|
Java 数据库连接 Spring
反射-----浅解析(Java)
在java中,我们可以通过反射机制,知道任何一个类的成员变量(成员属性)和成员方法,也可以堆任何一个对象,调用这个对象的任何属性和方法,更进一步我们还可以修改部分信息和。
|
1月前
|
监控 Java
Java基础——反射
本文介绍了Java反射机制的基本概念和使用方法,包括`Class`类的使用、动态加载类、获取方法和成员变量信息、方法反射操作、以及通过反射了解集合泛型的本质。同时,文章还探讨了动态代理的概念及其应用,通过实例展示了如何利用动态代理实现面向切面编程(AOP),例如为方法执行添加性能监控。
|
1月前
|
Java
Java的反射
Java的反射。
30 2
|
2月前
|
存储 Java
[Java]反射
本文详细介绍了Java反射机制的基本概念、使用方法及其注意事项。首先解释了反射的定义和类加载过程,接着通过具体示例展示了如何使用反射获取和操作类的构造方法、方法和变量。文章还讨论了反射在类加载、内部类、父类成员访问等方面的特殊行为,并提供了通过反射跳过泛型检查的示例。最后,简要介绍了字面量和符号引用的概念。全文旨在帮助读者深入理解反射机制及其应用场景。
38 0
[Java]反射
|
3月前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
78 9
Java——反射&枚举
|
2月前
|
安全 Java 测试技术
🌟Java零基础-反射:从入门到精通
【10月更文挑战第4天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
31 2
|
3月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
|
2月前
|
IDE Java 编译器
java的反射与注解
java的反射与注解
24 0
|
3月前
|
存储 安全 Java
扫盲java基础-反射(一)
扫盲java基础-反射(一)
|
3月前
|
Java
扫盲java基础-反射(二)
扫盲java基础-反射(二)