一道面试题:请问 Java 中的反射是什么?有什么效果和优势?怎么运用反射?

你们能够先测验答复答复

假如不会咱们就开端下面的文章

导言

什么是反射

Java反射(Reflection)是指在运转时获取目标信息(例如类名、办法、特点等),并且能够动态操作该目标,而无需事前知道该目标的静态类型。它答应程序在运转时查看目标和类,并且能够调用目标的办法、获取目标的特点和结构函数,甚至能够动态创立新的目标和数组。

反射的效果

Java反射的效果是使得程序能够在运转时动态地操作目标和类,而不需求事前知道这些信息,这种才能为程序设计带来了很大的灵敏性。运用反射能够完成许多高级功能,比如动态署理注解处理、JavaBean特点的自动设置等等。但是,由于反射操作具有较高的开销,因此在需求功能的场合应该谨慎运用。

背景常识

Java反射的根本原理是经过运用Java的反射API来获取类的信息。Java的反射API供给了一个Class类,它能够用来描绘类的信息,例如类名、结构函数、办法和字段等。经过运用Class类的办法,能够获取一个类的一切信息。

Java反射能够让程序在运转时动态地获取类的信息并进行操作,这使得程序的灵敏性更高。但是,由于反射会献身一定的功能,所以在功能要求较高的情况下,应该尽量防止运用反射。

讲解Java反射的中心概念和API

Java反射的中心概念首要涉及到以下几个类:ClassConstructorFieldMethod。以下是这些类的首要API及其用法:

  1. Class类

    • 效果:表明正在运转的Java应用程序中的类和接口。

    • 获取Class目标的办法:

      • Class.forName(String className):依据类名加载类并获取Class目标。
      • object.getClass():依据已知目标获取Class目标。
      • ClassName.class:直接获取类的Class目标。
  2. Constructor类

    • 效果:描绘类的结构办法。

    • 获取结构办法目标的办法:

      • Class.getConstructor(Class<?>... parameterTypes):获取类的public结构办法。
      • Class.getDeclaredConstructor(Class<?>... parameterTypes):获取类的一切结构办法,包含private的。
    • 创立目标:

      • Constructor.newInstance(Object... initargs):运用结构办法创立新的目标实例。
  3. Field类

    • 效果:描绘类的字段(成员变量)。

    • 获取字段目标的办法:

      • Class.getField(String name):获取类的public字段。
      • Class.getDeclaredField(String name):获取类的一切字段,包含private的。
    • 获取和设置字段值:

      • Field.get(Object obj):获取目标的字段值。
      • Field.set(Object obj, Object value):设置目标的字段值。
  4. Method类

    • 效果:描绘类的办法。

    • 获取办法目标的办法:

      • Class.getMethod(String name, Class<?>... parameterTypes):获取类的public办法。
      • Class.getDeclaredMethod(String name, Class<?>... parameterTypes):获取类的一切办法,包含private的。
    • 调用办法:

      • Method.invoke(Object obj, Object... args):调用办法并回来结果。

注意:在运用getDeclaredConstructorgetDeclaredFieldgetDeclaredMethod时,你或许需求调用setAccessible(true)办法来拜访private成员。

以上是Java反射中的中心API。在实际运用过程中,你或许还需求了解其他API,例如Class.getModifiers()Class.getSuperclass()等。但这些API不属于反射的中心功能,所以在此不详细罗列。

api详解

Class

Class 类是 Java 反射的中心类,它表明正在运转的 Java 应用程序中的类和接口。以下是 Class 类中的一些重要办法:

取得Class目标:

//        取得Class目标
//        1.经过类名.class
        Class<?> cls1 = Person.class;
//        2.经过Class.forName()
        Class<?> cls2 = Class.forName("Reflection.bean.Person");
//        3.经过目标.getClass()
        Person person = new Person();
        Class<?> cls3 = person.getClass();
