反射
通过java的反射机制,可以在程序中访问已经加载到JVM中的java对象的描述,实现访问、检测、和修改描述java对象本身信息的功能,它是字java.lang.reflect包下的,并且想要调用访问信息的方法,就要得到class类的对象。
反射可访问的主要描述信息
组成部分 | 访问方法 | 返回值类型 | 说明 |
包路径 | getPackage() | Package对象 | 获得该类的存放路径 |
类名称 | getName() | String对象 | 获得该类的名称 |
继承类 | getSuperclass() | Class对象 | 获得该类继承的类 |
实现接口 | getInterfaces() | Class型数组 | 获得该类所实现的所有接口 |
构造方法 | getConstructors() | Constructor型数组 | 获得所有权限为public的构造方法 |
getConstructor(Class<?>...parameterTypes) | Constructor对象 | 获得权限为public的指定构造方法 | |
getDeclaredConstructors() | Constructor型数组 | 获得所有构造方法,按声明顺序返回 | |
getDeclaredConstructors(Class<?>...parameterTypes) | Constructor对象 | 获取指定构造方法 | |
方法 | getMethods() | Method型数组 | 获得所有权限为public的方法 |
getMethod(String name,Class<?>...parameterTypes) | Method对象 | 获得权限为public的指定方法 | |
getDeclaredMethods() | Method型数组 | 获得所有方法,按照声明顺序返回 | |
getDeclaredMethods(String name,Class<?>...parameterTypes) | Method对象 | 获得指定方法 | |
成员变量 | getFileds() | Filed型数组 | 获得所有权限为public的成员变量 |
getFiled(String name) | Filed对象 | 获得权限为public的指定成员变量 | |
getDeclaredFileds() | Filed型数组 | 获得所有的成员变量,按照声明顺序返回 | |
getDeclaredFiled(String name) | Filed对象 | 获得指定的成员变量 | |
内部类 | getClasses() | Class型数组 | 获得所有权限为public的内部类 |
getDeclaredClasses() | Class型数组 | 获得所有内部类 | |
内部类的声明 | getDeclaringClass() | Class对象 | 如果该类为内部类,则返回它的成员类,否则返回null |
访问构造方法
组成部分 | 访问方法 | 返回值类型 | 说明 |
构造方法 | getConstructors() | Constructor型数组 | 获得所有权限为public的构造方法 |
getConstructor(Class<?>...parameterTypes) | Constructor对象 | 获得权限为public的指定构造方法 | |
getDeclaredConstructors() | Constructor型数组 | 获得所有构造方法,按声明顺序返回 | |
getDeclaredConstructors(Class<?>...parameterTypes) | Constructor对象 | 获取指定构造方法 |
Constructor常用方法
方法 | 说明 |
isVarArgs() | 查看该构造方法是否允许带有可变数量的参数,如果允许则返回true,否则返回false |
getParameterTypes() | 按照声明顺序以Class数组的形式获得该构造方法的各个参数的类型 |
getException() | 以Class数组的形式获得该构造方法可能抛出的异常类型 |
setAccessible(boolean flag) | 如果该构造方法的权限为private,默认为不允许通过反射利用newInstance(Object...initargs)方法创建对象。如果执行该方法,并将入口参数设为true,则允许创建 |
getModifiers() | 获得可以解析出该构造方法所采用修饰符的整数 |
newInstance(Object...initargs) | 通过该构造方法利用指定参数创建一个该类的对象,如果未设置参数则表示采用默认无参数的构造方法 |
调用getModifiers()返回的对象可以判断该对象是否被某修饰符所修饰,如:isPublic(方法返回的对象),可以判断出是否被public所修饰,还有很多类似的方法,我就不一一说明了,常见的修饰符基本上都有哦。
方法演示
创建几个需要访问的构造方法
public class Example1 {
private Example1() {
}
public Example1(String...S) {
System.out.println("可变参数的长度:"+S.length);
}
private Example1(String s,int i) {
}
}
main方法
import java.lang.reflect.Constructor;
public class Example1Test {
public static void main(String[] args) {
Example1 e = new Example1("jack", "rose","jim");
Class<? extends Example1> clazz = e.getClass();
//获得所有构造方法
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
boolean b=true;
while (b){
try {
//是否允许带有可变参数,是为true
System.out.println("是否允许带有可变参数:"+constructor.isVarArgs());
//获取所有参数类型
Class<?>[] types = constructor.getParameterTypes();
System.out.println("参数类型为:");
for (Class<?> type : types) {
System.out.println(type);
}
//获取所有可能出现的异常类型
Class<?>[] exceptions = constructor.getExceptionTypes();
System.out.println("异常类型:");
for (Class<?> exception : exceptions) {
System.out.println(exception);
}
b=false;
}catch (Exception ee){
System.out.println("解决私有构造方法不能访问的问题,需要把setAccessible设置为true");
constructor.setAccessible(true);
}
}
}
}
}
结果
可变参数的长度:3
是否允许带有可变参数:false
参数类型为:
异常类型:
是否允许带有可变参数:true
参数类型为:
class [Ljava.lang.String;
异常类型:
是否允许带有可变参数:false
参数类型为:
class java.lang.String
int
异常类型:
访问方法
组成部分 | 访问方法 | 返回值类型 | 说明 |
方法 | getMethods() | Method型数组 | 获得所有权限为public的方法 |
getMethod(String name,Class<?>...parameterTypes) | Method对象 | 获得权限为public的指定方法 | |
getDeclaredMethods() | Method型数组 | 获得所有方法,按照声明顺序返回 | |
getDeclaredMethods(String name,Class<?>...parameterTypes) | Method对象 | 获得指定方法 |
Method类常用方法
方法 | 说明 |
getName() | 获得该方法的名称 |
getParameterTypes() | 按照声明顺序以Class数组的形式获得该方法的各个参数类型 |
getReturnType() | 以Clss对象的形式获得该方法的返回值的类型 |
getExceptionTypes() | 以Class数组的形式获得该方法可能抛出的异常类型 |
invoke(Object obj,Object...args) | 利用指定的参数args执行指定对象obj中的该方法,返回值为Object类型 |
isVarArgs() | 查看该构造方法是否允许带有可变数量的参数,如果允许则返回true,否则返回false |
getModifiers() | 获得可以解析出该方法所采用修饰符的整数 |
代码演示
创建一些需要访问的方法
public class Example2 {
static void staticMethod(){
System.out.println("执行staticMethod方法");
}
public int publicMethod(int i){
System.out.println("执行publicMethod方法");
return i*100;
}
protected int protectedMethod(String s,int i){
System.out.println("执行protectedMethod方法");
return Integer.parseInt(s)+i;
}
private String privateMethod(String...s){
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < s.length; i++) {
buffer.append(s[i]);
}
return buffer.toString();
}
}
main方法
import java.lang.String;
import java.lang.reflect.Method;
public class Example2Test {
public static void main(String[] args) {
Example2 e = new Example2();
Class<? extends Example2> clazz = e.getClass();
//获得所有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
//获得方法名称
System.out.println("方法名称为:"+method.getName());
//获得参数类型
Class<?>[] types = method.getParameterTypes();
System.out.println("参数类型为:");
for (Class<?> type : types) {
System.out.println(type);
}
//获取方法返回值类型
Class<?> returnType = method.getReturnType();
System.out.println("返回值类型为:"+returnType);
//是否允许可变参数
System.out.println("是否允许可变参数:"+method.isVarArgs());
//判断该方法名字是否一致,如果一致,就调用该方法
boolean b=true;
while (b){
try{
b=false;
if("staticMethod".equals(method.getName())){
//调用方法,这个是无参的方法,因此只要写上方法所在的对象就可以了
method.invoke(e);
}else if("publicMethod".equals(method.getName())){
//调用方法,这个是有参的方法,因此写上方法所在的对象,再写上要传入的参数,根据方法的参数,传递参数哟
System.out.println("返回值:"+method.invoke(e, 10));
}else if("protectedMethod".equals(method.getName())){
System.out.println("返回值:"+method.invoke(e, "7", 100));
}else if("privateMethod".equals(method.getName())){
//当参数为可变参数时,需要把反射调用方法的第二个参数写成二维数组的形式
Object[] obj=new Object[]{new String[]{"oh", "my", "god", "!"}};
System.out.println("返回值:"+method.invoke(e,obj));
}
}catch (Exception ee){
//为了访问私有方法,把该方法设置为true
method.setAccessible(true);
b=true;
}
}
}
}
}
结果
方法名称为:staticMethod
参数类型为:
返回值类型为:void
是否允许可变参数:false
执行staticMethod方法
方法名称为:publicMethod
参数类型为:
int
返回值类型为:int
是否允许可变参数:false
执行publicMethod方法
返回值:1000
方法名称为:protectedMethod
参数类型为:
class java.lang.String
int
返回值类型为:int
是否允许可变参数:false
执行protectedMethod方法
返回值:107
方法名称为:privateMethod
参数类型为:
class [Ljava.lang.String;
返回值类型为:class java.lang.String
是否允许可变参数:true
返回值:ohmygod!
注意:可变参数的方法,在反射调用方法时,是需要把参数装入到二维数组中的。
访问成员变量
组成部分 | 访问方法 | 返回值类型 | 说明 |
成员变量 | getFileds() | Filed型数组 | 获得所有权限为public的成员变量 |
getFiled(String name) | Filed对象 | 获得权限为public的指定成员变量 | |
getDeclaredFileds() | Filed型数组 | 获得所有的成员变量,按照声明顺序返回 | |
getDeclaredFiled(String name) | Filed对象 | 获得指定的成员变量 |
Filed类的常用方法
方法 | 说明 |
getName() | 获得该成员变量的名称 |
getType() | 获得表示该成员变量类型的Class对象 |
get(Object obj) | 获得指定对象obj中成员变量的值,返回值为Object型 |
set(Object obj,Object value) | 将指定对象obj中成员变量的值设置为value |
getInt(Object obj) | 获得指定对象obj中类型为int的成员变量的值 |
setInt(Object obj,int i) | 将指定对象obj中类型为int的常用变量的值设置为i |
getFloat(Object obj) | 获得指定对象obj中类型为float的成员变量的值 |
setFloat(Object obj,float f) | 将指定对象obj中类型为float的常用变量的值设置为f |
getBoolean(Object obj) | 获得指定对象obj中类型为boolean的成员变量的值 |
SetBoolean(Object obj,boolean b) | 将指定对象obj中类型为boolean的常用变量的值设置为b |
setAccessible(boolean flag) | 此方法可以设置是否忽略权限直接访问private等私有权限的成员变量 |
getModifiers() | 获得可以解析出该成员变量所采用的修饰符的整数 |
代码演示
声明一些需要访问的成员变量
public class Example3 {
int i;
float f;
protected boolean b;
private String s;
}
main方法
import java.lang.reflect.Field;
public class Example3Test {
public static void main(String[] args) {
Example3 e = new Example3();
//获得class对象,这样可以访问需要的信息
Class clazz = e.getClass();
//获得所有成员变量
Field[] fields = clazz.getDeclaredFields();
//遍历成员变量数组
for (Field field : fields) {
//获得成员变量的名称
System.out.print("名称为:"+field.getName()+" ");
//获得成员变量的类型
Class type = field.getType();
System.out.println("类型为:"+type);
boolean flag=true;
while (flag){
try {
flag=false;
//获得没有修改前的成员变量值
System.out.println("获得修改前的值"+field.get(e));
//判断为什么类型的成员变量,并为其赋值
if(type.equals(int.class)){
//为该成员变量赋值
field.set(e,20);
}else if(type.equals(float.class)){
field.set(e,199.5f);
}else if(type.equals(boolean.class)){
field.set(e,true);
}else {
field.set(e,"idea");
}
System.out.println("获得修改过后的值:"+field.get(e));
}catch (Exception ee){
System.out.println("抛出私有成员变量不可访问异常,把setAccessible设置为true就可以访问了");
field.setAccessible(true);
flag=true;
}
}
}
}
}
结果
名称为:i 类型为:int
获得修改前的值0
获得修改过后的值:20
名称为:f 类型为:float
获得修改前的值0.0
获得修改过后的值:199.5
名称为:b 类型为:boolean
获得修改前的值false
获得修改过后的值:true
名称为:s 类型为:class java.lang.String
抛出私有成员变量不可访问异常,把setAccessible设置为true就可以访问了
获得修改前的值null
获得修改过后的值:idea
总结
通过反射访问,方法、构造方法、成员变量,你可以发现这与普通调用之间的区别,区别在于反射调用,只要设置setAccessible,就可以访问私有的成员,而普通的调用是不可以访问的,并且普通的调用不能够看到成员的信息,而反射调用可以。