前沿:
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);
}
}
输出结果如下:
反序列化执行命令(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);
}
}
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.
通过反射执行命令
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");
}
}
通过反射调用方法
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