精华内容
下载资源
问答
  • Jsonex JSONCoder是一个轻量级的通用对象序列化/反序列化库,类似于Jackson,GSON或FastJson。 多年来,该库已在各种eBay项目中广泛使用。 它不能替代其他流行的库。 但是,它解决了一些其他替代产品无法提供或无法...
  • Java 对象序列化和反序列化

    千次阅读 2017-03-30 10:22:49
    之前的文章中我们介绍过有关字节流字符流的使用,当时我们对于将一个对象输出到流中的操作,使用...本篇就简单的介绍Java对象序列化,主要内容如下: 简洁的代码实现 序列化实现的基本算法 两种特殊的情

         之前的文章中我们介绍过有关字节流字符流的使用,当时我们对于将一个对象输出到流中的操作,使用DataOutputStream流将该对象中的每个属性值逐个输出到流中,读出时相反。在我们看来这种行为实在是繁琐,尤其是在这个对象中属性值很多的时候。基于此,Java中对象的序列化机制就可以很好的解决这种操作。本篇就简单的介绍Java对象序列化,主要内容如下:

    • 简洁的代码实现
    • 序列化实现的基本算法
    • 两种特殊的情况
    • 自定义序列化机制
    • 序列化的版本控制

    一、简洁的代码实现
         在介绍对象序列化的使用方法之前,先看看我们之前是怎么存储一个对象类型的数据的。

    //简单定义一个Student类
    public class Student {
    
        private String name;
        private int age;
    
        public Student(){}
        public Student(String name,int age){
            this.name = name;
            this.age=age;
        }
    
        public void setName(String name){
            this.name = name;
        }
        public void setAge(int age){
            this.age = age;
        }
        public String getName(){
            return this.name;
        }
        public int getAge(){
            return this.age;
        }
        //重写toString
        @Override
        public String toString(){
            return ("my name is:"+this.name+" age is:"+this.age);
        }
    }
    //main方法实现了将对象写入文件并读取出来
    public static void main(String[] args) throws IOException{
    
            DataOutputStream dot = new DataOutputStream(new FileOutputStream("hello.txt"));
            Student stuW = new Student("walker",21);
            //将此对象写入到文件中
            dot.writeUTF(stuW.getName());
            dot.writeInt(stuW.getAge());
            dot.close();
    
            //将对象从文件中读出
            DataInputStream din = new DataInputStream(new FileInputStream("hello.txt"));
            Student stuR = new Student();
            stuR.setName(din.readUTF());
            stuR.setAge(din.readInt());
            din.close();
    
            System.out.println(stuR);
        }
    输出结果:my name is:walker age is:21

         显然这种代码书写是繁琐的,接下来我们看看,如何使用序列化来完成保存对象的信息。

    public static void main(String[] args) throws IOException, ClassNotFoundException {
    
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hello.txt"));
            Student stuW = new Student("walker",21);
            oos.writeObject(stuW);
            oos.close();
    
            //从文件中读取该对象返回
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hello.txt"));
            Student stuR = (Student)ois.readObject();
            System.out.println(stuR);
        }

         写入文件时,只用了一条语句就是writeObject,读取时也是只用了一条语句readObject。并且Student中的那些set,get方法都用不到了。是不是很简洁呢?接下来介绍实现细节。

    二、实现序列化的基本算法
         在这种机制中,每个对象都是对应着唯一的一个序列号,而每个对象在被保存的时候也是根据这个序列号来对应着每个不同的对象,对象序列化就是指利用了每个对象的序列号进行保存和读取的。首先以写对象到流中为例,对于每个对象,第一次遇到的时候会将这个对象的基本信息保存到流中,如果当前遇到的对象已经被保存过了,就不会再次保存这些信息,转而记录此对象的序列号(因为数据没必要重复保存)。对于读的情况,从流中遇到的每个对象,如果第一次遇到,直接输出,如果读取到的是某个对象的序列号,就会找到相关联的对象,输出。
         说明几点,一个对象要想是可序列化的,就必须实现接口 java.io.Serializable;,这是一个标记接口,不用实现任何的方法。而我们的ObjectOutputStream流,就是一个可以将对象信息转为字节的流,构造函数如下:

    public ObjectOutputStream(OutputStream out)

         也就是所有字节流都可以作为参数传入,兼容一切字节操作。在这个流中定义了writeObject和readObject方法,实现了序列化对象和反序列化对象。当然,我们也是可以通过在类中实现这两个方法来自定义序列化机制,具体的后文介绍。此处我们只需要了解整个序列化机制,所有的对象数据只会保存一份,至于相同的对象再次出现,只保存对应的序列号。下面,通过两个特殊的情况直观的感受下他的这个基本算法。

    三、两个特殊的实例
         先看第一个实例:

    public class Student implements Serializable {
    
        String name;
        int age;
        Teacher t;  //另外一个对象类型
    
        public Student(){}
        public Student(String name,int age,Teacher t){
            this.name = name;
            this.age=age;
            this.t = t;
        }
    
        public void setName(String name){this.name = name;}
        public void setAge(int age){this.age = age;}
        public void setT(Teacher t){this.t = t;}
        public String getName(){return this.name;}
        public int getAge(){return this.age;}
        public Teacher getT(){return this.t;}
    }
    
    public class Teacher implements Serializable {
        String name;
    
        public Teacher(String name){
            this.name = name;
        }
    }
    
    public static void main(String[] args) throws IOException, ClassNotFoundException {
    
            Teacher t = new Teacher("li");
            Student stu1 = new Student("walker",21,t);
            Student stu2 = new Student("yam",22,t);
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hello.txt"));
            oos.writeObject(stu1);
            oos.writeObject(stu2);
    
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hello.txt"));
            Student stuR1 = (Student)ois.readObject();
            Student stuR2 = (Student)ois.readObject();
    
            if (stuR1.getT() == stuR2.getT())
                System.out.println("相同对象");
        }

         结果是很显而易见的,输出了相同对象。我们在main函数中定义了两个student类型对象,他们却都引用的同一个teacher对象在内部。完成序列化之后,反序列化出来两个对象,通过比较他们内部的teacher对象是否是同一个实例,可以看出来,在序列化第一个student对象的时候t是被写入流中的,但是在遇到第二个student对象的teacher对象实例时,发现前面已经写过了,于是不再写入流中,只保存对应的序列号作为引用。当然在反序列化的时候,原理类似。这和我们上面介绍的基本算法是一样的。
         下面看第二个特殊实例:

    public class Student implements Serializable {
    
        String name;
        Teacher t;
    
    }
    
    public class Teacher implements Serializable {
        String name;
        Student stu;
    
    }
    
    public static void main(String[] args) throws IOException, ClassNotFoundException {
    
            Teacher t = new Teacher();
            Student s =new Student();
            t.name = "walker";
            t.stu = s;
            s.name = "yam";
            s.t = t;
    
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hello.txt"));
            oos.writeObject(t);
            oos.writeObject(s);
            oos.close();
    
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hello.txt"));
            Teacher tR = (Teacher)ois.readObject();
            Student sR = (Student)ois.readObject();
            if(tR == sR.t && sR == tR.stu)System.out.println("ok");
    
        }

         输出的结果是ok,这个例子可以叫做:循环引用。从结果我们可以看出来,序列化之前两个对象存在的相互的引用关系,经过序列化之后,两者之间的这种引用关系是依然存在的。其实按照我们之前介绍的判断算法来看,首先我们先序列化了teacher对象,因为他内部引用了student的对象,两者都是第一次遇到,所以将两者序列化到流中,然后我们去序列化student对象,发现这个对象以及内部的teacher对象都已经被序列化了,于是只保存对应的序列号。读取的时候根据序列号恢复对象。

    四、自定义序列化机制
         综上,我们已经介绍完了基本的序列化与反序列化的知识。但是往往我们会有一些特殊的要求,这种默认的序列化机制虽然已经很完善了,但是有些时候还是不能满足我们的需求。所以我们看看如何自定义序列化机制。自定义序列化机制中,我们会使用到一个关键字,它也是我们之前在看源码的时候经常遇到的,transient。将字段声明transient,等于是告诉默认的序列化机制,这个字段你不要给我写到流中去,我会自己处理的。、

    public class Student implements Serializable {
    
        String name;
        transient int age;
    
        public String toString(){
            return this.name + ":" + this.age;
        }
    }
    
    public static void main(String[] args) throws IOException, ClassNotFoundException {
    
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("hello.txt"));
            Student stu = new Student();
            stu.name = "walker";stu.age = 21;
            oos.writeObject(stu);
    
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("hello.txt"));
            Student stuR = (Student)ois.readObject();
    
            System.out.println(stuR);
        }
    输出结果:walker:0

         我们不是给age字段赋初始值了么,怎么会是0呢?正如我们上文所说的一样,被transient修饰的字段不会被写入流中,自然读取出来就没有值,默认是0。下面看看我们怎么自己来序列化这个age。

    //改动过的student类,main方法没有改动,大家可以往上看
    public class Student implements Serializable {
    
        String name;
        transient int age;
    
        private void writeObject(ObjectOutputStream oos) throws IOException {
            oos.defaultWriteObject();
    
            oos.writeInt(25);
        }
    
        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
            ois.defaultReadObject();
    
            age = ois.readInt();
        }
    
        public String toString(){
            return this.name + ":" + this.age;
        }
    }
    输出结果:walker:25

         结果既不是我么初始化的21,也不是0,而是我们在writeObject方法中写的25。现在我们一点一点看看每个步骤的意义。首先,要想要实现自定义序列化,就需要在该对象定义的类中实现两个方法,writeObject和readObject,而且格式必须和上面贴出来的一样,笔者试过改动方法修饰符,结果导致不能成功序列化。这是因为,Java采用反射机制,检查该对象所在的类中有没有实现这两个方法,没有的话就使用默认的ObjectOutputStream中的这个方法序列化所有字段,如果有的话就执行你自己实现的这个方法。
         接下来,看看这两个方法实现的细节,先看writeObject方法,参数是ObjectOutputStream 类型的,这个拿到的是我们在main方法中定义的ObjectOutputStream 对象,要不然它怎么知道该把对象写到那个地方去呢?第一行我们调用的是oos.defaultWriteObject();这个方法实现的功能是,将当前对象中所有没有被transient修饰的字段写入流中,第二条语句我们显式的调用了writeInt方法将age的值写入流中。读取的方法类似,此处不再赘述。

    五、版本控制
         最后我们来看看,序列化过程的的版本控制问题。在我们将一个对象序列化到流中之后,该对象对应的类的结构改变了,如果此时我们再次从流中将之前保存的对象读取出来,会发生什么?这要分情况来说,如果原类中的字段被删除了,那从流中输出的对应的字段将会被忽略。如果原类中增加了某个字段,那新增的字段的值就是默认值。如果字段的类型发生了改变,抛出异常。在Java中每个类都会有一个记录版本号的变量:static final serivalVersionUID = 115616165165L,此处的值只用于演示并不对应任意某个类。这个版本号是根据该类中的字段等一些属性信息计算出来的,唯一性较高。每次读出的时候都会去比较之前和现在的版本号确认是否发生版本不一致情况,如果版本不一致,就会按照上述的情形分别做处理。

         对象的序列化就写完了,如果有什么内容不妥的地方,希望大家指出!

    展开全文
  • java JSONObject对象序列化与反序列化

    万次阅读 2016-02-17 17:08:45
    1.将java对象转换为json字符串(序列化) 先将java对象转换为json对象,在将json对象转换为json字符串 JSONObject json = JSONObject.fromObject(obj);//将java对象转换为json对象 String str = json....
    

    首先需要导入一下包

    json-lib-2.3-jdk15.jar 
    commons-beanutils-1.7.0.jar 
    commons-httpclient-3.1.jar 
    commons-lang-2.3.jar 
    commons-logging-1.0.4.jar 
    commons-collections-3.1.jar 
    ezmorph-1.0.6.jar (类型转换用)
    这些包可以从一下地方下载: 
    http://commons.apache.org/index.html 
    http://json-lib.sourceforge.net/ 
    http://ezmorph.sourceforge.net/ 
    http://www.docjar.com/ 


    1.将java对象转换为json字符串(序列化)

    先将java对象转换为json对象,在将json对象转换为json字符串

    JSONObject json = JSONObject.fromObject(obj);//将java对象转换为json对象

    String str = json.toString();//将json对象转换为字符串


    2.将json字符串转换为java对象(反序列化)

    同样先将json字符串转换为json对象,再将json对象转换为java对象,如下所示。
    JSONObject obj = new JSONObject().fromObject(jsonStr);  //将json字符串转换为json对象,jsonStr为一个json字符串
    将json对象转换为java对象(Person为实例类)
    Person jb = (Person)JSONObject.toBean(obj,Person.class);//将建json对象转换为Person对象

    注:反序列化时可能会遇到以下问题
        JSONObject object = JSONObject.fromObject(user);
        Object dep = object.get("department");  //get可获取某个节点的值
        object.remove("department")   //remove可以移除某个节点
        object.element("test","test")  //element可添加任意一个节点

    问题1:时间格式转换出错 一般时间如("2016-02-16", "01/02/2016"或者其它 ),可用以下方法设置
      JSONUtils.getMorpherRegistry().registerMorpher(new DateMorpher(new String[] {"yyyy/MM/dd","yyyy-MM-dd","yyyy-MM-dd HH:mm:ss"}));
    问题2:你会发现,如果Date为""时,转换会出错,这时就得修改源码了(网上找),或者把该节点移除(用remove()方法)
    问题3:抽象类(abstract)是无法反序列化的,这时你得先取出该对象,然后再移除,最后使用子类转换

    <span style="font-family:Microsoft YaHei;font-size:14px;">	
    	JSONObject object = JSONObject.fromObject(checkInfo.getInfo());
    	Object assetsObject = object.get("baseinfo");
    	JSONObject assetsJson = JSONObject.fromObject(assetsObject);
    	//防止Date为""时反序列化出错
    	if(StringUtil.isNull(String.valueOf(assetsJson.get("firstDate")))){
    		assetsJson.remove("firstDate");  //为空时移除该节点
    	}
    	if(StringUtil.isNull(String.valueOf(assetsJson.get("storeDate")))){
    		assetsJson.remove("storeDate");
    	}
    	Object expands = assetsJson.get("expands");
    	Object currentCycle = assetsJson.get("currentCycle");  //currentCycle为抽象类,不能反序列化
    	assetsJson.remove("expands");
    	assetsJson.remove("currentCycle");
    	Map classMap = new HashMap();
    	classMap.put("expands", Set.class);  //如果该对象为Set、Map等,则在这里声明
    	Assets assets = (Assets) JSONObject.toBean(assetsJson, Assets.class, classMap);
    				
    	JSONObject cycleJson = JSONObject.fromObject(currentCycle);
    	Cycle cycle;</span>
    <span style="font-family:Microsoft YaHei;font-size:14px;">	//由于父类为抽象类,这里使用子类进行转化
    	if(checkList.getAssets().getCurrentCycle() instanceof CyclePart){
    		cycle = (CyclePart) JSONObject.toBean(cycleJson, CyclePart.class);
    	}else if(checkList.getAssets().getCurrentCycle() instanceof CycleCar){
    		cycle = (CycleCar) JSONObject.toBean(cycleJson, CycleCar.class);
    	}else{
    		cycle = (CycleDevice) JSONObject.toBean(cycleJson, CycleDevice.class);
    	}
    	assets.setCurrentCycle(cycle);
    	checkList.setAssets(assets);</span>

    <span style="font-family:Microsoft YaHei;font-size:14px;">1. List集合转换成json代码
    
    List list = new ArrayList();
    
    list.add( "first" );
    
    list.add( "second" );
    
    JSONArray jsonArray2 = JSONArray.fromObject( list );
    
    2. Map集合转换成json代码
    
    Map map = new HashMap();
    
    map.put("name", "json");
    
    map.put("bool", Boolean.TRUE);
    
    map.put("int", new Integer(1));
    
    map.put("arr", new String[] { "a", "b" });
    
    map.put("func", "function(i){ return this.arr[i]; }");
    
    JSONObject json = JSONObject.fromObject(map);
    
    3. Bean转换成json代码
    
    JSONObject jsonObject = JSONObject.fromObject(new JsonBean());
    
    4. 数组转换成json代码
    
    boolean[] boolArray = new boolean[] { true, false, true };
    
    JSONArray jsonArray1 = JSONArray.fromObject(boolArray);
    
    
    5. 一般数据转换成json代码
    
    JSONArray jsonArray3 = JSONArray.fromObject("['json','is','easy']" );</span>





    展开全文
  • Java对象序列化

    千次阅读 2019-05-28 10:28:18
    基本概念 什么是序列化和反序列化 序列化就是将对象写入到IO流中...Java对象转成字符串、Json、XML等其实也称为“序列化”,但与JVM提供的序列化功能不太一样,可以说序列化是一个比较抽象的概念,但本文主要指JVM...
    基本概念
    • 什么是序列化和反序列化
      序列化就是将对象写入到IO流中,反序列化就是从IO流中恢复对象。
    • 为什么需要序列化
      序列化后的流,可用于持久化到磁盘,也可以用于网络传输。使得Java对象可以跨进程、跨主机使用。
    • 转成Json和XML算序列化吗
      Java对象转成字符串、Json、XML等其实也称为“序列化”,但与JVM提供的序列化功能不太一样,可以说序列化是一个比较抽象的概念,但本文主要指JVM的序列化。
    如何序列化和反序列化
    • 实现Serializable接口
      序列化最常用的方式是实现Serializable接口,然后让客户端去序列化。
    public class Teacher implements Serializable {
        private String name;
        private int age;
    
        public Teacher(String name, int age) {
            System.out.println("调用了构造方法!");
            this.name = name;
            this.age = age;
        }
    	
        // getter方法
    }
    
    public class Client {
        public static void main(String[] args) {
            System.out.println("序列化:");
            try(ObjectOutputStream out = new ObjectOutputStream(
                    new FileOutputStream("object.txt"))) {
                Teacher teacher = new Teacher("Tom", 53);
                System.out.println("序列化之前的hash:" + teacher.hashCode());
                out.writeObject(teacher);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            System.out.println("反序列化:");
            try(ObjectInputStream input = new ObjectInputStream(
                    new FileInputStream("object.txt"))) {
                Teacher teacher = (Teacher) input.readObject();
                System.out.println("Teacher的名字:" + teacher.getName());
                System.out.println("Teacher的年龄:" + teacher.getAge());
                System.out.println("序列化之后的hash:" + teacher.hashCode());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    输出结果:

    序列化:
    调用了构造方法!
    序列化之前的hash:142666848
    反序列化:
    Teacher的名字:Tom
    Teacher的年龄:53
    序列化之后的hash:310656974
    

    根据输出结果,我们可以总结出以下三点信息:

    1. 不用提供无参构造方法
    2. 反序列化时,不会调用构造方法
    3. 如果没有重写hashCode,反序列化后,hashCode是不一样的

    至于为什么hashCode会不一样?因为Java默认的hashCode是一个内存地址指针。反序列化后,对象自然不在原来的内存地址上,所以hashCode会不一样。所以这里我们提倡要覆盖hashCode方法和equals方法。

    • transient关键字
      如果一个字段使用了transient关键字,那它就不会被序列化。在反序列化时,如果它的引用类型,值就是null,如果是基本类型,就是基本类型的默认值。这里我们尝试把age声明为transient的。会输出:
    Teacher的年龄:0
    
    • 自定义序列化方法
      transient关键字虽然使用方便,但“可定制化”不强,比如我虽然不想序列化age字段,但希望它反序列化时默认值是18,这样的需求就得使用自定义序列化方法来实现。

    主要有这样几个方法:

    private void writeObject(java.io.ObjectOutputStream out) throws IOException;
    private void readObject(java.io.ObjectIutputStream in) throws IOException,ClassNotFoundException;
    private void readObjectNoData() throws ObjectStreamException;
    
    private Object writeReplace() throws ObjectStreamException;
    private Object readResolve() throws ObjectStreamException;
    

    通过重写writeObject与readObject方法,就可以实现自定义的序列化和反序列化。这里需要注意的是两个方法要有“对称性”,自己在重写这两个方法的时候,需要保证被序列化后,能够顺利地被反序列化。

    示例代码:

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeInt(age);
    }
    
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        age = in.readInt();
    }
    

    PS: 这两个类是私有的,是在什么时候被调用,如何被调用的呢?

    答案是通过反射。详情可见 ObjectOutputStream 中的 writeSerialData 方法,以及
    ObjectInputStream 中的 readSerialData 方法。

    除此之外,还可以使用writeReplace与readResolve实现更高程度地定制化。

    1. writeReplace:在序列化时,会先调用此方法,再调用writeObject方法。此方法可将任意对象代替目标序列化对象。
    2. readResolve:反序列化时替换反序列化出的对象,反序列化出来的对象被立即丢弃。此方法在readeObject后调用。

    那readObjectNoData()方法用来干嘛的呢?详情可以看Serializable接口的注释。大意是:当序列化流不完整时,readObjectNoData()方法可以用来正确地初始化反序列化的对象。

    • 实现Externalizable接口
      Externalizable接口是继承自Serializable接口的。

    使用Externalizable接口不同于Serializable接口,实现此接口必须实现接口中的两个方法实现自定义序列化,这是强制性的;必须提供public的无参构造器,因为在反序列化的时候需要反射创建对象。

    示例代码:

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        StringBuffer reverse = new StringBuffer(name).reverse();
        out.writeObject(reverse);
        out.writeInt(age);
    }
    
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.name = ((StringBuffer) in.readObject()).reverse().toString();
        this.age = in.readInt();
    }
    
    常见问题
    • 如果不实现Serializable接口会怎样?
      如果不实现Serializable接口(这里包括也不实现Externalizable),客户端在序列化时,会报NotSerializableException异常。

    • 如果内部属性是引用类型,怎么办?
      如果要序列化的对象内部有引用类型的属性,那这个属性也必须实现序列化,否则同样会报NotSerializableException异常。

    • 子类实现序列化,父类不实现序列化
      子类实现序列化,父类不实现序列化,此时父类要实现一个无参数构造器,否则反序列化时会抛InvalidClassException异常。因为如果父类不实现序列化,反序列化时会调用父类的无参构造器。

    // 父类:
    public class Person {
        private String nationality;
    
        public Person(String nationality) {
            this.nationality = nationality;
        }
    }
    
    // 子类:
    public class Teacher extends Person implements Serializable {
        private String name;
        private int age;
    
    
    
        public Teacher(String name, int age) {
            super("China"); // 调用父类的有参构造方法
            System.out.println("调用了构造方法!");
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public int getAge() {
            return age;
        }
    }
    
    // 客户端:
    public class Client {
        public static void main(String[] args) {
            System.out.println("序列化:");
            try(ObjectOutputStream out = new ObjectOutputStream(
                    new FileOutputStream("object.txt"))) {
                Teacher teacher = new Teacher("Tom", 53);
                System.out.println("序列化之前的hash:" + teacher.hashCode());
                out.writeObject(teacher);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            System.out.println("反序列化:");
            try(ObjectInputStream input = new ObjectInputStream(
                    new FileInputStream("object.txt"))) {
                Teacher teacher = (Teacher) input.readObject();
                System.out.println("Teacher的名字:" + teacher.getName());
                System.out.println("Teacher的年龄:" + teacher.getAge());
                System.out.println("序列化之后的hash:" + teacher.hashCode());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    输出结果:

    序列化:
    调用了构造方法!
    序列化之前的hash:1060830840
    反序列化:
    java.io.InvalidClassException: serialize.Teacher; no valid constructor
    	at java.base/java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:159)
    	at java.base/java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:864)
    	at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2061)
    	at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1594)
    	at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:430)
    	at serialize.Client.main(Client.java:20)
    
    • 同一个对象多次序列化会发生什么?
      Java序列化同一对象,并不会将此对象序列化多次得到多个对象。Java会给每个序列化成功的对象一个“序列化编号”。当程序试图序列化一个对象时,会先检查此对象是否已经序列化过,只有此对象从未(在此虚拟机)被序列化过,才会将此对象序列化为字节序列输出。否则,直接输出编号。
    out.writeObject(teacher); // 序列化一次
    
    // 反序列化两次,会抛EOFException异常
    Teacher teacher = (Teacher) input.readObject();
    Teacher teacher2 = (Teacher) input.readObject();
    
    Teacher teacher = new Teacher("Tom", 53);
    out.writeObject(teacher); 
    teacher.setAge(23)
    out.writeObject(teacher); // 序列化两次
    
    // 反序列化两次
    Teacher teacher = (Teacher) input.readObject();
    Teacher teacher2 = (Teacher) input.readObject();
    System.out.println(teacher.equals(teacher2)); // true
    System.out.println("Teacher的年龄:" + teacher.getAge()); // 53
    
    • 序列化和反序列化的顺序?
      反序列化时,取出对象的顺序与序列化是一致的。也就是说,先存先取,后存后取。
    Teacher teacher1 = new Teacher("Tom", 53);
    out.writeObject(teacher1);
    Teacher teacher2 = new Teacher("Bob", 29);
    out.writeObject(teacher2);
    
    Teacher teacher1 = (Teacher) input.readObject();
    System.out.println(teacher1.getName()); // Tom
    Teacher teacher2 = (Teacher) input.readObject();
    System.out.println(teacher2.getName()); // Bob
    

    参考文章

    java序列化,看这篇就够了
    深度解析JAVA序列化

    展开全文
  • Java对象序列化小结

    千次阅读 2015-03-23 14:35:31
    Java中经常会用到对象序列化的地方,比如在持久化存储对象时,传输对象时等。目前Java对象的序列化有很多种,可参见“Java序列化工具大全及性能比较”,但个人认为大致可分为4类: 1)序列化对象需要事先实现...

    原创文章,转载时请注明作者:jmppok 及 出处 Java对象序列化小结


    在Java中经常会用到对象序列化的地方,比如在持久化存储对象时,传输对象时等。目前Java对象的序列化有很多种,可参见“Java序列化工具大全及性能比较”,但个人认为大致可分为4类:

    1)序列化对象需要事先实现Serializable接口的,如Java原生的序列化,Hessian, FST-serialization等等。

    优点是:可以自己控制Serializable的实现,十分灵活,用户可以自己控制序列化的内容,顺序等等,如果实现比较好的话,体积小,效率高。

    缺点是:序列化对象必须事先实现Serializable接口,限制比较大。并且用户需事先实现Serializable接口,工作量相对较大。

    个人不太推荐。


    2)使用某种中间语言,事先定义对象的Schema,然后生成相应的Java/C++/Python代码, 如goole的protobuffer,flatbuffer,thrift,ice等等。

    优点是:用户通过中间语言定义数据结构,可以生成大部分语言(C/C++/Java/C#/Python/Javascript等等)对应的代码,使用起来十分方便,且序列化效率高,体积小。

    缺点是:用户需事先定义数据结构,因为限制了使用范围。

    一般推荐。


    3)  通过开源工具,将对象直接序列化为XML,JSON等,如gson,fastjson等。

    优点是:可序列化任意的Java对象。

    缺点是:效率相对较低,序列化后体积较大。在反序列化时,必须要指定需要反序列化的数据的Class,个人认这一点非常不爽。

    如下所示:

    Persion类

    public class Persion {
        public String name;
        public int    age;
    
        public Persion(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public Persion() {
    
        }
    }

    Gson序列化/反序列化测试

    public class TestGson {
    
        /**
         * @author ligh4 2015年3月19日下午5:32:34
         * @param args
         */
        public static void main(String[] args) throws Exception {
    
            Map<Integer, Map<String, Persion>> classes = new Hashtable<Integer, Map<String, Persion>>();
    
            Map<String, Persion> map = new HashMap<String, Persion>();
            map.put("ligh1", new Persion("ligh1", 30));
            map.put("ligh2", new Persion("ligh2", 30));
    
            classes.put(1, map);
    
            String json = new Gson().toJson(classes);
    
            Map<Integer, Map<String, Persion>> cls = new Gson().fromJson(json,
                    new TypeToken<Map<Integer, Map<String, Persion>>>() {
                    }.getType());
    
            System.out.println(cls.get(1).get("ligh2").name);
    
        }
    }

    看到Gson反序列化时了么?

            Map<Integer, Map<String, Persion>> cls = new Gson().fromJson(json,
                    new TypeToken<Map<Integer, Map<String, Persion>>>() {
                    }.getType());

    好像很不爽阿?不过相比需要事先实现Serializable接口和实现定义Schema生成代码,忍了。


    实际上,在计算机硬件性能和带宽高速发展的今天,对序列化的效率和数据大小的追求逐渐下降。大部分公司追求的是项目的短,平,快,因此gson等序列化工具成为大家的首选。

    十分推荐。


    4)个人认为的终极序列化工具, 序列化任意对象。且在序列化反序列化时不需要人为指定类的信息,如Kryo。

    费话不多说,直接上代码:

    	public static void main (String[] args) throws Exception {
    
    
    		Map<String, Persion> map = new HashMap<String, MapTest.Persion>();
    		map.put("ligh1", new Persion("ligh1", 30));
    		map.put("ligh2", new Persion("ligh2", 30));
    
    		Map<Integer, Map<String, Persion>> classes = new Hashtable<Integer, Map<String, Persion>>();
    
    		classes.put(1, map);
    
    
    		Kryo kryo = new Kryo();
    
    		ByteArrayOutputStream buffer = new ByteArrayOutputStream();
    		Output output = new Output(buffer);
    		kryo.writeClassAndObject(output, classes);
    		output.flush();
    
    		byte[] datas = buffer.toByteArray();
    		System.out.println(datas.length);
    
    		Input input = new Input(datas);
    
    		Map<String, Persion> map2 = (Map)((Map)kryo.readClassAndObject(input)).get(1);
    		Persion p1 = map2.get("ligh2");
    	}

    看到强大之处了吧?

    kryo.writeClassAndObject(output, classes);
    <pre name="code" class="java">kryo.readClassAndObject(input);

     使用十分方便。 
    

    不过什么Output,Input貌似也有点Out了,来一个简单的封装:

    public class ObjectSerialization {
    
        /**
         * @author ligh4 2015年3月23日下午1:32:04
         * @param obj
         * @return null if failed.
         * @throws Exception
         */
        public static synchronized String ToString(Object obj) {
            Kryo kryo = new Kryo();
    
            ByteOutputStream stream = new ByteOutputStream();
            Output output = new Output(stream);
            kryo.writeClassAndObject(output, obj);
            output.flush();
    
            String str = null;
            try {
                byte[] bytes = stream.getBytes();
                str = new String(bytes, "ISO8859-1");
            } catch (Exception e) {
                str = null;
            } finally {
                output.close();
                stream.close();
            }
    
            return str;
        }
    
        /**
         * @author ligh4 2015年3月23日下午1:32:19
         * @param str
         * @return null if failed.
         * @throws Exception
         */
        public static synchronized Object FromString(String str) {
    
            Kryo kryo = new Kryo();
    
            try {
                byte[] bytes = str.getBytes("ISO8859-1");
                Input input = new Input(bytes);
                Object obj = kryo.readClassAndObject(input);
                input.close();
                return obj;
            } catch (Exception e) {
                return null;
            }
    
        }
    
        static class Persion {
            public String name;
            public int    age;
    
            public Persion(String name, int age) {
                this.name = name;
                this.age = age;
            }
    
            public Persion() {
    
            }
        }
    
        /**
         * @author ligh4 2015年3月20日下午4:07:50
         * @param args
         */
        @SuppressWarnings({ "unchecked", "rawtypes", "unused" })
        public static void main(String[] args) throws Exception {
    
            Map<String, Persion> map = new HashMap<String, Persion>();
            map.put("cn1", new Persion("中国1", 30));
            map.put("cn2", new Persion("中国2", 30));
    
            Map<Integer, Map<String, Persion>> classes = new HashMap<Integer, Map<String, Persion>>();
    
            classes.put(1, map);
    
            String str = ObjectSerialization.ToString(classes);
    
            System.out.println(str.length() + ":" + str);
    
            Map<String, Persion> map2 = (Map) ((Map) ObjectSerialization.FromString(str)).get(1);
    
            Persion p1 = map2.get("cn2");
        }
    }

    使用更加方便了:

    String str = ObjectSerialization.ToString(classes);
    ObjectSerialization.FromString(str)).get(1);


    展开全文
  • 下面小编就为大家分享一篇将Java对象序列化成JSON和XML格式的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • Java对象序列化与反序列化

    千次阅读 2018-03-21 00:00:00
    本文转载自公众号 Hollis序列化与反序列化序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。一般将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等。在网络传输过程中,可以是...
  • Java对象的XML序列化

    千次阅读 2012-03-06 19:13:46
    引言 Java对象的序列化(serialization)是对象的持久存储和对象状态的网络传输的 关键环节...对象序列化可以实现分布式对象,例如RMI要利用对象序列化运行远程主机上的服务,就像在本地机器上调用对象一样。对象序列化
  • Java反射机制和对象序列化

    千次阅读 2015-06-26 12:08:13
    反射机制:  JAVA反射机制是在运行状态中,对于任意...对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。  通俗地说,反射机制就是可以把
  • 系统解耦与序列化、持久化 从开始学习和实践系统设计起就一直围绕着这样一个问题——怎样降低系统的耦合度,使系统更加健壮、更加灵活?无论桌面应用还是网络应用,确实它的系统中无非包含如下几方面的内容——如何...
  • java I/O系统(9)-对象序列化与还原

    千次阅读 2017-05-24 15:39:50
    万物皆对象,在我们程序运行中,对象只要在引用链上存在引用,那么它就会一直存在。但是当我们程序结束的时候,那么对象就会...在本篇博文中,详细介绍对象序列化的概念,不同序列化的方式和结果,并给出相应的demo。
  • Gson可以处理任意Java对象,包括您没有源代码的现有对象。 有一些开源项目可以将Java对象转换为JSON。 但是,大多数方法都要求您在类中放置Java批注。 如果您无法访问源代码,则无法执行某些操作。 大多数还不完全...
  • Flink中kafkaconnector自定义序列化与反序列化 对象与String自行互转进行生产与消费 ​ 当我们对Flink的kafka-connector有了一个大概的认识,并且能够对String,Json等类型的数据进行一个生产和消费操作后,能够想到...
  • Java提供了一种对象序列化的机制: 用一个字节序列可以表示一个对象 该字节序列包含该对象的数据 类型 和 对象中存储的属性等信息 字节序列写出到文件之后 相当于文件中持久保存了一个对象的信息 反之 该字节序列还...
  • JAVA RMI 反序列化远程命令执行漏洞

    万次阅读 2017-05-11 00:42:34
    JAVA RMI 反序列化远程命令执行漏洞 ...Weblogic Commons-Collections反序列化RCE漏洞CVE-2015-4852JAVA RMI 反序列化远程命令执行漏洞漏洞资料Java RMI远程反序列化任意类及远程代码执行解析(CVE-2017-324
  • Java对象序列化详细解析

    万次阅读 2006-12-13 11:02:00
    Bean Serializable Interface 的接口让BEAN可以串行,将其变成一个可保存为以后使用的二进制流。当一个BEAN被系列到磁盘上或者其他任何地方,其状态被保存起来,其中的属性值也不会改变。在BEAN的规范中,JSP并...
  • 序列化:把Java对象转换为字节序列的过程,实现对象的持久化。 反序列化:把字节序列恢复为Java对象的过程。 “持久化”意味着一个对象的生存周期并不取决于程序是否正在执行:它可以生存于程序的调用的之间,它的...
  • XStream对象序列化

    2008-06-22 17:52:43
    使用 XStream 把 Java 对象序列化为 XML
  • Java Serializable 序列化对象克隆

    千次阅读 2019-07-10 15:26:10
    序列化:即提取数据对象并将其转换为字节流(二进制格式)的过程,因此它可以通过网络传输或保存在数据库、以及本地文件中,后续可以再反序列化为对象。 实现 Serializable 接口 1、使用默认的序列化机制,即实现 ...
  • java序列化算法揭秘

    2017-02-14 12:34:06
    Java Serialization API为开发者提供了一套标准机制来处理对象序列化。本文你将看到如何序列化一个对象以及为什么对象的序列化在有的情况下是必须的。你将会学习到java的序列化算法以及一个揭示序列化对象格式的例子...
  • 对象序列化

    2012-07-30 18:30:56
    一、什么是对象序列化对象序列化:就是将一个对象转换为二进制数据流。 如果一个类对象要想实现对象序列化,怎对象所在的类必须实现Serializable(序列化)接口。 在此接口中没有任何的方法,此接口只是作为一个...
  • Java 序列化和反序列化总结

    千次阅读 2013-09-14 23:51:54
    首先我们知道对象都是存放在堆内存中的,一旦使用完毕不在被引用之后就会被垃圾回收机制回收,或则退出程序的时候就会被释放,...将对象读取到字节流中并写入保存起来,并在以后还原这个对象,这种机制叫做对象序列化
  • java序列化和反序列化,面试必备

    千次阅读 多人点赞 2020-04-26 17:04:19
    意义:序列化机制允许将实现序列化Java对象转换为字节序列,并将字节序列保存在磁盘中,或通过网络传输,以达到以后恢复成原来的对象序列化机制使地对象可以脱离程序的运行而独立存在。 使用场景:所有在网络上...
  • Gson可以使用任意Java对象Gson Gson是一个Java库,可用于将Java对象转换为其JSON表示形式。 它也可以用于将JSON字符串转换为等效的Java对象。 Gson可以处理任意Java对象,包括您没有源代码的预先存在的对象。 有一些...
  • Java序列化和打印流

    千次阅读 2019-07-12 14:22:48
    一、对象序列化和反序列化 1、概述 2、对象序列化ObjectOutputStream和反序列化ObjectInputStream 二、序列化注意事项 1、静态不能序列化 2、瞬态 transient 关键字 3、Serializable 接口的含义 4、序列化中...
  • 什么是Java序列化

    千次阅读 2020-06-27 21:14:51
    Java的序列化一、序列化的含义、意义及使用场景二、序列化实现的方式2.1 Serializable(1)普通序列化(2)成员是引用的序列化(3)同一对象序列化多次的机制(4)java序列化算法潜在的问题(5)可选的自定义序列化...
  • Java 原生 JSON 序列化

    千次阅读 2019-01-29 13:30:44
    JSON(《用 Rhino/Nashorn 代替第三方 JSON 转换库》、《使用 Rhino 作为 Java 的 JSON 解析/转换包》),几经思考后,还是决然毅然放弃这个不切实际的想法,老老实实去写转换函数,几经打磨,有了下面“序列化” ...
  • Java 和 Hadoop 序列化机制浅讲

    千次阅读 2015-04-22 11:42:30
    序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程(字节流)。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该...
  • Thinking in java 琐碎知识点之 I/O流 、对象序列化

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 94,824
精华内容 37,929
关键字:

java任意对象序列化

java 订阅