java反射机制
概念
Java 反射机制是 Java 语言的一个重要特性。在学习 Java 反射机制前,大家应该先了解两个概念,编译期和运行期。
编译期:是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。
运行期:是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。
百度百科:
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
个人理解:
Java反射机制可以视为一个输入流,将一个自定义的类,视为一个普通文件,我们可以得到该文件的所有信息,但又比输入流更加的高级,我们又可以执行该类中的功能点,简单来说,反射机制指的是程序在运行时能够获取自身的信息。在 Java 中,只要给定类的全限定名,就可以通过反射机制来获得类的所有信息
什么是静态语言什么是动态语言?
静态类型语言:变量定义时有类型声明的语言。
- 变量的类型在编译的时候确定
- 变量的类型在运行时不能修改
动态类型语言:变量定义时无类型声明的语言。
- 变量的类型在运行的时候确定
- 变量的类型在运行可以修改
如何实现java的语言动态化?
- 通过java的反射机制
- 多态,Java又可以通过接口等方式,在运行时注入相关的类的实现,所以这个又是其动态性的提现
如何使用java的反射机制
熟悉获取 Java 反射使用到类
- Class类
- Constructor 类
- Method 类
- Field 类
Constructor,Method,Field 都是通过Class类得到的,在java.lang.reflect 包下
掌握Class类的常用方法
如何得到Class类对象?
1.众所周知,所有 Java 类均继承了 Object 类,在 Object 类中定义了一个 getClass() 方法,该方法返回同一个类型为 Class 的对象。
2.或者使用 Class类的静态方法forName(“全限定类名”)
类型 | 访问方法 | 返回值类型 | 说明 |
---|---|---|---|
包路径 | getPackage() | Package 对象 | 获取该类的存放路径 |
类名称 | getName() | String 对象 | 获取该类的名称 |
继承类 | getSuperclass() | Class 对象 | 获取该类继承的类 |
实现接口 | getlnterfaces() | Class 型数组 | 获取该类实现的所有接口 |
构造方法 | getConstructor() | Constructor 对象实例 | 获取权限为 public 的构造方法,包括,所继承的父类的构造 |
getConstructors() | Constructor 型数组 | 获取所有权限为 public 的构造方法 | |
getDeclaredContruectors() | Constructor 对象 | 获取当前对象的所有构造方法 | |
方法 | getMethods() | Method 型数组 | 获取权限为 public 的方法,包括父类 |
getDeclaredMethods() | Method 对象数组 | 获取当前对象的所有方法 | |
getDeclaredMethod(String name, Class<?>… parameterTypes) | Method对象 | 获取本类指定的方法 | |
成员变量 | getFields() | Field 型数组 | 获取所有权限为 public 的成员变量,包含父类 |
getDeclareFileds() | Field 型数组 | 获取当前对象的所有成员变量 |
Student类
package cn.entity;
public class Student {
private String name;
private Integer age;
private Double score;
public Student(){
System.out.println("执行了Student的无参构造方法---------------->");
}
public Student(String name) {
System.out.println("执行了Student的有参构造方法---------------->");
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
System.out.println("setAge-------------------->");
}
public Double getScore() {
return score;
}
public void setScore(Double score) {
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
示例代码:
Class<?> cls = Class.forName("cn.entity.Student");
//得到Student类的构造器,此时并未创建实例
Constructor constructor= cls.getDeclaredConstructor();
//得到Student类的所有方法,不包括父类
Method[] methods = cls.getDeclaredMethods()
//得到Student类的指定方法名的方法
Method getName = cls.getMethod("getName");
//得到Student类的指定属性名的属性
Field age = cls.getDeclaredField("age");
掌握 Constructor 类的常用方法
方法名称 | 说明 |
---|---|
isVarArgs() | 查看该构造方法是否允许带可变数量的参数,如果允许,返回 true,否则返回false |
getParameterTypes() | 按照声明顺序以 Class 数组的形式获取该构造方法各个参数的类型 |
getExceptionTypes() | 以 Class 数组的形式获取该构造方法可能抛出的异常类型 |
newInstance(Object … initargs) | 通过该构造方法利用指定参数创建一个该类型的对象,如果未设置参数则表示采用默认无参的构造方法 |
setAccessiable(boolean flag) | 如果该构造方法的权限为 private,默认为不允许通过反射利用 |
newlnstance() | 方法创建对象。如果先执行该方法,并将入口参数设置为 true,则允许创建对象 |
getModifiers() | 获得可以解析出该构造方法所采用修饰符的整数 |
示例代码:
//此方法忽略构造方法的访问修饰符,不推荐使用,会破坏类的封装性,
constructor.setAccessible(true);
//此步是真正的创建一个实例,执行了类的无惨构造,当无惨构造为private时无法使用,需执行setAccessible(true);才能创建实例
Object obj = constructor.newInstance();
Method类的常用方法
静态方法名称 | 说明 |
---|---|
getName() | 获取该方法的名称 |
getParameterTypes() | 按照声明顺序以 Class 数组的形式返回该方法各个参数的类型 |
getReturnType() | 以 Class 对象的形式获得该方法的返回值类型 |
getExceptionTypes() | 以 Class 数组的形式获得该方法可能抛出的异常类型 |
invoke(Object obj,Object…args) | 利用 args 参数执行指定对象 obj 中的该方法,返回值为 Object 类型 |
isVarArgs() | 查看该方法是否允许带有可变数量的参数,如果允许返回 true,否则返回 false |
getModifiers() | 获得可以解析出该方法所采用修饰符的整数 |
示例代码:
Method[] methods = cls.getDeclaredMethods();
//遍历所有本类方法
for (Method method : methods) {
System.out.println(method);
}
//使用方法名,和指定参数类型,得到需要执行的方法,此时并未指定,只是得到该方法。参数类型,可根据对应的属性类型获取
Method setName = cls.getDeclaredMethod("setName", String.class);
/*此时才是真正的执行方法,"obj"为使用那一个对象,
来调用这个方法,“张三”为参数的值,
使用非反射的方式调用就是这种:((Student)obj).setName("张三")*/
setName.invoke(obj,"张三");
Field类的常用方法
方法名称 | 说明 |
---|---|
getName() | 获得该成员变量的名称 |
getType() | 获取表示该成员变量的 Class 对象 |
get(Object obj) | 获得指定对象 obj 中成员变量的值,返回值为 Object 类型 |
set(Object obj, Object value) | 将指定对象 obj 中成员变量的值设置为 value |
getlnt(0bject obj) | 获得指定对象 obj 中成员类型为 int 的成员变量的值 |
setlnt(0bject obj, int i) | 将指定对象 obj 中成员变量的值设置为 i |
setFloat(Object obj, float f) | 将指定对象 obj 中成员变量的值设置为 f |
getBoolean(Object obj) | 获得指定对象 obj 中成员类型为 boolean 的成员变量的值 |
setBoolean(Object obj, boolean b) | 将指定对象 obj 中成员变量的值设置为 b |
getFloat(Object obj) | 获得指定对象 obj 中成员类型为 float 的成员变量的值 |
setAccessible(boolean flag) | 此方法可以设置是否忽略权限直接访问 private 等私有权限的成员变量 |
getModifiers() | 获得可以解析出该方法所采用修饰符的整数 |
示例代码:
//得到本类所有属性并遍历输出
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
//得到指定名称的属性
Field age = cls.getDeclaredField("age");
//此方法属性的访问修饰符,不推荐使用,会破坏类的封装性,
age.setAccessible(true);
//为obj的age属性赋值,常规写法,为:((Student)obj).age=18;age为私有的,无法调用
age.set(obj,18);
那么反射到底有什么用?
反射最主要还是运用在框架的设计中,了解了反射才更好的了解一些框架功能实现的原理。
我们所学的springmvc中的某些功能使用到的反射:
- 页面向controller中传值,的参数自动映射
他的下一个博客
使用反射自定义一个简易的参数自动映射. - DispatchServlet(前端控制器)
他的下一个博客
使用反射自定义一个简易DispatchServlet
Java 反射机制的优缺点
优点:
- 能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。
- 与 Java 动态编译相结合,可以实现无比强大的功能。
- 对于 Java 这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
缺点:
- 一个主要的缺点是对性能有影响,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
- 反射调用方法时可以忽略权限检查,获取这个类的私有方法和属性,因此可能会破坏类的封装性而导致安全问题。
总结:
使用java的反射机制,一般需要遵循三步:
- 获得你想操作类的Class对象,使用全限定类名,“包名+类名””
- 使用Class对象,得到构造,方法,属性,
- 再使用具体的构造,方法,属性,来操作对应的方法,