java序列化操作函数

实现Serializable接口

ObjectOutputStream类的writeObject(Object obj)方法,将对象序列化成字符串数据
ObjectInputStream类的readObject(Object obj)方法,将字符串数据反序列化成对象

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Person implements Serializable {   
private String name;   
private int age;   //省略构造方法,get及set方法   
private void writeObject(ObjectOutputStream out) throws IOException {       //将名字反转写入二进制流       
out.writeObject(new StringBuffer(this.name).reverse());       
out.writeInt(age);   
}   
private void readObject(ObjectInputStream ins) throws IOException,ClassNotFoundException{ 
//将读出的字符串反转恢复回来       
this.name = ((StringBuffer)ins.readObject()).reverse().toString();       
this.age = ins.readInt();   
}
}

更彻底的自定义反序列化

writeReplace:在序列化时,会先调用此方法,再调用writeObject方法。此方法可将任意对象代替目标序列化对象

readResolve:反序列化时替换反序列化出的对象,反序列化出来的对象被立即丢弃。此方法在readeObject后调用。

Externalizable:强制自定义序列化

通过实现Externalizable接口,必须实现writeExternal、readExternal方法。

java反射简介

先看在java中执行系统命令的方法

1
2
3
4
5
public class ExecTest {
public static void main(String[] args) throws Exception{
Runtime.getRuntime().exec("notepad.exe");
}
}

该代码会运行并打开windows下的记事本
它正常的步骤是

1
2
3
4
5
6
public class ExecTest {
public static void main(String[] args) throws Exception{
Runtime runtime = Runtime.getRuntime();
runtime.exec("notepad.exe");
}
}

Java反射之 getMethod() 与invoke的使用

那么相应的反射的代码如下

ps: Method.invoke

1
2
3
4
5
6
7
8
9
10
11
import java.lang.reflect.Method;

public class ExecTest {
public static void main(String[] args) throws Exception{
Object runtime = Class.forName("java.lang.Runtime").getMethod("getRuntime", new Class[]{}).invoke(null);
//System.out.println(runtime.getClass().getName());
Class.forName("java.lang.Runtime").getMethod("exec",String.class).invoke(runtime,"notepad.exe");
}
}
getMethod(方法名, 方法类型) getMethod方法根据方法名称和相关参数,来定位需要查找的Method对象并返回。
invoke(某个对象实例, 传入参数) 作用:调用包装在当前Method对象中的方法。

这里第一句Object runtime =Class.forName("java.lang.Runtime")的作用

​ 等价于 Object runtime = Runtime.getRuntime()
目的是获取一个对象实例好被下一个invoke调用

第二句Class.forName("java.lang.Runtime").xxxx的作用就是调用上一步生成的runtime实例的exec方法,并将"notepad.exe"参数传入exec()方法

getMethod方法

Method Class.getMethod(String name, Class<?>… parameterTypes)的作用是获得对象所声明的公开方法
该方法的第一个参数name是要获得方法的名字,第二个参数parameterTypes是按声明顺序标识该方法形参类型。
person.getClass().getMethod(“Speak”, null);
//获得person对象的Speak方法,因为Speak方法没有形参,所以parameterTypes为null
person.getClass().getMethod(“run”, String.class);
//获得person对象的run方法,因为run方法的形参是String类型的,所以parameterTypes为String.class

Transformer接口: 将一个类转换为另一个类,其中的方法是transform

这是apache发布的一个组件包中的方法 下载地址:https://mvnrepository.com/artifact/commons-collections/commons-collections/3.1

有如下几个类使用了Transformer接口,分别是ConstantTransformerinvokerTransformerChainedTransformerTransformedMap

ConstantTransformer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ConstantTransformer implements Transformer, Serializable {
static final long serialVersionUID = 6374440726369055124L;
public static final Transformer NULL_INSTANCE = new ConstantTransformer((Object)null);
private final Object iConstant;

public static Transformer getInstance(Object constantToReturn) {
return (Transformer)(constantToReturn == null ? NULL_INSTANCE : new ConstantTransformer(constantToReturn));
}

public ConstantTransformer(Object constantToReturn) {
this.iConstant = constantToReturn;
}

public Object transform(Object input) {
return this.iConstant; //
}

public Object getConstant() {
return this.iConstant;
}
}