//        4.经过类加载器
        Class<?> cls4 =Thread.currentThread().getContextClassLoader().loadClass("Reflection.bean.Person");
  1. 获取类的称号:

    • String getName():回来类的全限定名(包含包名)。
    • String getSimpleName():回来类的简略名(不包含包名)。
    • String getCanonicalName():回来类的规范名(类似于全限定名,但关于内部类和数组类有所不同)。
  2. 加载和获取类:

    • static Class<?> forName(String className):依据类名加载类并获取其 Class 目标。
    • static Class<?> forName(String name, boolean initialize, ClassLoader loader):依据类名、是否初始化以及类加载器加载类并获取其 Class 目标。
  3. 获取类的修饰符:

    • int getModifiers():回来类的修饰符,例如 public、private、abstract 等。

    Modifier这个类里有判别回来数字是什么类型的办法

  4. 获取类的父类和接口:

    • Class<?> getSuperclass():回来类的父类。
    • Class<?>[] getInterfaces():回来类完成的接口。
  5. 获取类的结构办法、字段和办法:

    • Constructor<?>[] getConstructors():回来类的 public 结构办法。
    • Constructor<?>[] getDeclaredConstructors():回来类的一切结构办法,包含 private 的。
    • Field[] getFields():回来类的 public 字段。
    • Field[] getDeclaredFields():回来类的一切字段,包含 private 的。
    • Method[] getMethods():回来类的 public 办法。
    • Method[] getDeclaredMethods():回来类的一切办法,包含 private 的。
  6. 获取类的注解:

    • Annotation[] getAnnotations():回来类的注解。
    • Annotation[] getDeclaredAnnotations():回来类的一切注解,包含继承自父类的。
    • T getAnnotation(Class<T> annotationClass):回来类的指定类型的注解。
  7. 创立类的实例:

    • T newInstance():运用默许结构办法创立类的实例。在 Java 9 之后被弃用,建议运用 Constructor.newInstance()
  8. 判别类的联系:

    • boolean isAssignableFrom(Class<?> cls):判别当时类是否为参数类的超类或接口。
    • boolean isInstance(Object obj):判别指定目标是否为当时类的实例。
    • boolean isInterface():判别当时类是否为接口。
    • boolean isPrimitive():判别当时类是否为根本类型。
    • boolean isArray():判别当时类是否为数组。

Constructor

Constructor 类是 Java 反射中的一个重要类,用于表明类的结构办法。以下是 Constructor 类中的一些重要办法:

  1. 获取一切的结构办法:运用 Class 类的 getConstructors() 办法获取类的一切公共结构办法,或运用 getDeclaredConstructors() 办法获取类的一切结构办法(包含私有、受维护和默许拜访权限的结构办法)。
    示例:
Class<?> clazz = SomeClass.class;
Constructor<?>[] constructors = clazz.getConstructors(); // 获取一切公共结构办法
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); // 获取一切结构办法(包含非公共结构办法)
  1. 获取特定的结构办法:运用 Class 类的 getConstructor() 办法获取类的特定公共结构办法,或运用 getDeclaredConstructor() 办法获取类的特定结构办法(包含私有、受维护和默许拜访权限的结构办法)。这些办法需求传入一个表明结构办法参数类型的 Class 目标数组。
Class<?> clazz = SomeClass.class;
try {
    Constructor<?> constructorWithNoArgs = clazz.getConstructor(); // 获取无参数的公共结构办法
    Constructor<?> constructorWithArgs = clazz.getConstructor(String.class, int.class); // 获取带有 String 和 int 参数的公共结构办法
    Constructor<?> declaredConstructorWithNoArgs = clazz.getDeclaredConstructor(); // 获取无参数的结构办法(包含非公共结构办法)
    Constructor<?> declaredConstructorWithArgs = clazz.getDeclaredConstructor(String.class, int.class); // 获取带有 String 和 int 参数的结构办法(包含非公共结构办法)
} catch (NoSuchMethodException e) {
    System.err.println("结构办法未找到: " + e.getMessage());
}
  1. 获取结构办法的称号和修饰符:

    • String getName():回来结构办法的称号,即类名。
    • int getModifiers():回来结构办法的修饰符,例如 public、private、protected 等。
  2. 获取结构办法的参数类型和反常类型:

    • Class<?>[] getParameterTypes():回来结构办法的参数类型。
    • Type[] getGenericParameterTypes():回来结构办法的泛型参数类型。
    • Class<?>[] getExceptionTypes():回来结构办法抛出的反常类型。
    • Type[] getGenericExceptionTypes():回来结构办法抛出的泛型反常类型。
  3. 获取和设置结构办法的拜访权限:

    • boolean isAccessible():回来结构办法的可拜访状况。
    • void setAccessible(boolean flag):设置结构办法的可拜访状况。假如要拜访 private 结构办法,需求将 flag 设置为 true
  4. 创立类的实例:

    • T newInstance(Object... initargs):运用结构办法创立类的实例。initargs 参数表明结构办法的参数值。
  5. 获取结构办法的注解:

    • Annotation[] getAnnotations():回来结构办法的注解。
    • Annotation[] getDeclaredAnnotations():回来结构办法的一切注解。
    • T getAnnotation(Class<T> annotationClass):回来结构办法的指定类型的注解。
    • Annotation[][] getParameterAnnotations():回来结构办法参数的注解。

