您的位置 首页 java

Java反射、序列化及命令执行

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,第二段代码是运行时传入的类。

总结下:

  1. 在运行时加载,探知和使用编译期间完全未知的类。
  2. 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已经加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用他的任意一个方法和属性。
  3. 加载完类之后,在堆内存中会产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息,而且这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以被称之为:反射。
  4. 每个类被加载进入内存之后,系统就会为该类生成一个对应的java.lang.Class对象,通过该Class对象就可以访问到JVM中的这个类。

Java反射对象的获取方法

  1. 实例对象的getClass()方法;
  2. 类的.class(最安全/性能最好)属性;
  3. 运用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("#34;)});
    //要反射调用的类名
        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();
        }
    }
}  

文章来源:智云一二三科技

文章标题:Java反射、序列化及命令执行

文章地址:https://www.zhihuclub.com/175602.shtml

关于作者: 智云科技

热门文章

网站地图