Java反射
定义
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
反射应该是 JVM读取相应类的字节码文件,而不是.java
性能问题
反射的性能很慢,和直接调用方法相比慢了30倍
如果关闭安全检查(setAccessible(true))也会慢5倍左右
简单地说
- Java 反射,就是在运行状态中。
- 获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等
- 获取任意对象的属性,并且能改变对象的属性
- 调用任意对象的方法
- 判断任意一个对象所属的类
- 实例化任意一个类的对象
反射机制的相关类
与Java反射相关的类如下:
类名 | 用途 |
---|---|
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量(成员变量也称为类的属性) |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
Class类
Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。
获得类相关的方法
方法 | 用途 |
---|---|
asSubclass(Class clazz) | 把传递的类的对象转换成代表其子类的对象 |
Cast | 把对象转换成代表类或是接口的对象 |
getClassLoader() | 获得类的加载器 |
getClasses() | 返回一个数组,数组中包含该类中所有公共类和接口类的对象 |
getDeclaredClasses() | 返回一个数组,数组中包含该类中所有类和接口类的对象 |
forName(String className) | 根据类名返回类的对象 |
getName() | 获得类的完整路径名字 |
newInstance() | 创建类的实例 |
getPackage() | 获得类的包 |
getSimpleName() | 获得类的名字 |
getSuperclass() | 获得当前类继承的父类的名字 |
getInterfaces() | 获得当前类实现的类或是接口 |
String path="reflection.bean.User";
/**
*User类中包含id,username和password属性
*/
Class clazz=Class.forName(path);
//获取全类名
System.out.println(clazz.getName());
//获取类名
System.out.println(clazz.getSimpleName());
结果
reflection.bean.User
User
获得类中属性相关的方法
方法 | 用途 |
---|---|
getField(String name) | 获得某个公有的属性对象 |
getFields() | 获得所有公有的属性对象 |
getDeclaredField(String name) | 获得某个属性对象 |
getDeclaredFields() | 获得所有属性对象 |
获得类中注解相关的方法
方法 | 用途 |
---|---|
getAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的公有注解对象 |
getAnnotations() | 返回该类所有的公有注解对象 |
getDeclaredAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的所有注解对象 |
getDeclaredAnnotations() | 返回该类所有的注解对象 |
/**
* 获取属性
*/
Field[] clazzs=clazz.getFields();
//只能获得public的,否则里面为空
System.out.println(clazzs.length);
//获取private的可以
clazzs=clazz.getDeclaredFields();
System.out.println(clazzs.length);
//获取单个属性
Field f=clazz.getDeclaredField("username");
System.out.println("获取单个属性"+f);
for (Field field : clazzs) {
System.out.println("属性"+field);
}
结果
获取单个属性private java.lang.String reflection.bean.User.username
属性private int reflection.bean.User.id
属性private java.lang.String reflection.bean.User.username
属性private java.lang.String reflection.bean.User.password
获得类中构造器相关的方法
方法 | 用途 |
---|---|
getConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class…<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
/**
* 构造器(和属性、方法差不多)
* 有参数时加上参数的class,无参数则填入null
*/
Constructor[] constructors=clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println("构造器"+constructor);
}
结果
构造器public reflection.bean.User(int,java.lang.String,java.lang.String)
构造器public reflection.bean.User()
获得类中方法相关的方法
方法 | 用途 |
---|---|
getMethod(String name, Class…<?> parameterTypes) | 获得该类某个公有的方法 |
getMethods() | 获得该类所有公有的方法 |
getDeclaredMethod(String name, Class…<?> parameterTypes) | 获得该类某个方法 |
getDeclaredMethods() | 获得该类所有方法 |
/**
* 获取方法(和Field一样方法,只不过替换一下单词)
*/
Method[] methods=clazz.getDeclaredMethods();
System.out.println("获取所有method方法");
for (Method method : methods) {
System.out.println("方法"+method);
}
//获取单个方法,无参数则只写方法名和null,有参数要添加参数的class
Method m1=clazz.getDeclaredMethod("getUsername",null);
/**
* 因为java中可以重载同名不同参的方法,为了确定对应的方法,应该选择性的添加参数的class
*/
Method m2=clazz.getDeclaredMethod("setUsername", String.class);
System.out.println("m1=>"+m1+"\nm2=>"+m2);
结果
获取所有method方法
方法public java.lang.String reflection.bean.User.toString()
方法public int reflection.bean.User.getId()
方法public java.lang.String reflection.bean.User.getUsername()
方法public void reflection.bean.User.setUsername(java.lang.String)
方法public void reflection.bean.User.setPassword(java.lang.String)
方法public void reflection.bean.User.setId(int)
方法public java.lang.String reflection.bean.User.getPassword()
m1=>public java.lang.String reflection.bean.User.getUsername()
m2=>public void reflection.bean.User.setUsername(java.lang.String)
类中其他重要的方法
方法 | 用途 |
---|---|
isAnnotation() | 如果是注解类型则返回true |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果是指定类型注解类型则返回true |
isAnonymousClass() | 如果是匿名类则返回true |
isArray() | 如果是一个数组类则返回true |
isEnum() | 如果是枚举类则返回true |
isInstance(Object obj) | 如果obj是该类的实例则返回true |
isInterface() | 如果是接口类则返回true |
isLocalClass() | 如果是局部类则返回true |
isMemberClass() | 如果是内部类则返回true |
Field类
Field代表类的成员变量(成员变量也称为类的属性)
方法 | 用途 |
---|---|
equals(Object obj) | 属性与obj相等则返回true |
get(Object obj) | 获得obj中对应的属性值 |
set(Object obj, Object value) | 设置obj中对应属性值 |
/**
* 通过反射API调用属性
*/
Field field=clazz.getDeclaredField("password");
//告诉程序field不用作安全检查了(即可访问到private属性进行修改)
field.setAccessible(true);
field.set(user,"Field");
System.out.println("field修改的password=>"+field.get(user));
结果
field中Field
Method类
Method代表类的方法
方法 | 用途 |
---|---|
invoke(Object obj, Object… args) | 传递object对象及参数调用该对象对应的方法 |
//反射API调用方法
Method methodSet=clazz.getDeclaredMethod("setUsername", String.class);
//前者表示对象,后者表示参数(可以不写)
methodSet.invoke(user,"啦啦啦");
Method methodGet=clazz.getDeclaredMethod("getUsername");
System.out.println("通过反射set和get到username是"+methodGet.invoke(user));
结果
通过反射set和get到username是啦啦啦
Constructor类
Constructor代表类的构造方法
方法 | 用途 |
---|---|
newInstance(Object… initargs) | 根据传递的参数创建类的对象 |
/**
* 通过反射API调用构造方法、构造对象
* Class clazz= Class.forName(path); 如果不强转(User)的话,需要改为Class<User> clazz=(Class<User>) Class.forName(path)
* User user=(User) clazz.newInstance();
*/
//实际上默认调用了无参构造器方法,所以平时写javabean时最好添加上无参构造器
User user= (User) clazz.newInstance();
System.out.println(user);
Constructor constructor=clazz.getDeclaredConstructor(int.class,String.class,String.class);
User u1= (User) constructor.newInstance(1,"if","123");
System.out.println("u1->"+u1);
结果
构造器public reflection.bean.User(int,java.lang.String,java.lang.String)
构造器public reflection.bean.User()
User{id=0, username=‘null’, password=‘null’}
u1->User{id=1, username=‘if’, password=‘123’}
既然反射可以拿到private里的值,那private的意义在哪里?
- Java的private修饰符不是为了绝对安全设计的,而是对用户常规使用Java的一种约束。就好比饭店厨房门口挂着“闲人免进”的牌子,但是你还是能够通过其他方法进去。
- private的意义是OOP(面向对象编程)的封装概念。
- setAccessible(true),这行代码取消了java的权限控制检查,会产生安全隐患
总结
获取Class类的对象:forName()、类名.Class、对象.getClass()
源代码阶段[forName()]–>Class对象阶段(内存)[类名.Class]–>Runtime阶段[对象.getClass()]
.java文件–编译–>Class文件–类加载器–>Class对象–创建对象–>new JavaBean()
要的一些类及其方法,更多信息和用法需要近一步的阅读Google提供的相关文档和示例。