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
目录
相关文章
|
27天前
|
安全 Java 索引
Java——反射&枚举
本文介绍了Java反射机制及其应用,包括获取Class对象、构造方法、成员变量和成员方法。反射允许在运行时动态操作类和对象,例如创建对象、调用方法和访问字段。文章详细解释了不同方法的使用方式及其注意事项,并展示了如何通过反射获取类的各种信息。此外,还介绍了枚举类型的特点和使用方法,包括枚举的构造方法及其在反射中的特殊处理。
47 9
Java——反射&枚举
|
1月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
1月前
|
Java 程序员 编译器
Java的反射技术reflect
Java的反射技术允许程序在运行时动态加载和操作类,基于字节码文件构建中间语言代码,进而生成机器码在JVM上执行,实现了“一次编译,到处运行”。此技术虽需更多运行时间,但广泛应用于Spring框架的持续集成、动态配置及三大特性(IOC、DI、AOP)中,支持企业级应用的迭代升级和灵活配置管理,适用于集群部署与数据同步场景。
|
23天前
|
存储 安全 Java
扫盲java基础-反射(一)
扫盲java基础-反射(一)
|
23天前
|
Java
扫盲java基础-反射(二)
扫盲java基础-反射(二)
|
3月前
|
安全 Java 测试技术
day26:Java零基础 - 反射
【7月更文挑战第26天】🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
32 5
|
3月前
|
Java Perl
Java进阶之正则表达式
【7月更文挑战第17天】正则表达式(RegEx)是一种模式匹配工具,用于在字符串中执行搜索、替换等操作。它由普通字符和特殊元字符组成,后者定义匹配规则。
26 4
|
2月前
|
缓存 安全 Java
【Java 第十篇章】反射
Java 反射技术让程序能在运行时动态获取类信息并操作对象,极大提升了灵活性与扩展性。本文将介绍反射的基本概念、原理及应用,包括如何使用 `Class`、`Field`、`Method` 和 `Constructor` 类进行动态操作。此外,还将探讨反射在动态加载、框架开发与代码测试中的应用场景,并提醒开发者注意性能与安全方面的问题,帮助你更合理地运用这一强大工具。
24 0
|
3月前
|
设计模式 Java
Java进阶之代理
Java进阶之代理
23 4
|
3月前
|
设计模式 Java
Java进阶之代理
Java进阶之代理
25 3