网站首页 > java教程 正文
开篇之前先来一道面试题:请问 Java 中的反射是什么?有什么作用和优势?如何使用反射?
你们可以先尝试回答回答
如果不会我们就开始下面的文章
引言
什么是反射
Java反射(Reflection)是指在运行时获取对象信息(例如类名、方法、属性等),并且可以动态操作该对象,而无需事先知道该对象的静态类型。它允许程序在运行时检查对象和类,并且可以调用对象的方法、获取对象的属性和构造函数,甚至可以动态创建新的对象和数组。
反射的作用
Java反射的作用是使得程序能够在运行时动态地操作对象和类,而不需要事先知道这些信息,这种能力为程序设计带来了很大的灵活性。使用反射可以实现很多高级功能,比如动态代理、注解处理、JavaBean属性的自动设置等等。但是,由于反射操作具有较高的开销,因此在需要性能的场合应该谨慎使用。
背景知识
Java反射的基本原理是通过使用Java的反射API来获取类的信息。Java的反射API提供了一个Class类,它可以用来描述类的信息,例如类名、构造函数、方法和字段等。通过使用Class类的方法,可以获取一个类的所有信息。
Java反射可以让程序在运行时动态地获取类的信息并进行操作,这使得程序的灵活性更高。但是,由于反射会牺牲一定的性能,所以在性能要求较高的情况下,应该尽量避免使用反射。
讲解Java反射的核心概念和API
Java反射的核心概念主要涉及到以下几个类:Class、Constructor、Field 和 Method。以下是这些类的主要API及其用法:
- Class类
- 作用:表示正在运行的Java应用程序中的类和接口。
- 获取Class对象的方法:
- Class.forName(String className):根据类名加载类并获取Class对象。
- object.getClass():根据已知对象获取Class对象。
- ClassName.class:直接获取类的Class对象。
- Constructor类
- 作用:描述类的构造方法。
- 获取构造方法对象的方法:
- Class.getConstructor(Class<?>... parameterTypes):获取类的public构造方法。
- Class.getDeclaredConstructor(Class<?>... parameterTypes):获取类的所有构造方法,包括private的。
- 创建对象:
- Constructor.newInstance(Object... initargs):使用构造方法创建新的对象实例。
- Field类
- 作用:描述类的字段(成员变量)。
- 获取字段对象的方法:
- Class.getField(String name):获取类的public字段。
- Class.getDeclaredField(String name):获取类的所有字段,包括private的。
- 获取和设置字段值:
- Field.get(Object obj):获取对象的字段值。
- Field.set(Object obj, Object value):设置对象的字段值。
- Method类
- 作用:描述类的方法。
- 获取方法对象的方法:
- Class.getMethod(String name, Class<?>... parameterTypes):获取类的public方法。
- Class.getDeclaredMethod(String name, Class<?>... parameterTypes):获取类的所有方法,包括private的。
- 调用方法:
- Method.invoke(Object obj, Object... args):调用方法并返回结果。
注意:在使用getDeclaredConstructor、getDeclaredField和getDeclaredMethod时,你可能需要调用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");
复制代码
- 获取类的名称:
- String getName():返回类的全限定名(包含包名)。
- String getSimpleName():返回类的简单名(不包含包名)。
- String getCanonicalName():返回类的规范名(类似于全限定名,但对于内部类和数组类有所不同)。
- 加载和获取类:
- static Class<?> forName(String className):根据类名加载类并获取其 Class 对象。
- static Class<?> forName(String name, boolean initialize, ClassLoader loader):根据类名、是否初始化以及类加载器加载类并获取其 Class 对象。
- 获取类的修饰符:
- int getModifiers():返回类的修饰符,例如 public、private、abstract 等。
- Modifier这个类里有判断返回数字是什么类型的方法
- 获取类的父类和接口:
- Class<?> getSuperclass():返回类的父类。
- Class<?>[] getInterfaces():返回类实现的接口。
- 获取类的构造方法、字段和方法:
- Constructor<?>[] getConstructors():返回类的 public 构造方法。
- Constructor<?>[] getDeclaredConstructors():返回类的所有构造方法,包括 private 的。
- Field[] getFields():返回类的 public 字段。
- Field[] getDeclaredFields():返回类的所有字段,包括 private 的。
- Method[] getMethods():返回类的 public 方法。
- Method[] getDeclaredMethods():返回类的所有方法,包括 private 的。
- 获取类的注解:
- Annotation[] getAnnotations():返回类的注解。
- Annotation[] getDeclaredAnnotations():返回类的所有注解,包括继承自父类的。
- T getAnnotation(Class<T> annotationClass):返回类的指定类型的注解。
- 创建类的实例:
- T newInstance():使用默认构造方法创建类的实例。在 Java 9 之后被弃用,建议使用 Constructor.newInstance()。
- 判断类的关系:
- boolean isAssignableFrom(Class<?> cls):判断当前类是否为参数类的超类或接口。
- boolean isInstance(Object obj):判断指定对象是否为当前类的实例。
- boolean isInterface():判断当前类是否为接口。
- boolean isPrimitive():判断当前类是否为基本类型。
- boolean isArray():判断当前类是否为数组。
Constructor
Constructor 类是 Java 反射中的一个重要类,用于表示类的构造方法。以下是 Constructor 类中的一些重要方法:
- 获取所有的构造方法:使用 Class 类的 getConstructors() 方法获取类的所有公共构造方法,或使用 getDeclaredConstructors() 方法获取类的所有构造方法(包括私有、受保护和默认访问权限的构造方法)。 示例:
Class<?> clazz = SomeClass.class;
Constructor<?>[] constructors = clazz.getConstructors(); // 获取所有公共构造方法
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); // 获取所有构造方法(包括非公共构造方法)
复制代码
- 获取特定的构造方法:使用 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());
}
复制代码
- 获取构造方法的名称和修饰符:
- String getName():返回构造方法的名称,即类名。
- int getModifiers():返回构造方法的修饰符,例如 public、private、protected 等。
- 获取构造方法的参数类型和异常类型:
- Class<?>[] getParameterTypes():返回构造方法的参数类型。
- Type[] getGenericParameterTypes():返回构造方法的泛型参数类型。
- Class<?>[] getExceptionTypes():返回构造方法抛出的异常类型。
- Type[] getGenericExceptionTypes():返回构造方法抛出的泛型异常类型。
- 获取和设置构造方法的访问权限:
- boolean isAccessible():返回构造方法的可访问状态。
- void setAccessible(boolean flag):设置构造方法的可访问状态。如果要访问 private 构造方法,需要将 flag 设置为 true。
- 创建类的实例:
- T newInstance(Object... initargs):使用构造方法创建类的实例。initargs 参数表示构造方法的参数值。
- 获取构造方法的注解:
- Annotation[] getAnnotations():返回构造方法的注解。
- Annotation[] getDeclaredAnnotations():返回构造方法的所有注解。
- T getAnnotation(Class<T> annotationClass):返回构造方法的指定类型的注解。
- Annotation[][] getParameterAnnotations():返回构造方法参数的注解。
Field
Field 类是 Java 反射中的一个重要类,用于表示类的字段(成员变量)。以下是 Field 类中的一些重要方法:
- 获取字段的名称和修饰符:
- String getName():返回字段的名称。
- int getModifiers():返回字段的修饰符,例如 public、private、protected、static 等。
- 获取字段的类型:
- Class<?> getType():返回字段的类型。
- Type getGenericType():返回字段的泛型类型。
- 获取和设置字段的值:
- 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。
- 获取和设置字段的访问权限:
- boolean isAccessible():返回字段的可访问状态。
- void setAccessible(boolean flag):设置字段的可访问状态。如果要访问 private 字段,需要将 flag 设置为 true。
- 获取字段的注解:
- Annotation[] getAnnotations():返回字段的注解。
- Annotation[] getDeclaredAnnotations():返回字段的所有注解。
- T getAnnotation(Class<T> annotationClass):返回字段的指定类型的注解。
Method
Method 类是 Java 反射中的一个重要类,用于表示类的方法。以下是 Method 类中的一些重要方法:
- 获取方法的名称和修饰符:
- String getName():返回方法的名称。
- int getModifiers():返回方法的修饰符,例如 public、private、protected、static 等。
- 获取方法的参数类型和返回类型:
- Class<?>[] getParameterTypes():返回方法的参数类型。
- Type[] getGenericParameterTypes():返回方法的泛型参数类型。
- Class<?> getReturnType():返回方法的返回类型。
- Type getGenericReturnType():返回方法的泛型返回类型。
- 获取方法的异常类型:
- Class<?>[] getExceptionTypes():返回方法抛出的异常类型。
- Type[] getGenericExceptionTypes():返回方法抛出的泛型异常类型。
- 调用方法:
- Object invoke(Object obj, Object... args):调用指定对象上的该方法。obj 参数表示方法所在的对象,args 参数表示方法的参数值。对于静态方法,obj 参数可以为 null。
- 获取和设置方法的访问权限:
- boolean isAccessible():返回方法的可访问状态。
- void setAccessible(boolean flag):设置方法的可访问状态。如果要访问 private 方法,需要将 flag 设置为 true。
- 获取方法的注解:
- Annotation[] getAnnotations():返回方法的注解。
- Annotation[] getDeclaredAnnotations():返回方法的所有注解。
- T getAnnotation(Class<T> annotationClass):返回方法的指定类型的注解。
- Annotation[][] getParameterAnnotations():返回方法参数的注解。
创建实例
- 使用默认构造方法创建实例:
// 加载类
Class<?> cls = Class.forName("Reflection.bean.Person");
// 获取默认构造方法(无参构造方法)
Constructor<?> constructor = cls.getDeclaredConstructor();
// 设置构造方法的访问权限,如果构造方法是私有的,则需要设置为 true
constructor.setAccessible(true);
// 创建实例
Object obj = constructor.newInstance();
复制代码
- 使用带参数的构造方法创建实例:
// 加载类
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 方法被弃用,原因是它存在一些问题和局限性。主要问题如下:
异常处理不明确:Class 类的 newInstance 方法只能抛出 InstantiationException 和 IllegalAccessException 异常。当调用构造方法时,如果构造方法本身抛出了异常,newInstance 会将该异常包装为 InvocationTargetException,然后将其设置为 InstantiationException 的原因(cause),这使得异常处理变得复杂和不直观。
仅支持无参构造方法:Class 类的 newInstance 方法只能调用类的无参(默认)构造方法。这对于需要使用带参数构造方法的类来说是不够灵活的。
为了解决这些问题,Java 引入了 Constructor 类的 newInstance 方法。它具有以下优势:
异常处理更清晰:Constructor 类的 newInstance 方法可以直接抛出 InvocationTargetException,使得异常处理更直接和简单。
支持带参数构造方法:Constructor 类的 newInstance 方法可以处理带参数的构造方法,使其具有更好>的灵活性。
支持访问控制:通过 Constructor 类的 setAccessible 方法,可以在必要时访问私有构造方法。
反射的常见面试题
- 什么是 Java 反射?
- 反射是 Java 语言提供的一种机制,允许在运行时检查、访问和操作类、方法、字段和构造方法等元素。使用反射,可以在程序运行时动态加载类、创建对象、调用方法和访问字段。
- Java 反射的主要用途是什么?
- Java 反射的主要用途包括:
- 动态加载类和创建对象
- 动态调用方法
- 动态访问和修改字段
- 获取类的元数据(例如类名、方法、字段、注解等)
- 实现动态代理
- 如何使用反射获取类的实例?
- 使用 Class.forName("类的全限定名") 方法动态加载类,然后使用 Constructor.newInstance() 方法创建类的实例。
- 如何使用反射调用方法?
- 使用 Class.getDeclaredMethod() 方法获取特定的方法,然后使用 Method.invoke() 方法调用该方法。
- 如何使用反射访问字段?
- 使用 Class.getDeclaredField() 方法获取特定的字段,然后使用 Field.get() 和 Field.set() 方法分别获取和设置字段的值。
- 反射的性能问题?
- 反射操作相对于直接操作会有一定的性能损失,因为反射涉及到运行时类型检查、方法调用等额外操作。在性能敏感的场景下应谨慎使用反射。但在很多场景中,反射带来的灵活性和可扩展性可以抵消性能损失。
- 什么是动态代理?如何使用反射实现动态代理?
- 动态代理是一种在运行时动态创建代理对象的技术,代理对象可以实现指定的接口。Java 反射提供了 java.lang.reflect.Proxy 类来实现动态代理。通过 Proxy.newProxyInstance() 方法可以创建代理对象,需要提供一个实现了 InvocationHandler 接口的对象来处理代理方法的调用。
作者:小新x
链接:
https://juejin.cn/post/7223280402474926138
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
猜你喜欢
- 2025-05-03 又被问到了, java 面试题:反射的实现原理及用途?
- 2025-05-03 java面向对象编程(Java面向对象编程的三个特征)
- 2025-05-03 Java程序员如何学习Golang(一)(java程序员教程)
- 2025-05-03 Java程序员,一周Python入门:面向对象(OOP) 对比学习
- 2025-05-03 Java面试常见问题:Java注解(java注解使用案例)
- 2025-05-03 idea万能快捷键,你不知道的17个实用技巧!!!
- 2025-05-03 疯传!Java 继承底层逻辑大揭秘,看完直接拿捏面试官!
- 2025-05-03 Java中public、protected、package-private和private的区别
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)