轻松理解Java序列化 反序列化、反射、动态代理、注解

序列化

把Java对象转换为字节序列的过程

对象序列化的最主要的用处就是在传递和保存对象的时候,保证对象的完整性和可传递性。序列化是把对象转换成有序字节流,以便在网络上传输或者保存在本地文件中。核心作用是对象状态的保存与重建。

反序列化

把字节序列恢复为Java对象的过程

客户端从文件中或网络上获得序列化后的对象字节流,根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象

实现Serializable接口的类支持序列化操作

eg:

首先准备一个JavaBean类

public class Student implements Serializable {
    private String name;
    private int age;
    private String gender;

    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getGender() {
        return gender;
    }

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

    public void setAge(int age) {
        this.age = age;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}

实现序列化 

import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;


public class ObjectOutputStreamDemo {

     public static void main(String[] args) {
        // 创建一个Student对象
        Student student = new Student("Tom", 18, "male");

        // 序列化对象
        try {
            FileOutputStream fos = new FileOutputStream("student.ser");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(student);
            oos.close();
            fos.close();
            System.out.println("Student对象已经被序列化到student.ser文件中");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 反序列化对象
        try {
            FileInputStream fis = new FileInputStream("student.ser");
            ObjectInputStream ois = new ObjectInputStream(fis);
            Student newStudent = (Student) ois.readObject();
            ois.close();
            fis.close();
            System.out.println("从student.ser文件中反序列化出来的Student对象:");
            System.out.println("Name: " + newStudent.getName());
            System.out.println("Age: " + newStudent.getAge());
            System.out.println("Gender: " + newStudent.getGender());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

这个例子中,我们创建了一个Student类,并实现了Serializable接口。然后我们创建了一个Student对象,将其序列化到一个文件中,然后再从文件中反序列化出来一个新的Student对象,并输出其属性。注意,序列化和反序列化对象时,都需要使用ObjectOutputStream和ObjectInputStream类来进行操作。


反射

反射允许对成员变量,成员方法和构造方法的信息进行编程访问

通俗一点就是可以通过写代码来知道我们用代码写的类有哪些成员变量,成员方法和构造方法

1.获取class对象

  • 最常用的

Class class = Class.getName("全类名");

  • 常当作参数传递

Class class = 类名.class;

  • 当有了类的实例对象时才能用

Student s = new Student();
Class class = s.getClass();


方法上加了“s”的就是返回多个对象

declared 公开宣布的,不带declared只能获取public修饰的

2.获取字段(成员变量 Filed)

Class中用于获取成员变量的方法

返回所有公共成员变量对象的数姐

Field[] getFields();

所有成员变量对象的数组

Field[] getDeclaredFields();

返回单个公共成员变量对象

Field getField(String name);

返回单个成员变量对象

Field getDeclaredField(String name);

Field 类中用于创建对象的方法

void (object obj,object value) //赋值
Object get(object obj)          //获取值

再获取到成员变量后,还可以通过以下方法获取权限修饰符变量名、变量类型、变量值

class在这里表示获取到的变量

getModifiers() 注意,该类型为int型,用数字代表访问权限

class.getName()

class.getType()

class.get("类的实例对象")


在获取单个方法时,参数类型要对应上,因为方法会有重载现象

3.获取构造方法(Constructor)

parameterTypes是参数类型 例如String.class

//获取所有公共构造方法(带了s,是复数,所以可以获取多个方法)
Constructor<?>[] getConstructors()
//获取所有构造方法,不管私用的还是公共的,也是复数,可以获取多个
Constructor<?>[] getDeclaredConstructors()
//获取单个(没有加s)公共构造方法
Constructor<T> getConstructor(Class<?>...parameterTypes)
//获取单个(没有加s)构造方法,不限定修饰符的类型
Constructor<T> getDeclaredConstructor(class<?>...parameterTypes)

获取构造方法可以利用newInstance初始化对象

//eg:在com.demo包下得有Student类,并且有相应构造方法
//获取class对象
Class c = Class.getName("com.demo.Student");
//获取构造方法
Constructor con = c.getDeclareConstructor(String.class, int.class);
//临时取消权限校验
con.setAccessible(true);
//创建对象
Student stu = (Student) con.newInstance("张三", 23);

4.获取成员方法(Method)

Class类中用于获取成员方法的方法
返回所有公共成员方法对象的数组,包括继承的

Method[] getMethods();

返回所有成员方法对象的数组,不包括继承的

Method[] getDeclaredMethods();

返回单个公共成员方法对象

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

返回单个成员方法对象

Method getDeclaredMethod(String name,,Class<?>.parameterTypes);

Method类中用于创建对象的方法(在动态代理中会用到)

Object invoke(Object obj,Object..args);运行方法

invoke方法 可以在运行时通过方法的名称和参数来调用一个方法


参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)


动态代理

需求

在维护代码时,要增强某些方法的功能时,原来的代码不宜轻易改动,不然一不小心代码就会无法运行,因此动态代理就出现了

动态代理可以在不改动原来代码得基础上,拓展方法的功能,充当一个中间人,在代理里拓展功能,然后调用原方法

实现

通过对象和代理现实相同的接口,接口中就是被代理的所有方法

需要代理的方法要统一写在一个接口中,在代理类中拓展该方法的功能

最重要的是第三个参数InvocationHandler,一般用匿名内部类的写法,在这里面写功能代码

接口名 对象名 = (接口名) Proxy.newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h)
/*
ClassLoader loader 
参数1:用于指定一个类加载器,固定写法:  当前类名.class.getClassLoader()
Class<?>[] interfaces
参数2:指定生成的代理长什么样子,也就是有哪些方法,即声明是哪个接口
InvocationHandler h  
参数3:用来指定生成的代理对象要干什么事情
真正要写具体代码逻辑的地方,需要代理执行什么拓展功能

一般用匿名内部类的写法
*/
new InvocationHandler( {

        @Override//回调方法

        public Object invoke(Object proxy,Method method,Object[] args){
        //代理对象要做的事情,会在这里写代码
        //会用到反射获取方法名,根据方法不同,执行不同的代码
    }
)

注解

就是Java代码里的特殊的标记,让其他程序根据注解信息来决定如何运行程序,相当于做个标记

自定义注解

  • @Target 注解作用域(类、方法...)
  • @Retention 注解有效时间范围
@Target()
@Retention()
public @interface 注解名称{
    public 属性名称 属性名() default 默认值;
}

解析注解

仅添加自定义的注解不够,程序还不知道加了该注解要怎么做,所以就需要解析注解,告诉计算机加了该注解的方法或类应该做什么

//1.首先获得Class对象
Class c = Demo.class; //Demo为有需要解析注解的类
//2.解析类上的注解
//要利用反射获取相应的类和类上的方法
    //判断类上是否包含某个注解
    c.isAnnotationPresent(注解名.class);

     //判断方法上是否包含某个注解
    Method m = c.getDeclaredMethod("方法名");
    m.isAnnotationPresent(注解名.class);

    //可在此处对加了注解的类或方法进行需要的处理

    //获取注解对象
    Annotation a = c.getDeclaredAnnotation(注解名.class);//类上
    Annotation a = m.getDeclaredAnnotation(注解名.class);//方法上
    //获取注解属性值
      a.属性();

    

具体例子:

注解及注解解析

黑马程序员,Java从入门到起飞(下部)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,针对您的问题,我可以给出以下实现步骤: 1. 定义注解,用于标记需要序列化反序列化的字段。 2. 定义序列化器和反序列化器,使用反射获取被注解标记的字段,并将其转换为字符串或从字符串转换回来。 3. 在需要进行序列化反序列化中使用注解标记需要进行操作的字段。 以下是一个简单的示例代码: ```java import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @interface Serialize { } class Serializer { public static String serialize(Object obj) throws IllegalAccessException { StringBuilder sb = new StringBuilder(); for (Field field : obj.getClass().getDeclaredFields()) { if (field.isAnnotationPresent(Serialize.class)) { field.setAccessible(true); sb.append(field.getName()).append("=").append(field.get(obj)).append(";"); } } return sb.toString(); } } class Deserializer { public static void deserialize(Object obj, String str) throws NoSuchFieldException, IllegalAccessException { for (String fieldString : str.split(";")) { String[] parts = fieldString.split("="); Field field = obj.getClass().getDeclaredField(parts[0]); if (field.isAnnotationPresent(Serialize.class)) { field.setAccessible(true); if (field.getType() == int.class) { field.setInt(obj, Integer.parseInt(parts[1])); } else if (field.getType() == String.class) { field.set(obj, parts[1]); } } } } } class Person { @Serialize private String name; @Serialize private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } } public class Main { public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException { Person person = new Person("Alice", 30); String serialized = Serializer.serialize(person); System.out.println(serialized); Person deserialized = new Person("", 0); Deserializer.deserialize(deserialized, serialized); System.out.println(deserialized); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值