Field

Field 类是 Java 反射中的一个重要类,用于表明类的字段(成员变量)。以下是 Field 类中的一些重要办法:

  1. 获取字段的称号和修饰符:

    • String getName():回来字段的称号。
    • int getModifiers():回来字段的修饰符,例如 public、private、protected、static 等。
  2. 获取字段的类型:

    • Class<?> getType():回来字段的类型。
    • Type getGenericType():回来字段的泛型类型。
  3. 获取和设置字段的值:

    • Object get(Object obj):回来指定目标上该字段的值。关于静态字段,obj 参数能够为 null
    • void set(Object obj, Object value):为指定目标上的该字段设置值。关于静态字段,obj 参数能够为 null
    • boolean getBoolean(Object obj)byte getByte(Object obj)char getChar(Object obj)double getDouble(Object obj)float getFloat(Object obj)int getInt(Object obj)long getLong(Object obj)short getShort(Object obj):分别回来指定目标上该字段的布尔值、字节值、字符值、双精度浮点值、单精度浮点值、整数值、长整数值和短整数值。关于静态字段,obj 参数能够为 null
    • void setBoolean(Object obj, boolean z)void setByte(Object obj, byte b)void setChar(Object obj, char c)void setDouble(Object obj, double d)void setFloat(Object obj, float f)void setInt(Object obj, int i)void setLong(Object obj, long l)void setShort(Object obj, short s):分别为指定目标上的该字段设置布尔值、字节值、字符值、双精度浮点值、单精度浮点值、整数值、长整数值和短整数值。关于静态字段,obj 参数能够为 null
  4. 获取和设置字段的拜访权限:

    • boolean isAccessible():回来字段的可拜访状况。
    • void setAccessible(boolean flag):设置字段的可拜访状况。假如要拜访 private 字段,需求将 flag 设置为 true
  5. 获取字段的注解:

    • Annotation[] getAnnotations():回来字段的注解。
    • Annotation[] getDeclaredAnnotations():回来字段的一切注解。
    • T getAnnotation(Class<T> annotationClass):回来字段的指定类型的注解。

Method

Method 类是 Java 反射中的一个重要类,用于表明类的办法。以下是 Method 类中的一些重要办法:

  1. 获取办法的称号和修饰符:

    • String getName():回来办法的称号。
    • int getModifiers():回来办法的修饰符,例如 public、private、protected、static 等。
  2. 获取办法的参数类型和回来类型:

    • Class<?>[] getParameterTypes():回来办法的参数类型。
    • Type[] getGenericParameterTypes():回来办法的泛型参数类型。
    • Class<?> getReturnType():回来办法的回来类型。
    • Type getGenericReturnType():回来办法的泛型回来类型。
  3. 获取办法的反常类型:

    • Class<?>[] getExceptionTypes():回来办法抛出的反常类型。
    • Type[] getGenericExceptionTypes():回来办法抛出的泛型反常类型。
  4. 调用办法:

    • Object invoke(Object obj, Object... args):调用指定目标上的该办法。obj 参数表明办法地点的目标,args 参数表明办法的参数值。关于静态办法,obj 参数能够为 null
  5. 获取和设置办法的拜访权限:

    • boolean isAccessible():回来办法的可拜访状况。
    • void setAccessible(boolean flag):设置办法的可拜访状况。假如要拜访 private 办法,需求将 flag 设置为 true
  6. 获取办法的注解:

    • Annotation[] getAnnotations():回来办法的注解。
    • Annotation[] getDeclaredAnnotations():回来办法的一切注解。
    • T getAnnotation(Class<T> annotationClass):回来办法的指定类型的注解。
    • Annotation[][] getParameterAnnotations():回来办法参数的注解。

