前提代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class Person { public String name; private int age;
public Person(String name, int age) { this.name = name; this.age = age; } public Person(int age){ this.age = age; }
public void Say(){ System.out.println(this.name+": My name is " + name + " and I am " + age + " years old."); }
private void Say(String message ){ System.out.println(this.name+": "+message); } }
|
反射
直接创建类的方法
1 2 3
| Person person = new Person(1); person.setName("Jane"); person.Say();
|
反射创建
使用的场景:
在程序运行的时候动态的加载类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method;
public class Main { public static void main(String[] args) throws Exception { Class c = Class.forName("Person");
Constructor cs=c.getDeclaredConstructor(String.class,int.class); Object o2=cs.newInstance("张三",20);
Method publicMethod=c.getMethod("Say"); publicMethod.invoke(o2); Method privateMethod=c.getDeclaredMethod("Say",String.class); privateMethod.setAccessible(true); privateMethod.invoke(o2,"Hello");
for (Field field:c.getFields()){ System.out.println("public 成员变量:"+field.getName()); } for (Field field:c.getDeclaredFields()){ System.out.println("成员变量:"+field.getName()); } Field publicField=c.getField("name"); publicField.set(o2,"李四"); publicMethod.invoke(o2); Field privateField=c.getDeclaredField("age"); privateField.setAccessible(true); privateField.set(o2,30); publicMethod.invoke(o2);
} }
|
当需要调用private 的函数或者是需要修改private 的变量的时候
需要使用含有declared 的函数
还需要设置对应的setAccessible 为true
1 2
| Field privateField=c.getDeclaredField("age") privateField.setAccessible(true)
|
反序列化
为什么需要序列化
由于代码都是运行在内存,当发生了特殊情况的时候,内存中的数据就没有了
如果内存中存放的是用户的session 类的话
当发生了意外那么所有的session 信息都没有
这个时候就可以存储一些数据到数据库或者是文件中
总的来说,序列化的作用就是为了存放对象的数据,或者是用于传输
一个简单的序列化demo
将一个对象保存成文件
Main.java
1 2 3 4 5 6 7 8 9 10 11
| public class Main { public static void main(String[] args) throws Exception { SerializationClass serializationClass=new SerializationClass("张三",20); Serialization.SerializationToFile("test.bin",serializationClass); Object o=Serialization.UnserializationFromFile("test.bin"); SerializationClass s=(SerializationClass) o; s.name="李四"; s.Say(); } }
|
SerializationClass.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import java.io.Serializable;
public class SerializationClass implements Serializable { public String name; private int age;
public SerializationClass(String name, int age) { this.name = name; this.age = age; } public SerializationClass(int age){ this.age = age; }
public void Say(){ System.out.println(this.name+": My name is " + name + " and I am " + age + " years old."); }
private void Say(String message ){ System.out.println(this.name+": "+message); }
public void setName(String name){ this.name = name; } }
|
简介
在Java中实现对象反序列化非常简单,实现java.io.Serializable(内部序列化)
或java.io.Externalizable(外部序列化)
接口即可被序列化,其中java.io.Externalizable
接口只是实现了java.io.Serializable
接口。
反序列化类对象时有如下限制:
被反序列化的类必须存在。
serialVersionUID
值必须一致。
在Java编程中,serialVersionUID是一个用于序列化的类的版本号。它作为Serializable接口的一部分,确保了序列化和反序列化过程中类的版本一致性。当一个类对象需要通过网络发送或在文件系统中存储时,Java序列化机制通过比较serialVersionUID来确认发送和接收的是同一版本的类对象。
并且反序列化不会直接调用构造函数,反序列化创建对象的时候使用的是sun.reflect.ReflectionFactory.newConstructorForSerialization
创建了一个反序列化专用的Constructor(反射构造方法对象)
使用这个特殊的Constructor
可以绕过构造方法创建类实例(前面章节讲sun.misc.Unsafe
的时候我们提到了使用allocateInstance
方法也可以实现绕过构造方法创建类实例)。
自定义序列化和反序列化
实现了java.io.Serializable
接口的类,还可以定义如下方法(反序列化魔术方法
),这些方法将会在类序列化或反序列化过程中调用:
private void writeObject(ObjectOutputStream oos)
,自定义序列化。
private void readObject(ObjectInputStream ois)
,自定义反序列化。
private void readObjectNoData()
。
protected Object writeReplace()
,写入时替换对象。
protected Object readResolve()
。
当我们对DeserializationTest
类进行序列化操作时,会自动调用(反射调用)该类的writeObject(ObjectOutputStream oos)
方法,对其进行反序列化操作时也会自动调用该类的readObject(ObjectInputStream)
方法,也就是说我们可以通过在待序列化或反序列化的类中定义readObject
和writeObject
方法,来实现自定义的序列化和反序列化操作,当然前提是,被序列化的类必须有此方法,并且方法的修饰符必须是private
。
类加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package org.example;
import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;
public class Main { public static void main(String[] args) throws IOException { String classPath = "/Users/august/code/java/JavaSecLearn/src/main/java/fun/au9u5t/User.class"; File file = new File(classPath); byte[] classData = new byte[(int) file.length()]; try { java.io.FileInputStream fileInputStream = new java.io.FileInputStream(file); fileInputStream.read(classData); fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } MyClassLoader myClassLoader = new MyClassLoader(); myClassLoader.setClassData(classData); try { Class userClass = myClassLoader.loadClass("fun.au9u5t.User"); Constructor<?> constructor = userClass.getConstructor(String.class, int.class); Object user = constructor.newInstance("hhhhh", 20); System.out.println(user); Method method = userClass.getMethod("Say"); method.invoke(user);
} catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } System.out.println("fun.au9u5t.User 加载成功");
} }
|