可以看出transform返回的是iConstant的变量,iConstant的变量必定在ConstantTransformer(Object)方法中被赋值。

image-20200621094128902

InvokerTransformer 获得runtime的Method对象

通过反射创建一个新的对象实例

构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Constructor that performs no validation.
* Use <code>getInstance</code> if you want that.
*
* @param methodName the method to call
* @param paramTypes the constructor parameter types, not cloned
* @param args the constructor arguments, not cloned
*/
public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
super();
iMethodName = methodName;
iParamTypes = paramTypes;
iArgs = args;
}

其中的transform方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Transforms the input to result by invoking a method on the input.
*
* @param input the input object to transform
* @return the transformed result, null if null input
*/
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);
}
}

iMethodName, iParamTypes通过构造函数传入,transform中通过反射的方法,的到了这个方法(iMethodName)的对象,最后返回Method对象

使用举例,根据上述源码构造一个对象,并且调用transform对象:

1
2
3
4
5
6
InvokerTransformer tran = new InvokerTransformer(
"getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime",null}
);
System.out.println(tran.transform(Runtime.class).toString());

image-20200621103953295

说一下transform方法中这三行的意思:

cls变量获取到的是传递进来的input的对象值,此处input传递的是Runtime的对象

image-20200621104728560

,下面两行代码要反射RuntimegetRuntime方法,iMethodName表示要得到的方法名称iParamTypes表示方法中所使用的参数类型的数组

此处的iMethodName需要Mehtod对象,因此此处是getMethod(构造函数时设置的,getMethod能返回Method对象)

image-20200621104831653

,因此iParamTypes对应的是getMethod对象的参数类型集合,getMethod方法文档如下图所示:

image.png

通过查阅官方文档,我们知道了参数应该是String.classClass[].class

继续往下执行invoke方法,因为是反射getRuntime()方法,参数为空,所以iArgs的值可以为空,回到主程序代码可以发现为null,如下图所示:

image-20200621104118933

执行完毕后:

image-20200621104219254

成功的反射出了Runtime.getRuntime()方法,然而如果要执行任意代码的化,还需要有exec代码段,全部应该是Runtime.getRuntime().exec(“calc.exe”)

进一步利用获得的getRuntime构造exec执行代码

此时我们已经获得了getRuntime()的Method对象,如果要执行exec(“calc.exe”),我们还需要进行一次invoke反射的过程,因此我们根据上面构造出下面的代码段,如下图:

image-20200622024948918

上图中,构造出tran2的方法,配置invoke的参数都为null,利用tran2.transform(run),反射invoke方法,过程与上文中一样,此处直接看输出了:

image-20200622035141662

此处已经是Runtime类了,继续构造exec(“calc.exe”)代码段,如下图所示:

快速浏览

获取类对象

获取方法

method.invoke(类,参数)

getMethod(方法名, 方法类型) getMethod方法根据方法名称和相关参数,来定位需要查找的Method对象并返回。
invoke(某个对象实例, 传入参数) 作用:调用包装在当前Method对象中的方法。

正常流程: Runtime.getRuntime.exec(“calc.exe”);

反射流程:

  1. 需要 从Runtime类中搜索匹配getRuntime方法的名称及参数类型来找到getRuntime方法 并通过getMethod方法获取到getRuntime方法

    1
    2
    3
    4
    Class runClass = Runtime.getClass();  // 获取Runtime类 <==> Class.forName("java.lang.Runtime")

    Method getRuntime = runClass.getMethod("getRuntime",getRuntime所需要的参数类型); //通过`getMethod`方法获取到`getRuntime`方法

java反射机制

Java反射之 getMethod() 与invoke的使用

https://blog.csdn.net/qq_27446553/article/details/78325204

https://juejin.im/post/5ce3cdc8e51d45777b1a3cdf

https://paper.seebug.org/792/