前沿:

  • java序列化是指把java对象转化为字节序列的过程。

    • ObjectOutputStream类的 writeObject() 方法可以实现序列化。
  • java反序列化是指将字节序列恢复为java对象的过程。

    • ObjectInputStream 类的 readObject() 方法用于反序列化。

java序列化和反序列化有利于java实现多平台之间的通信,对象的持久化存储。
主要用于以下场景:

  • HTTP

    • 平台之间的通信
  • RMI

    • java远程方法调用,RMI的传输100%基于反序列化进行的
  • JMX

    • JMX 是一套标准的代理和服务,用户可以在任何 Java 应用程序中使用这些代理和服务实现管理,WebLogic 的管理页面和JBoss 整个系统都基于 JMX 构架。

只有实现java.io.Serializable接口才可以被反序列化。并且所有属性必须是可反序列化的。(用transient关键字修饰的属性除外,不参与序列化过程)

序列化实现

注: 在我们对一个类进行序列化时,这个类必须实现Serializable接口,也就是implements Serializable,否则会报错。
chushilie.java(要实现序列化的类)

package test;

import java.io.Serializable;

public class chushilie implements Serializable{
    private String name;
    private  int age;

    public chushilie(String name,int age){
        this.name = name;
        this.age = age;
        System.out.println("success");
    }
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Main.java(序列化利用ObjectOutputStream的writeObject类)

package test;

import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception{
        chushilie person = new chushilie("NNULLULL",21);
        // 打开一个文件输出流
        FileOutputStream fout= new FileOutputStream("test.txt");
        // 建立对象输入流
        ObjectOutputStream oos = new ObjectOutputStream(fout);
        //输出反序列化对象
        oos.writeObject(person);
        oos.close();
    }
}

最终会输出success;
并且会在当前目录下生成test.txt。
写到文件的便是该对象序列化后的二进制数据。

反序列化实现

反序列化需要利用ObjectInputStream中的readObject()方法。
稍微改下main.java即可。

package test;

import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception{
        // 打开一个文件输入流
        FileInputStream fout = new FileInputStream("test.txt");
        // 反序列化流,将之前使用 ObjectOutputStream 序列化的原始数据恢复为对象,以流的方式读取对象。 
        ObjectInputStream ois = new ObjectInputStream(fout);
        // 读取对象
        chushilie p = (chushilie) ois.readObject();
        System.out.println(p);
    }

}

输出结果如下:
image

反序列化执行命令(Readobject重写)

  • readObject()方法被重写的的话,反序列化该类时调用便是重写后的readObject()方法。如果该方法书写不当的话就有可能引发恶意代码的执行
    chushilie.java
package test;

import java.io.Serializable;

public class chushilie implements Serializable{
    public String cmd;
    //重写方法
    private void readObject(java.io.ObjectInputStream stream) throws Exception {
        // 执行默认的ReadObject()方法
        stream.defaultReadObject();
        //执行命令
        Runtime.getRuntime().exec(cmd);
    }
}

Main.java

package test;

import java.io.*;

public class Main {
    public static void main(String[] args) throws Exception{
        chushilie eval = new chushilie();
        eval.cmd = "open /System/Applications/Calculator.app";
        FileOutputStream fout= new FileOutputStream("test.txt");
        // 建立对象输入流
        ObjectOutputStream oos = new ObjectOutputStream(fout);
        //输出反序列化对象
        oos.writeObject(eval);
        oos.close();
        FileInputStream fout1 = new FileInputStream("test.txt");
        // 反序列化流,将之前使用 ObjectOutputStream 序列化的原始数据恢复为对象,以流的方式读取对象。
        ObjectInputStream ois = new ObjectInputStream(fout1);
        // 读取对象
        chushilie p = (chushilie) ois.readObject();
        System.out.println(p);
    }

}

image

java反射

注:反射这里基本上参考的是l3yx师傅的

  • 对于任意的一个类,都可以得到这个类的所有属性和方法,对于任意的一个对象,都可以调用他的任意方法和属性。
  • 动态获取信息和动态调用对象的方法,都称为java的反射机制。

在反射中获取该类的方法有三种:

  • class.forName("包名.类名")

    • 静态方法,同样可以用来加载类。
  • User.class

    • 通过User类的class属性获取
  • new User().getClass()

    • 通过对象实例的getClass()方法获取。

获取/执行方法

获取某个Class对象的方法集合,主要有以下几个方法:

  • getDeclaredMethods方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

    • public Method[] getDeclaredMethods() throws SecurityException
  • getMethods方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。

    • public Method[] getMethods() throws SecurityException
  • getMethod 方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。

    • public Method getMethod(String name, Class<?>... parameterTypes)

执行方法:
执行方法,是依靠invoke作用的。

public Object invoke(Object obj, Object... args)
       throws IllegalAccessException, IllegalArgumentException,
          InvocationTargetException

第一个参数需要注意:

  • 如果这个方法是一个普通方法,那么第一个参数是实例对象。
  • 如果这个方法是一个静态方法,那么第一个参数是类。

利用类对象创建对象

User类

package test;

public class User {
    private String name;

    public User(String name) {
        this.name=name;
    }
    public void setName(String name) {
        this.name=name;
    }
    public String getName() {
        return name;
    }
}

CreateObject类

package test;

import java.lang.reflect.*;

public class CreateObject {
    public static void main(String[] args) throws Exception {
        // 获取User类对象
        Class UserClass= Class.forName("test.User");
        // Class UserClass=User.class;
        // 获得该类中与参数类型匹配的公有构造方法
        Constructor constructor=UserClass.getConstructor(String.class);
        User user=(User) constructor.newInstance("test");

        System.out.println(user.getName());
    }
}

输出结果为test.
image

通过反射执行命令

package test;

public class Exec {
    public static void main(String[] args) throws Exception {
        Class runtimeClass=Class.forName("java.lang.Runtime");
        // Class runtimeClass=User.class;
        Object runtime=runtimeClass.getMethod("getRuntime").invoke(null);// getRuntime是静态方法,invoke时不需要传入对象
        runtimeClass.getMethod("exec", String.class).invoke(runtime,"open /System/Applications/Calculator.app");
    }
}

image

通过反射调用方法

package test;

import java.lang.reflect.*;

public class CallMethod {
    public static void main(String[] args) throws Exception {
        Class UserClass=Class.forName("test.User");
        // Class UserClass=User.class;
        Constructor constructor=UserClass.getConstructor(String.class);
        User user=(User) constructor.newInstance("test");

        Method method = UserClass.getDeclaredMethod("setName", String.class);
        method.invoke(user, "test");

        System.out.println(user.getName());
    }
}

通过反射访问属性

package test;

import java.lang.reflect.*;

public class AccessAttribute {
    public static void main(String[] args) throws Exception {
        Class UserClass=Class.forName("reflection.User");
         // Class UserClass=User.class;
        Constructor constructor=UserClass.getConstructor(String.class);
        User user=(User) constructor.newInstance("test");

        Field field= UserClass.getDeclaredField("name");
        field.setAccessible(true);// name是私有属性,需要先设置可访问
        field.set(user, "test");

        System.out.println(user.getName());
    }

}

参考文章:
https://paper.seebug.org/312/#3
https://xz.aliyun.com/t/6787#toc-1
https://www.secpulse.com/archives/137940.html

最后修改:2020 年 11 月 24 日 12 : 11 PM
如果觉得我的文章对你有用,请随意赞赏