创立实例

  1. 运用默许结构办法创立实例:
// 加载类
Class<?> cls = Class.forName("Reflection.bean.Person");
// 获取默许结构办法(无参结构办法)
Constructor<?> constructor = cls.getDeclaredConstructor();
// 设置结构办法的拜访权限,假如结构办法是私有的,则需求设置为 true
constructor.setAccessible(true);
// 创立实例
Object obj = constructor.newInstance();
  1. 运用带参数的结构办法创立实例:
// 加载类
Class<?> cls = Class.forName("Reflection.bean.Person");
// 获取带参数的结构办法
Constructor<?> constructor = cls.getDeclaredConstructor(String.class, int.class);
// 设置结构办法的拜访权限,假如结构办法是私有的,则需求设置为 true
constructor.setAccessible(true);
// 创立实例,传入结构办法的参数值
Object obj = constructor.newInstance("张三", 42);

Class 类:能够运用 Class 类的 newInstance 办法创立类的实例,但需求注意的是,这种办法仅适用于具有无参(默许)结构办法的类。在 Java 9 中,Class 类的 newInstance 办法已被弃用,引荐运用 Constructor 类的 newInstance 办法。

// 加载类
Class\<?> cls = Class.forName("Reflection.bean.Person");
// 运用无参结构办法创立实例
Object obj = cls.newInstance();

Class 类中的 newInstance 办法被弃用,原因是它存在一些问题和局限性。首要问题如下:

  1. 反常处理不明确:Class 类的 newInstance 办法只能抛出 InstantiationExceptionIllegalAccessException 反常。当调用结构办法时,假如结构办法本身抛出了反常,newInstance 会将该反常包装为 InvocationTargetException,然后将其设置为 InstantiationException 的原因(cause),这使得反常处理变得复杂和不直观。

  2. 仅支撑无参结构办法:Class 类的 newInstance 办法只能调用类的无参(默许)结构办法。这关于需求运用带参数结构办法的类来说是不够灵敏的。

为了解决这些问题,Java 引入了 Constructor 类的 newInstance 办法。它具有以下优势:

  1. 反常处理更清晰:Constructor 类的 newInstance 办法能够直接抛出 InvocationTargetException,使得反常处理更直接和简略。

  2. 支撑带参数结构办法:Constructor 类的 newInstance 办法能够处理带参数的结构办法,使其具有更好>的灵敏性。

  3. 支撑拜访操控:经过 Constructor 类的 setAccessible 办法,能够在必要时拜访私有结构办法。

反射的常见面试题

  1. 什么是 Java 反射?

    反射是 Java 言语供给的一种机制,答应在运转时查看、拜访和操作类、办法、字段和结构办法等元素。运用反射,能够在程序运转时动态加载类、创立目标、调用办法和拜访字段。

  2. Java 反射的首要用途是什么?

    Java 反射的首要用途包含:

    • 动态加载类和创立目标
    • 动态调用办法
    • 动态拜访和修正字段
    • 获取类的元数据(例如类名、办法、字段、注解等)
    • 完成动态署理
  3. 怎么运用反射获取类的实例?

    运用 Class.forName("类的全限定名") 办法动态加载类,然后运用 Constructor.newInstance() 办法创立类的实例。

  4. 怎么运用反射调用办法?

    运用 Class.getDeclaredMethod() 办法获取特定的办法,然后运用 Method.invoke() 办法调用该办法。

  5. 怎么运用反射拜访字段?

    运用 Class.getDeclaredField() 办法获取特定的字段,然后运用 Field.get()Field.set() 办法分别获取和设置字段的值。

  6. 反射的功能问题?

    反射操作相关于直接操作会有一定的功能丢失,因为反射涉及到运转时类型查看、办法调用等额定操作。在功能敏感的场景下应谨慎运用反射。但在许多场景中,反射带来的灵敏性和可扩展功能够抵消功能丢失。

  7. 什么是动态署理?怎么运用反射完成动态署理?

    动态署理是一种在运转时动态创立署理目标的技能,署理目标能够完成指定的接口。Java 反射供给了 java.lang.reflect.Proxy 类来完成动态署理。经过 Proxy.newProxyInstance() 办法能够创立署理目标,需求供给一个完成了 InvocationHandler 接口的目标来处理署理办法的调用。