反射
基本概念
🚀 作用:在程序运行期间,动态的获取类对象,从而使用类对象获取类中相关的信息并且访问;
通俗的讲:不通过new对象的方式,依然可以访问类中的属性、方法、构造方法;
生活中的反射:倒车镜,拍X光片,IDE的自动提示,
在外部就可以得到内部的东西,类似于一个镜子,通过镜子能看到后边的内容。这就是反射。
万物皆对象 类、属性、方法、构造器 也属于对象。
🎨 反射类介绍
java.lang.Class
类类,每一个类都将在类加载阶段自动产生此类的实例;
java.lang.reflect.Field
字段/属性类,任何一个属性都属于此类的实例;
java.lang.reflect.Method
方法类,任何一个方法都属于此类的实例;
java.lang.Constructor
构造器类,任何一个构造器都属于此类的实例;
获取Class对象
🚀 方式1:通过Class.forName 获取Class对象
// 方式1:通过Class.forName 获取Class对象 ( String类型的 全限定名)
// 只能在运行时,获取到传入的参数,无法在编译时得知Class类对象的类型
Class<?> aClass = Class.forName("com.ReflectPart.Singer");
System.out.println("aClass = " + aClass); // class com.ReflectPart.Singer
🚀 方式2:使用类名.class属性 获取Class对象
// 方式2:使用类名.class属性 获取Class对象
// 在编译时,就得知Class类对象的类型,所以可以提前得知Class类型的对象,泛型为Singer
Class<Singer> singerClass = Singer.class;
System.out.println("singerClass = " + singerClass); // class com.ReflectPart.Singer
🚀 方式3:使用Object类中getClass() 方法,获取Class对象(实例方法)
// 方式3:使用Object类中getClass() 方法,获取Class对象(实例方法)
// 在编译时,可以得知Class类型的对象为Singer类或Singer类的子类,
Singer hanser = new Singer();
Class<? extends Singer> aClass1 = hanser.getClass();
System.out.println("aClass1 = " + aClass1); // class com.ReflectPart.Singer
Class类
类本身也是一个类
方法
newInstance()
作用:创建当前Class对象所对应泛型的实例(实例方法),调用当前类对象中的无参构造创建对象。
参数:无
返回值:实例对象
示例:
// 方式2:使用类名.class属性 获取Class对象
// 在编译时,就得知Class类对象的类型,所以可以提前得知Class类型的对象,泛型为Singer
Class<Singer> singerClass = Singer.class;
System.out.println("singerClass = " + singerClass); // class com.ReflectPart.Singer
// 返回一个 Singer (泛型)类型的实例
Singer singer = singerClass.newInstance()
getField(String fieldName)
作用:根据字段名称获取到一个public修饰的字段对象
参数:String类型
返回值:Field类型
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field = stuClass.getField("age");
getFields()
作用:获取本类中所有的public关键字修饰的字段
参数:无
返回值:Field类型的数组
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field[] fields = stuClass.getFields();
// 遍历
fields.for
getDeclaredField(String name)
作用:获取到指定的本类中已定义的字段(私有的也可以获取到,但获取不到父类中的方法)
参数:字段的String名称
返回值:Field对象
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field = stuClass.getDeclaredField("name");
getDeclaredFields()
作用:获取到本类中所有的已定义的字段(但获取不到父类中的方法)
参数:无
返回值:Field对象数组
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field[] fields = stuClass.getDeclaredFields();
// 遍历
fields.for
getMethod(String name,...paramsType)
getMethod(String name,Class<?>...paramsType)
作用:跟据名字获取本类public修饰的以及继承父类的方法
参数:第一个参数是String类型的方法名,第二个参数是可变长参数。
第二个参数默认不传代表,参数为空。
若方法中的参数为int类型 例如 public void m1(int age){}
则参数为 int类型的class对象,即为 int.class
返回值:Method类型对象
示例:
// 创建类Class对象
Class<?> vupClass = Class.forName("com.ReflectPart.getMethod.Vup");
// 获取方法对象
Method m3 = vupClass.getMethod("m3", int.class, String.class);
// 忽略JVM警告
m3.setAccessible(true);
m3.invoke(o,18,"hanser");
getMethods()
作用:获取所有本类public修饰的以及继承父类的方法
参数:无
返回值:Method类型的对象数组
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Method[] methods = stuClass.getMethods();
getDeclareMethod(String name,paramsType )
作用:根据参数获取本类中已定义的方法(包含私有的)
参数:第一个参数是String类型的方法名,第二个参数是可变长参数(形参列表)。
返回值:Method类型的对象
示例:
// 创建类Class对象
Class<?> vupClass = Class.forName("com.ReflectPart.getMethod.Vup");
// 获取本类中已定义的方法(私有方法)
Method m31 = vupClass.getDeclaredMethod("m3", String.class, int.class);
getDeclareMethods()
作用:获取本类所有已定义的以及继承父类的方法(包含私有的)
参数:无
返回值:Method类型的对象数 组
示例:
// 创建类Class对象
Class<?> vupClass = Class.forName("com.ReflectPart.getMethod.Vup");
// 获取本类中已定义的方法(私有方法)
Method m31 = vupClass.getDeclaredMethod("m3", String.class, int.class);
Method[] declaredMethods = vupClass.getDeclaredMethods();
getConstructor()
作用:获取指定的类的public修饰的构造方法(根据形参列表不同进行筛选)
参数:形参的Class对象
返回值:返回一个 Constructor对象
示例:
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.Vup");
// 获取无参构造
Constructor<?> constructor = vupClass.getConstructor();
// 获取有参构造
Constructor<?> constructor1 = vupClass.getConstructor(String.class, int.class, int.class);
getConstructors()
作用:获取类的所有public修饰的构造方法
参数:无
返回值:返回数组 Constructor对象
示例:
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.Vup");
// 获取无参构造
Constructor<?>[] constructors = vupClass.getConstructors();
// 遍历
for (Constructor<?> constructor : constructors) {
System.out.println(constructor.getName() + " " +constructor.getParameterCount());
}
getDeclaredConstructor(...parameterTypes)
作用:获取指定形参的构造方法
参数:形参的Class对象
返回值:返回Constructor对象
示例:
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.Vup");
// 获取无参构造
Constructor<?> constructor = vupClass.getConstructor(String.class, int.class, int.class);
getDeclaredConstructors()
作用:获取所有访问修饰符修饰的构造方法
参数:无
返回值:返回数组 Constructor对象
示例:
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.Vup");
// 获取无参构造
Constructor<?>[] constructors = vupClass.getConstructors();
// 遍历
for (Constructor<?> constructor : constructors) {
System.out.println("constructor = " + constructor);
}
getClassLoader()
作用:返回类的类加载器
参数:无
返回值:ClassLoader类型
示例:
ClassLoader cl= this.getClass().getClassLoader();
// 通过这个类的ClassLoader对象,获取到指定路径的properties文件,生成一个读取流,读取文件内容
// jdbc.properties这个文件位于src目录中
InputStream inputStream = cl.getResourceAsStream("jdbc.properties");
getGenericSuperclass()
作用:
获取当前类的 直接父类 的 带泛型类型信息 的 Type。
如果父类没有使用泛型,则返回普通的 Class;
如果父类用了泛型,比如 BaseDao<User>,它能保留 <User> 这样的信息。
参数:无
返 回值:
Type 类型(可以是 ParameterizedType、Class 等)
常见情况:
ParameterizedType → 父类带泛型,比如 BaseDao<User>。
Class → 父类没带泛型。
null → 没有父类(比如 Object)。
示例:
// 通过子类实例对象,获取父类(自己)的泛型T的实际名称
// 此处的this代表的是FruitDaoImpl实例,而不是BaseDao
// this.getClass()得到的就是FruitDaoImpl的Class对象
// getGenericSuperclass() 获取带有泛型的父类,因此可以获取到 BaseDao<Fruit>
// 因为我们是这样定义的:class FruitDaoImpl extends BaseDao<Fruit>,所以泛型父类是: BaseDao<Fruit>
Type genericSuperclass = this.getClass().getGenericSuperclass();
// 把父类的泛型信息,从通用的 Type 强转为 ParameterizedType,以便后续获取实际的泛型参数。
// 强转为ParameterizedType类型
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
// getActualTypeArguments 获取实际的类型参数
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
// 因为当前BaseDao<T>后面只有一个泛型位置,所以此处我们使用的是[0]
// getTypeName() 获取类型名称
// getTypeName() 返回完整类名,例如 "com.xxx.pojo.Fruit"
String typeName = actualTypeArguments[0].getTypeName();
entityClassName = typeName;
Field类
类中的属性也有自己的类(类型定义,万物皆对象)
方法
getName()
作用:获取字段的名称
参数:无
返回值:String
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field = stuClass.getField("age");
// 获取字段的名称
field.getName()
getType()
作用:获取字段的类型
参数:无
返回值:String
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field = stuClass.getField("age");
// 获取字段的类型
field.getType() // 基本数据类型、数组、等
getModifiers()
作用:获取字段的Java语言访问修饰符
1 代表是public修饰的
参数:无
返回值:String
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field = stuClass.getField("age");
// 获取字段的类型
field.getType() // 基本数据类型、数组、等
set()
作用:给属性赋值。
参数:第一个参数是哪一个学生对象,第二个参数是赋值
返回值:无
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field1 = stuClass.getField("age");
// 获取对象(方式1)
// Student stu = new Student();
Object o = stuClass.newInstance()
// 赋值(方式1)
// field1.set(stu,12);
field1.set(o,12);
get()
作用:获取属性的值。
参数:第一个参数是哪一个对象
返回值:Field类型
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field1 = stuClass.getField("age");
// 获取对象
Object o = stuClass.newInstance()
// 获取属性值
field1.get(o);
setAccessible()
作用:忽略JVM的安全检查。即可以在不在同类中访问私有属性
(不通过反射是无法访问私有属性的,原因是在编译期间就无法通过)
(反射是在运行时才用到的,绕过的编译的检查,但是在运行期间仍会抛异常,通过设置忽略风险,仍能够获取到私有属性)
参数:布尔值 true,忽略安全检查
返回值:无
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取字段对象
Field field1 = stuClass.getField("age");
// 忽略JVM的安全检查,访问私有属性
field1.setAccessible(true)
getAnnotation(annotationClass)
作用:获取某个元素(类/方法/字段等)上指定类型的注解对象
参数:注解类型class对象
返回值:注解实例对象 Annotation类型的泛型 UserName 自己
示例:
Class<?> dataClass = Class.forName("com.ReflectPart.AnnoAndReflect.DataBaseDemo.DataInfo");
Field declaredField = dataClass.getDeclaredField("userName");
// 获取注解实例对象
UserName name = declaredField.getAnnotation(UserName.class);
// 获取注解属性的值,实例的方法就是 注解定义的属性的名称
String value = name.value();
getAnnotations()
作用:获取当前属性上添加的所有注解,包含继承自父类的注解对象。
参数:无
返回值:注解实例对象数组[username,password]
示例:
Class<?> dataClass = Class.forName("com.ReflectPart.AnnoAndReflect.DataBaseDemo.DataInfo");
// 获取属性
Field declaredField = dataClass.getDeclaredField("password");
Annotation[] annotations = declaredField.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
getDeclaredAnnotation(annotationClass)
作用:获取指定类型的注解对象,如果字段上有这个注解,就返回注解的实例,否则返回 null,不包含继承自父类的注解对象
参数:注解的class对象
返回值:注解类型实例
示例:
Class<?> dataClass = Class.forName("com.ReflectPart.AnnoAndReflect.DataBaseDemo.DataInfo");
// 获取属性
Field declaredField = dataClass.getDeclaredField("password");
Driver drive = declaredField.getDeclaredAnnotation(Driver.class);
System.out.println("drive.value() = " + drive.value()); // drive.value() = 123
getDeclaredAnnotations()
作用:获取当前属性上添加的所有注解,不包含继承自父类的注解对象。
参数:无
返回值:注解实例对象数组,每一个元素的是Annotation类型。
示例:
Class<?> dataClass = Class.forName("com.ReflectPart.AnnoAndReflect.DataBaseDemo.DataInfo");
// 获取属性
Field declaredField = dataClass.getDeclaredField("password");
Annotation[] anns = declaredField.getDeclaredAnnotations();
for (Annotation ann : anns) {
System.out.println(ann);
}
isAnnotationPresent()
作用:判断某个元素(类/方法/字段等)上是否存在指定类型的注解
参数:注解类型class对象,当前属性上方是否添加了UserName注解。
返回值:布尔值,存在指定类型的注释,则返回true,否则返回false
示例:
Class<?> dataClass = Class.forName("com.ReflectPart.AnnoAndReflect.DataBaseDemo.DataInfo");
Field declaredField = dataClass.getDeclaredField(username);
if (declaredField.isAnnotationPresent(UserName.class)){
// 获取注解实例对象
UserName name = declaredField.getAnnotation(UserName.class);
// 获取注解属性的值
String value = name.value();
System.out.println("value = " + value);
}
Method类
方法类;java.lang.reflect.Method
方法类,任何一个方法都 属于此类的实例;
方法
getName()
作用:获取方法的名称
参数:无
返回值:String类型的方法名
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取方法数组
Method[] methods = stuClass.getMethods();
// 获取方法名称
for(Method method: methods){
String name = method.getName();
System.out.print(name)
}
getParameterCount()
作用:获取方法中参数的个数
参数:无
返回值:个数
示例:
// 获取Class对象
Class<?> stuClass = Class.forName("com.yuluochenxiao.Student");
// 获取方法数组
Method[] methods = stuClass.getMethods();
// 获取方法名称
for(Method method: methods){
String name = method.getName();
int count = method.getParameterCount();
System.out.print(name)
}
invoke(obj,args)
作用:调用方法(实例级别)
参数:第一个参数是类的实例对象,第二个参数是给这个方法传递的参数
若有参数,直接写参数即可。
返回值:无
示例:
Class<?> vupClass = Class.forName("com.ReflectPart.getMethod.Vup");
// 根据指定名称和参数列表获取方法对象实例
Method m3 = vupClass.getMethod("m3", int.class, String.class);
// 忽略JVM警告
m3.setAccessible(true);
m3.invoke(o,18,"hanser");
setAccessible()
作用:如同Field中的setAccessible()方法使用一致,忽略JVM警告。
参数:true
返回值:无
示例:
Class<?> vupClass = Class.forName("com.ReflectPart.getMethod.Vup");
// 根据指定名称和参数列表获取方法对象实例
Method m3 = vupClass.getMethod("m3", int.class, String.class);
// 忽略JVM警告
m3.setAccessible(true);
m3.invoke(o,18,"hanser");
getParameters()
作用:获取方法的参数信息,返回一个 Parameter 数组,表示该方法的所有形参。每个 Parameter 对象包含了参数的 类型、名字(需编译时加上 -parameters 选项才保留)、修饰符 等信息。
参数:无参数
返回值:Parameter[]:方法的参数数组,如果方法没有形参,返回一个长度为 0 的数组
示例:
// 获取方法对象
Method method = Demo.class.getMethod("testMethod", String.class, int.class);
// 获取参数列表
Parameter[] params = method.getParameters();
for (Parameter param : params) {
// 默认是 arg0, arg1,除非用 -parameters 编译
// 在IDEA中设置 ,编译时添加 -parameters
System.out.println("参数名: " + param.getName());
System.out.println("参数类型: " + param.getType());
System.out.println("参数类型: " + param.getType().getName());
System.out.println("是否有名字: " + param.isNamePresent());
System.out.println("------------");
}
getParameters()编译问题
JDK中的反射,getParameters()
方法,默认获取到的形参名称是 arg0,arg1...,从JDK1.8之后可以获取到形参名称,在IDEA中的设置中添加 -parameters,即可。
file-setting-builder-compiler-java compiler-additional command line 设置 -parameters
同时在 Other Setting - setting for new Project中设置一遍,目的是为了在其他的项目中也生效。
Constructor类
Constructor提供了一个类的单个构造函数的信息和访问。所有构造器都属于此类的实例。
构造方法
无
方法
getName()
作用:以字符串形式返回此构造函数的名称。
参数:无
返回值:String类型
示例:
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.getConstructor.Vup");
// 获取Contructor对象
Constructor<?> constructor = vupClass.getConstructor();
// 显示
int parameterCount = constructor.getParameterCount();
String name = constructor.getName();
getParameterCount()
作用:返回由此对象表示的可执行文件的形式参数(无论是显式声明还是隐式声明)的数量。
参数:无
返回值:int类型的数量
示例:
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.getConstructor.Vup");
// 获取Contructor对象
Constructor<?> constructor = vupClass.getConstructor();
// 显示
int parameterCount = constructor.getParameterCount();
String name = constructor.getName();
System.out.println("name:" + name + "| parameterTypes:" + parameterCount);
newInstance(Object ... args)
作用:根据传入的参数列表 调用对应的有参构造方法创建对象
注意:如果有重名的类名,可以写包的全称,例如:
java.lang.String.class 或 java.lang.util.Date.class
参数:传入有参构造的参数
返回值:类的实例对象
示例:
// 获取class类对象
Class<?> vupClass = Class.forName("com.ReflectPart.Vup");
// 获取无参构造
Constructor<?> constructor = vupClass.getConstructor();
// 获取有参构造
Constructor<?> constructor1 = vupClass.getConstructor(String.class, int.class, int.class);
// 创建实例对象
Object vup = constructor.newInstance();
Object vup1 = constructor1.newInstance("Hanser", 18, 175);
// vup: Vup{name='null', age=0, height=0}
System.out.println("vup: "+vup);
// vup1: Vup{name='Hanser', age=18, height=175}
System.out.println("vup1: "+vup1);