目录
引言:
一、获取Class对象的方法
二、使用反射获取构造方法
三、使用反射获成员变量的方法
四、使用反射获成员方法
五.反射的优缺点有哪些?
优点包括:
缺点包括:
总结:
引言:
什么是反射:反射其实我们很早就知晓了,比如我们经常访问一个对象的方法,idea给的提示就是通过反射来获取的(dogo!!!)
反射技术作为编程语言中的一项强大工具,允许我们在运行时对类的字段、方法和构造函数进行编程访问。这一特性使得反射在框架设计、插件系统、单元测试等场景中有着广泛的应用。本文将详细介绍如何通过反射技术获取Class对象,进而访问类的构造方法、属性和方法。
简而言之:反射就是霸道的,在反射面前,就是透明的!!!我可以随意获取到你的任何属性信息,然后去操作本且去修改它!!!
使用方法:
主要包括2个步骤:获取 和解剖。
首先获取class 对象,然后通过class 对象获取到我们想要的属性,比如成员变量,构造方法,成员方法。然后通过这些就可以再次细获取他们信息,比如(类型,名字,修饰符...) ,甚至你还可以去修改他们的值!!!(包括私有的属性)所有说反射就是破坏封装性的!!!
详细如下:
一、获取Class对象的方法
在反射技术中,获取Class对象是进行后续操作的基础。以下是三种获取Class对象的方法:
自定义了一个学生类:私已成员变量和私有set方法共有get方法,后续演示:
java">package com.hz.example;public class Student {private String name;private int age;public Student() {}public Student(String name) {}private Student( int age) {this.age = age;}private Student(String name, int age) {this.name = name;this.age = age;}public int getAge() {return age;}private void setAge(int age) {this.age = age;}public String getName() {return name;}private void setName(String name) {this.name = name;}private void eat(String something){System.out.println("Student is eating "+something);}private String eat(String something1,int munch){return "Student is eating "+something1+" and he has eaten"+munch+"no of times";}public void show(){System.out.println("Student is eating");}
}
-
通过类名获取: 使用
Class.forName(String className)
方法,其中className
为类的全类名(包名 +类名)(快捷键 :鼠标点击你要获取的类 然后:ctrl +alt + shift + c) 例如,Class<?> clazz = Class.forName("
com.hz.example.Student"); 此方法会源代码阶段去调用(java 文件 -> class文件)
-
通过类字面量获取:使用
类名.class
的方式。这是最直接、最常用的获取Class对象的方法。例如,Class<?> clazz =
Student.class;
。(加载阶段使用)
-
通过对象获取:使用
对象.getClass()
方法。如果已有一个类的实例对象,可以通过该方法获取其Class对象。例如,Student student = new Student();; Class<?> clazz =
Student.getClass(); (运行阶段使用)
使用场景总结:
Class.forName
:适用于需要根据字符串形式的类名动态加载类的情况,常用于框架设计、插件机制等。对象.getClass
:适用于已有一个类的实例对象,需要获取该对象的Class
对象以进行反射操作的情况。类名.class
:适用于编译时就已知要操作的类,是最简单、最常用的获取Class
对象的方法。
二、使用反射获取构造方法
获取了Class对象后,我们就可以通过反射来获取类的构造方法。构造方法分为私有和公有两种,通过反射我们可以访问这两种构造方法。
小知识: 在java中,万物接可对象,java中也定义了相应的类来描述构造方法,成员方法,成员变量:
- 构造方法: Constructor
- 成员方法; Method
- 成员变量: Field
通过这些就可以很快获取到相应对象,进而去操作他们!!!
-
获取所有构造方法:
-
使用
Class.getConstructors()
方法获取所有公有的构造方法;构造方法就是通过Constructor获取,然后加s 就是获取所有公共的构造方法,返回的就是数组, -
使用
Class.getDeclaredConstructors()
方法获取所有(包括私有)构造方法。Declared就是权限的意思,那么类似root 你可以获取所有的,包括私有的,然后下面获取成员变量,方法也一样了!!!
-
-
获取特定构造方法:
-
使用
Class.getConstructor(Class<?>... parameterTypes)
方法根据参数类型获取公有的构造方法,(没加s,就表示获取单个的,本且没有加Declared,就自然获取指定公共的
) -
使用
Class.getDeclaredConstructor(Class<?>... parameterTypes)
方法根据参数类型获取所有(包括私有)构造方法。
-
-
创建对象:获取了构造方法后,可以
-
使用
Constructor.newInstance(Object... initargs)
方法来创建对象实例 -
当然,有时候私已的private还得用 setAccessible(boolen flag) 去更改它的权限为true 才可以去操作:
-
简单Demo:
java">// 通过类名获取Class对象Class<?> aClass = Class.forName("com.hz.example.Student");// 获取类的所有公共构造方法Constructor<?>[] constructors = aClass.getConstructors();// 获取类的所有构造方法,包括私有、受保护和默认访问级别的构造方法Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();// 获取接受一个String类型参数的公共构造方法Constructor<?> constructor = aClass.getConstructor(String.class);// 获取接受一个String类型和一个int类型参数的私有构造方法Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class);// 设置该构造方法为可访问declaredConstructor.setAccessible(true);// 使用该构造方法创建Student对象Student student = (Student) declaredConstructor.newInstance("张三", 18);
原Demo:
三、使用反射获成员变量的方法
其实下面也差不多了!!
Class类中用于获取成员变量的方法
Field[] getsields(): 返回所有公共成员变量对象的数组
Field[]getDeclaredFields(): 返回所有成员变量对象的数组
Field getField(String name): 返回单个公共成员变量对象
Field getDeclaredField(String name): 返回单个成员变量对象
Field类中用于创建对象的方法
void set(Object obj, Object value): 赋值
Object get(Object obj) 获取值。
参考Demo:
java">// 获取Student类的Class对象
Class<?> aClass = Class.forName("com.hz.example.Student");// 获取Student类的所有公共字段(包括继承自父类的字段)
Field[] fields = aClass.getFields();
for (Field field : fields) {// 打印每个公共字段的信息System.out.println(field);
}// 获取Student类声明的所有字段,包括私有字段,但不包括继承的字段
Field[] declaredFields = aClass.getDeclaredFields();// 通过字段名获取Student类的公共字段对象"name"
Field name = aClass.getField("name");
System.out.println(name);// 获取字段名
String name1 = name.getName();// 获取字段类型
Class<?> type = name.getType();
System.out.println(name1 + " " + type);// 创建Student类的实例对象
Student student = new Student("hz");// 设置字段可访问性,以便可以访问私有字段
name.setAccessible(true);// 通过反射获取student对象"name"属性的值
Student value = (Student) name.get(student);
System.out.println("通过反射获取 创建对象name属性的值value = " + value);// 通过反射修改student对象"name"属性的值为"mn"
name.set(student, "mn");// 打印修改后的"name"属性值
System.out.println("修改后的name的值为:" + student.getName());
四、使用反射获成员方法
Class类中用于获取成员方法的方法:
Method[]getMethods():返回所有公共成员方法对象的数组,包括继承的
Methodl] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name, Class<?>... parameterTypes):返回单个公共成员方法对象
Method getDeclaredMethod(String name, Class<?>.. parameterTypes):返回单个成员方法对象
Method类中用于创建对象的方法;
Object invoke(Object obj, Object... args):运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)
getMethods和getDeclaredMethods 区别,getMethods获取公共以外,还会获取父类的方法:我这里没有,但是有一个祖宗 Object !!!!
getMethod 和getDeclaredMethod获取 指定方法的时候: 除了指定方法的名字外,还得去指定他们的参数类型,主要是区分方法重载!!!!(其实和什么类似,构造方法因为名字都一样,不用指定,但是也指定了参数的类型,这些都是类似的!!!)
invoke() 运行方法:
java">// 获取Student类的Class对象
Class<?> aClass = Class.forName("com.hz.example.Student");// 通过反射获取Student类中名为"eat"的方法,该方法接收一个String类型的参数
Method eat = aClass.getDeclaredMethod("eat", String.class);// 设置该方法为可访问,即使它是私有的也可以被访问
eat.setAccessible(true);// 创建Student类的实例
Student student = new Student();// 调用Student对象的eat方法,并传入"苹果"作为参数
// 注意:由于eat方法没有返回值,因此这里不需要处理返回值
// 原eat 方法没有返回值 就不用管
eat.invoke(student,"苹果");// 通过反射获取Student类中名为"eat"的另一个方法,该方法接收一个String类型和一个int类型的参数
Method eat1 = aClass.getDeclaredMethod("eat", String.class, int.class);// 同样设置该方法为可访问
eat1.setAccessible(true);// 调用Student对象的eat方法,并传入"蔡徐坤"和999作为参数
// 由于该方法有返回值,且我们期望返回值为String类型,因此进行类型转换
String invoke = (String) eat1.invoke(student, "蔡徐坤", 999);// 打印返回值
System.out.println(invoke);
五. 反射的优缺点有哪些?
优点包括:
- 动态性:反射允许在运行时动态地获取和操作类的信息,而不需要在编译时知道类的具体细节。
- 灵活性:反射可以通过类名字符串来创建对象实例,可以在运行时动态地调用方法和字段,以及访问和修改私有成员。
- 扩展性:反射可以用于实现一些高级功能,如动态代理、注解处理和依赖注入等。
缺点包括:
- 性能损耗:由于反射需要在运行时获取和操作类的信息,所以相比直接调用,反射操作通常会导致性能下降。
- 安全问题:反射可以访问和修改私有成员,这可能破坏了封装性和安全性,因此,在使用反射时需要谨慎处理权限和访问控制。
- 编码复杂性:反射的使用和理解相对复杂,容易引入错误和难以调试。同时,反射操作也不受编译器的类型检查,可能导致运行时异常。
六. 总结:
反射技术为我们在运行时动态地访问和修改类的字段、方法和构造函数提供了强大的能力。在实际应用中,反射技术有着广泛的应用场景,如框架设计、插件系统、单元测试等。然而,反射技术也带来了一定的性能开销和安全性问题,因此在使用时需要权衡其利弊。