Java-基础-反射
1. 简介
JAVA反射机制是在运行状态中。
对于任意一个类,都能够知道这个类的所有属性和方法。
对于任意一个对象,都能够调用它的任意一个方法和属性。
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
2. Class
Class类其实也是一个Java类,存在于JDK的java.lang包中。
java在运行时的类信息就是通过Class对象表示的。它包含了类的所有信息。
所有的类都是在对其第一次使用时,动态加载到JVM中的(懒加载)。当程序创建第一个对类的静态成员的引用时,就会加载这个类。使用new创建类对象的时候也会被当作对类的静态成员的引用。因此java程序程序在它开始运行之前并非被完全加载,其各个类都是在必需时才加载的。
所以,当我们写的.java文件被加载到JVM的后,会在方法区生成该类的Class实例。此实例包含了该类的所有信息。
2.1 如何获取Class实例
- 类型.class
- 类实例.getClass()
- Class.forName(类的权限定类名)
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
Class<Test> testClass = Test.class;
Test test = new Test();
Class<? extends Test> testClass1 = test.getClass();
Class<Test> testClass2 = (Class<Test>) Class.forName("com.ldx.test.Test");
}
}
2.2 使用Class实例
创建一个User
类
package com.ldx.test;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class User {
private String name;
private Integer age;
public boolean isAdmin(String name) {
if("admin".equals(name)) {
return true;
}
return false;
}
}
创建一个测试类Test
package com.ldx.test;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
@Slf4j
public class Test {
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
Field[] declaredFields = userClass.getDeclaredFields();
log.info("User类的名称:{}", userClass.getSimpleName());
log.info("User类的权限定类名:{}", userClass.getName());
log.info("User类的字段有:{}", Arrays.toString(declaredFields));
log.info("User类的方法有:{}", Arrays.toString(userClass.getDeclaredMethods()));
Method isAdmin = userClass.getMethod("isAdmin", String.class);
log.info("User::isAdmin方法返回值为:" + isAdmin.invoke(userClass.newInstance(), "admin"));
}
}
输出内容如下:
com.ldx.test.Test - User类的名称:User
com.ldx.test.Test - User类的权限定类名:com.ldx.test.User
com.ldx.test.Test - User类的字段有:[private java.lang.String com.ldx.test.User.name, private java.lang.Integer com.ldx.test.User.age]
com.ldx.test.Test - User类的方法有:[public boolean com.ldx.test.User.isAdmin(java.lang.String), public java.lang.Integer com.ldx.test.User.getAge(), public void com.ldx.test.User.setAge(java.lang.Integer), public java.lang.String com.ldx.test.User.toString(), public java.lang.String com.ldx.test.User.getName(), public void com.ldx.test.User.setName(java.lang.String)]
com.ldx.test.Test - User::isAdmin方法返回值为:true
2.3 常用方法摘要
返回值 | 方法说明 |
---|---|
<U> Class<? extends U> |
asSubclass(Class<U> clazz) 强制转换该 Class 对象,以表示指定的 class 对象所表示的类的一个子类。 |
static Class<?> |
forName(String className) 返回与带有给定字符串名的类或接口相关联的 Class 对象。 |
<A extends Annotation>A |
getAnnotation(Class<A> annotationClass) 如果存在该元素的指定类型的注解,则返回这些注解,否则返回 null。 |
Annotation[] |
getAnnotations() 返回此元素上存在的所有注解。 |
ClassLoader |
getClassLoader() 返回该类的类加载器。 |
Class<?> |
getComponentType() 返回表示数组组件类型的 Class 。 |
Annotation[] |
getDeclaredAnnotations() 返回直接存在于此元素上的所有注解。 |
Class<?> |
getDeclaringClass() 如果此 Class 对象所表示的类或接口是另一个类的成员,则返回的 Class 对象表示该对象的声明类。 |
T[] |
getEnumConstants() 如果此 Class 对象不表示枚举类型,则返回枚举类的元素或 null。 |
Type[] |
getGenericInterfaces() 返回表示某些接口的 Type ,这些接口由此对象所表示的类或接口直接实现。 |
Type |
getGenericSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type 。 |
Class<?>[] |
getInterfaces() 确定此对象所表示的类或接口实现的接口。 |
int |
getModifiers() 返回此类或接口以整数编码的 Java 语言修饰符。 |
String |
getName() 以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。 |
String |
getCanonicalName() 返回 Java Language Specification 中所定义的底层类的规范化名称。主要用于输出(toString)或log打印,大多数情况下和 getName() 一样,但是在内部类、数组等类型的表示形式就不同了。不能用getCanonicalName() 去加载类对象,必须用getName() , |
Package |
getPackage() 获取此类的包。 |
InputStream |
getResourceAsStream(String name) 查找具有给定名称的资源。 |
String |
getSimpleName() 返回源代码中给出的底层类的简称。 |
Class<? super T> |
getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class 。 |
boolean |
isAnnotation() 如果此 Class 对象表示一个注解类型则返回 true。 |
boolean |
isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。 |
boolean |
isAnonymousClass() 当且仅当底层类是匿名类时返回 true 。 |
boolean |
isArray() 判定此 Class 对象是否表示一个数组类。 |
boolean |
isAssignableFrom(Class<?> cls) 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同,或是否是其超类或超接口。 |
boolean |
isEnum() 当且仅当该类声明为源代码中的枚举时返回 true。 |
boolean |
isInstance(Object obj) 如果obj是这个类的一个实例此方法返回true。 |
boolean |
isInterface() 判定指定的 Class 对象是否表示一个接口类型。 |
boolean |
isLocalClass() 当且仅当这个类是局部类此方法返回true。 |
boolean |
isMemberClass() 当且仅当底层类是成员类时返回 true 。 |
boolean |
isPrimitive() 判定指定的 Class 对象是否表示一个基本类型。 |
T |
newInstance() 创建此 Class 对象所表示的类的一个新实例。 |
3. new Instance
想通过反射创建对象大概有以下几种方式:
- 通过
Class.newInstance()
直接创建对象。 - 通过Class实例获取到
Constructor
(构造器),通过构造器创建对象。
获取构造方法的途径有以下几种:
返回值 | 方法说明 |
---|---|
Constructor |
getConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的public构造方法。 |
Constructor<?>[] |
getConstructors() 返回所有 Constructor 对象,它反映此 Class 对象所表示的类的public构造方法。 |
Constructor |
getDeclaredConstructor(Class<?>... parameterTypes) 返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类的public/private构造方法。 |
Constructor<?>[] |
getDeclaredConstructor() 返回所有 Constructor 对象,该对象反映此 Class 对象所表示的类的public/private构造方法。 |
示例代码如下:
@Getter
@Setter
@ToString
public class User {
private String name;
private Integer age;
public User() {
this.name = "张三";
this.age = 24;
}
public boolean isAdmin(String name) {
if("admin".equals(name)) {
return true;
}
return false;
}
}
@Slf4j
public class Test {
public static void main(String[] args) throws Exception {
Class<User> userClass = User.class;
User user = userClass.newInstance();
Constructor<User> constructor = userClass.getConstructor();
User user1 = constructor.newInstance();
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor();
// 如果构造器为private,则可以设置访问权限为true,即可newInstance
declaredConstructor.setAccessible(true);
User user2 = declaredConstructor.newInstance();
log.info(user.toString());
log.info(user1.toString());
log.info(user2.toString());
}
}
输出内容如下:
15:59:01.653 [main] INFO com.ldx.test.Test - User(name=张三, age=24)
15:59:01.663 [main] INFO com.ldx.test.Test - User(name=张三, age=24)
15:59:01.663 [main] INFO com.ldx.test.Test - User(name=张三, age=24)
4. Method
返回值 | 方法说明 |
---|---|
Method |
getMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,它反映此 Class 对象所表示的类的public方法。(可以获取父类的方法) |
Method[] |
getMethods() 返回所有 Method 对象,它反映此 Class 对象所表示的类的public方法。(可以获取父类的方法) |
Method |
getDeclaredMethod(String name, Class<?>... parameterTypes) 返回一个 Method 对象,该对象反映此 Class 对象所表示的类的public/private方法。(只获取当前类的方法) |
Method[] |
getDeclaredMethods() 返回所有 Method 对象,它反映此 Class 对象所表示的类的public/private方法。(只获取当前类的方法) |
示例代码如下:
@Slf4j
public class Test {
public static void main(String[] args) throws Exception {
Class<Man> manClass = Man.class;
Man man = manClass.newInstance();
Method getHairColor = manClass.getMethod("getHairColor");
Method getHairColor1 = manClass.getDeclaredMethod("getHairColor");
// 如果方法为private,则可以设置访问权限为true,即可newInstance
getHairColor1.setAccessible(true);
log.info(getHairColor.invoke(man).toString());
log.info(getHairColor1.invoke(man).toString());
// 可以调用到父类方法
Method isAdmin = manClass.getMethod("isAdmin", String.class);
log.info(isAdmin.invoke(man, "admin").toString());
// 获取不到父类方法
Method isAdmin1 = manClass.getDeclaredMethod("isAdmin", String.class);
log.info(isAdmin1.invoke(man, "admin").toString());
}
}
class Man extends User {
public String getHairColor() {
return "一头黑色秀发";
}
}
输出内容如下:
16:30:49.809 [main] INFO com.ldx.test.Test - 一头黑色秀发
16:30:49.818 [main] INFO com.ldx.test.Test - 一头黑色秀发
16:30:49.818 [main] INFO com.ldx.test.Test - true
Exception in thread "main" java.lang.NoSuchMethodException: com.ldx.test.Man.isAdmin(java.lang.String)
at java.lang.Class.getDeclaredMethod(Class.java:2130)
at com.ldx.test.Test.main(Test.java:31)
Method类除了有上面的invoke
方法,常用方法还有如下:
返回值 | 方法说明 |
---|---|
<T extends Annotation>T |
getAnnotation(Class<T> annotationClass) 如果存在该元素的指定类型的注解,则返回这些注释,否则返回 null。 |
Annotation[] |
getDeclaredAnnotations() 返回直接存在于此元素上的所有注解。 |
Class<?> |
getDeclaringClass() 返回表示声明由此 Method 对象表示的方法的类或接口的 Class 对象。 |
Type[] |
getGenericParameterTypes() 按照声明顺序返回 Type 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型的。 |
Type |
getGenericReturnType() 返回表示由此 Method 对象所表示方法的正式返回类型的 Type 对象。 |
int |
getModifiers() 以整数形式返回此 Method 对象所表示方法的 Java 语言修饰符。 |
String |
getName() 以 String 形式返回此 Method 对象表示的方法名称。 |
Annotation[][] |
getParameterAnnotations() 返回表示按照声明顺序对此 Method 对象所表示方法的形参进行注解的那个数组的数组。 |
Class<?>[] |
getParameterTypes() 按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。 |
Class<?> |
getReturnType() 返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型。 |
Object |
invoke(Object obj, Object... args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。 |
boolean |
isVarArgs() 如果将此方法声明为带有可变数量的参数,则返回 true ;否则,返回 false 。 |
String |
toGenericString() 返回描述此 Method 的字符串,包括类型参数。 |
void |
setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值,即设置其可访问性。 |
其中:getReturnType/getGenericReturnType
都是获取Method对象表示的方法的返回类型,只不过前者返回的Class类型后者返回的Type,Type就是一个接口而已,在Java8中新增一个默认的方法实现,返回的就参数类型信息。getParameterTypes/getGenericParameterTypes
亦是如此。
其返回结果和Class.getTypeName()
放回结果相同,就是输出返回值的类的name
(带包名)。
public interface Type {
default String getTypeName() {
return toString();
}
}
5. Field
返回值 | 方法说明 |
---|---|
Field |
getField(String name) 返回一个 Field 对象,它反映此 Class 对象所表示的类的public属性。(可以获取父类的方法) |
Field[] |
getFields() 返回所有 Field 属性,它反映此 Class 对象所表示的类的public属性。(可以获取父类的方法) |
Field |
getDeclaredField(String name) 返回一个 Field 对象,该对象反映此 Class 对象所表示的类的public/private属性。(只获取当前类的方法) |
Field[] |
getDeclaredFields() 返回所有 Field 属性,该对象反映此 Class 对象所表示的类的public/private属性。(只获取当前类的方法) |
示例代码如下:
@Slf4j
public class Test {
public static void main(String[] args) throws Exception {
Class<Man> manClass = Man.class;
Man man = manClass.newInstance();
Field hair = manClass.getDeclaredField("hair");
hair.setAccessible(true);
hair.set(man, "浓密的黑发");
log.info(hair.get(man).toString());
log.info(man.getHair());
Class<?> type = hair.getType();
Class<?> declaringClass = hair.getDeclaringClass();
log.info(type.getCanonicalName());
log.info(declaringClass.getCanonicalName());
}
}
@Getter
class Man extends User {
private String hair;
}
输出内容如下:
17:13:17.903 [main] INFO com.ldx.test.Test - 浓密的黑发
17:13:17.909 [main] INFO com.ldx.test.Test - 浓密的黑发
17:13:17.909 [main] INFO com.ldx.test.Test - java.lang.String
17:13:17.909 [main] INFO com.ldx.test.Test - com.ldx.test.Man
Field类除了有上面写到的方法,常用方法还有如下:
返回值 | 方法说明 |
---|---|
Object |
get(Object obj) 返回指定对象上此 Field 表示的字段的值。 |
<T extends Annotation>T |
getAnnotation(Class<T> annotationClass) 如果存在该元素的指定类型的注解,则返回这些注释,否则返回 null。 |
boolean |
getBoolean(Object obj) 获取一个静态或实例 boolean 字段的值。 |
byte |
getByte(Object obj) 获取一个静态或实例 byte 字段的值。 |
char |
getChar(Object obj) 获取 char 类型或另一个通过扩展转换可以转换为 char 类型的基本类型的静态或实例字段的值。 |
Annotation[] |
getDeclaredAnnotations() 返回直接存在于此元素上的所有注解。 |
Class<?> |
getDeclaringClass() 返回表示类或接口的 Class 对象,该类或接口声明由此 Field 对象表示的字段。 |
double |
getDouble(Object obj) 获取 double 类型或另一个通过扩展转换可以转换为 double 类型的基本类型的静态或实例字段的值。 |
float |
getFloat(Object obj) 获取 float 类型或另一个通过扩展转换可以转换为 float 类型的基本类型的静态或实例字段的值。 |
Type |
getGenericType() 返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型。 |
int |
getInt(Object obj) 获取 int 类型或另一个通过扩展转换可以转换为 int 类型的基本类型的静态或实例字段的值。 |
long |
getLong(Object obj) 获取 long 类型或另一个通过扩展转换可以转换为 long 类型的基本类型的静态或实例字段的值。 |
int |
getModifiers() 以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符。 |
String |
getName() 返回此 Field 对象表示的字段的名称。 |
short |
getShort(Object obj) 获取 short 类型或另一个通过扩展转换可以转换为 short 类型的基本类型的静态或实例字段的值。 |
Class<?> |
getType() 返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。 |
boolean |
isEnumConstant() 如果此字段表示枚举类型的元素,则返回 true ;否则返回 false 。 |
void |
set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。 |
void |
setBoolean(Object obj, boolean z) 将字段的值设置为指定对象上的一个 boolean 值。 |
void |
setByte(Object obj, byte b) 将字段的值设置为指定对象上的一个 byte 值。 |
void |
setChar(Object obj, char c) 将字段的值设置为指定对象上的一个 char 值。 |
void |
setDouble(Object obj, double d) 将字段的值设置为指定对象上的一个 double 值。 |
void |
setFloat(Object obj, float f) 将字段的值设置为指定对象上的一个 float 值。 |
void |
setInt(Object obj, int i) 将字段的值设置为指定对象上的一个 int 值。 |
void |
setLong(Object obj, long l) 将字段的值设置为指定对象上的一个 long 值。 |
void |
setShort(Object obj, short s) 将字段的值设置为指定对象上的一个 short 值。 |
String |
toGenericString() 返回一个描述此 Field (包括其一般类型)的字符串。 |
void |
setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值,即设置其可访问性。 |