精华内容
参与话题
问答
  • ObjectOutputStream和ObjectInputStream的简单使用

    使用ObjectOutputStream往文本写内容时,首先在文本里面标记开始,然后是内容,最后加上结束标示。如果想再次往文本里面添加内容的话,就要加在开始标示之后和结束标示之前,不然会读取不到写入的内容。

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    
    public class ObjectBuffer {
        public static void main(String[] args) throws Exception {
            ObjectOutputStream oos = new ObjectOutputStream(new
                    FileOutputStream("src/TXT/Z.txt",false));//工程里面的路径
            oos.writeObject(new User("java",16));
            oos.writeObject(new User("C++",18));
            oos.writeObject(new User("c语言",22));
            oos.writeObject(null);   //最后添加一个空对象,作为后面读取内容的判断
            oos.flush();
            oos.close();
            //第一次使用ObjectOutputStream往文本写内容时,就会自动在文本内容最后打上结束标示。
            //当再次使用ObjectOutputStream往里面写入内容时,内容加在上次内容的后面,当使用ObjectInputStream读取内容时,
            //因为第一次写入内容时,在后面加上了结束标示,将会读取不到第二次写入的内容。
    
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("src/TXT/Z.txt"));
            User u;
            while((u = (User) ois.readObject()) != null){
                System.out.println(u);
            }
            ois.close();
        }
    }
    class User implements Serializable{
        private static final  long  serialVersionUID = 1L;  //序列化
        static int n = 0;
        private String name;
        private int age;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public User(String name, int age) {
            super();
            n++;
            this.name = name;
            this.age = age;
        }
        public User() {
            super();
        }
        @Override
        public String toString() {
            n--;
            return "User [name=" + name + ", age=" + age + "]";
        }
    }

    输出结果:
    User [name=java, age=16]
    User [name=C++, age=18]
    User [name=c语言, age=22]


    序列化的概念:
    Java序列化机制使用名称为serialVersionUID的long型字段来标志类的版本。序列化对象时,Jvm会把serialVersionUID的值写到类的序列化数据中;反序列化时,JVM会把对象数据数据中的serialVersionUID与本地相应类的serialVersionUID进行比较,如果值不相同(意味着类的版本不同),那么报异常InvalidCastException,即:类版本不对应,不能进行反序列化。如果类版本相同,则可以进行反序列化。


    常见的serialVersionUID的值有两种情况:
    一种是固定的一个long型值,比如:
    private static final long serialVersionUID = 1L;
    private static final long serialVersionUID = 123L;
    第二种是根据类名、接口名、成员方法及属性等生成一个64位的哈希值(ide可以完成此工作),比如:
    private static final long serialVersionUID = xxxxL;
    当实现java.io.Serializable接口的类没有显式地定义一个serialVersionUID值时,Java序列化机制会根据编译的Class生成一个serialVersionUID作序列化版本比较用,一旦class文件有所变动(比如空格、变量名),那么serialVersionUID也会随之变动。


    • 假如A端和B端通信,在A端先把一个对象序列化,在B端进行接收。两端的对象serialVersionUID值是相同的。如果A端增加一个字段,然后序列化,而B端不变,然后反序列化;反序列化正常,但是A端增加的字段丢失(被B端忽略)。
    • 如果B端减少一个字段,A端不变,反序列化正常,B端字段少于A端,A端多的字段值丢失(被B端忽略)。
    • 如果B端增加一个字段,A端不变,反序列化正常,B端增加的字段被赋予初始值。
    展开全文
  • java.io.ObjectOutputStream是实现序列化的关键类,它可以将一个对象转换成二进制流,然后可以通过ObjectInputStream将二进制流还原成对象。 在阅读ObjectOutputStream源码之前,我们先来回顾一下序列化相关的基础...

    这篇文章是博主阅读源码之后根据自己的理解写出来的,由于网上ObjectOutputStream源码分析文章很少且大多并不详细,所以只分析了一小部分,可能会有错误或描述的不到位的地方,欢迎指出。

    一、引言

    java.io.ObjectOutputStream是实现序列化的关键类,它可以将一个对象转换成二进制流,然后可以通过ObjectInputStream将二进制流还原成对象。

    在阅读ObjectOutputStream源码之前,我们先来回顾一下序列化相关的基础知识:
    1、需要序列化的类必须实现java.io.Serializable接口,否则会抛出NotSerializableException异常
    2、如果检测到反序列化后的类的serialVersionUID和对象二进制流的serialVersionUID不同,则会抛出
    异常。
    3、Java的序列化会将一个类包含的引用中所有的成员变量保存下来(深度复制),所以里面的引用类型必须也要实现java.io.Serializable接口。
    4、对于不采用默认序列化或无须序列化的成员变量,可以添加transient关键字,并不是说添加了transient关键字就一定不能序列化。
    5、每个类可以实现readObject、writeObject方法实现自己的序列化策略,即使是transient修饰的成员变量也可以手动调用ObjectOutputStream的writeInt等方法将这个成员变量序列化。

    二、使用方法

    我们先来回顾一下ObjectOutputStream的使用方法:

    class Person implements Serializable {
        private static final long serialVersionUID = 1386583756403881124L;
        String name;
        int age;
    }
    
    public class Test {
        public static void main(String[] args) throws IOException {
            FileOutputStream fos = new FileOutputStream("D:\\out.txt");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            Person p = new Person();
            p.name = "LZF";
            p.age = 19;
            oos.writeObject(p);
            oos.close();
        }
    }

    ObjectOutputStream只有一个public权限的构造方法:该构造方法需要传入一个OutputStream表示将对象二进制流写入到指定的OutputStream。
    查看out.txt:输出数据如下:
    这里写图片描述

    三、源码简要分析

    ObjectOutputStream的实现很复杂,建议读者们先对ObjectOutputStream源码的主要方法先过一遍再往下看。
    ObjectOutputStream类定义:

    public class ObjectOutputStream extends OutputStream 
            implements ObjectOutput, ObjectStreamConstants {
        //...
    }

    ObjectOutputStream继承了OutputStream类,实现了ObjectOutput接口和ObjectStreamConstants接口
    ObjectStreamConstants接口并没有定义方法,其内部定义了很多byte类型常量,表示序列化后的单个字节数据的含义。

    了解完这些成员变量后,我们从几个最常用的序列化操作为切入点分析:ObjectOutputStream的构造方法和writeObject方法。
    ObjectOutputStream的构造方法:

    public ObjectOutputStream(OutputStream out) throws IOException {
        //检查继承权限
        verifySubclass();
        //构造一个BlockDataOutputStream用于向out写入序列化数据
        bout = new BlockDataOutputStream(out);
        //构造一个大小为10,负载因子为3的HandleTable和ReplaceTable
        handles = new HandleTable(10, (float) 3.00);
        subs = new ReplaceTable(10, (float) 3.00);
        //恒为false,除非子类调用protected构造方法
        enableOverride = false;
        writeStreamHeader();
        //将缓存模式打开,写入数据时先写入缓冲区
        bout.setBlockDataMode(true);
        if (extendedDebugInfo) {
            debugInfoStack = new DebugTraceInfoStack();
        } else {
            debugInfoStack = null;
        }
    }

    BlockDataOutputStream是ObjectOutputStream的内部类,它将构造ObjectOutputStream传入的OutputStream实例包装起来,当外部类ObjectOutputStream需要向这个OutputStream写入序列化数据时,就由这个类来完成实际的写入操作。

    构造方法首先调用verifySubclass方法分析现在构造的是不是ObjectOutputStream的子类,即:

    private void verifySubclass() {
        Class<?> cl = getClass();
        //如果构造的不是ObjectOutputStream的子类则直接返回
        if (cl == ObjectOutputStream.class)
            return;
        //否则获取安全管理器检查是否有继承ObjectOutputStream的权限
        SecurityManager sm = System.getSecurityManager();
        if (sm == null)
            return;
        //移除Caches中已经失去引用的Class对象
        processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
        //将ObjectOutputStream的子类存入Caches
        WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
    
        Boolean result = Caches.subclassAudits.get(key);
        if (result == null) {
            result = Boolean.valueOf(auditSubclass(cl));
            Caches.subclassAudits.putIfAbsent(key, result);
        }
        if (result.booleanValue())
            return;
        //如果没有权限则抛出SecurityException异常
        sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
    }

    该方法如果识别到构造的是ObjectOutputStream的子类,则会检查是否拥有SUBCLASS_IMPLEMENTATION_PERMISSION权限,否则抛出SecurityException异常。
    另外,ObjectOutputStream通过一个Cache静态内部类中的ConcurrentHashMap来缓存ObjectOutputStream子类信的息。Class类通过内部类WeakClassKey(继承WeakReference,将一个弱引用指向一个Class对象)存储。

    private static class Caches {
        static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits = new ConcurrentHashMap<>();
        static final ReferenceQueue<Class<?>> subclassAuditsQueue = new ReferenceQueue<>();
    }

    在进行完ObjectOutputStream的类型检查后,构造方法会随之构建一个BlockDataOutputStream用于向传入的OutputStream写入对象信息,并构建长度为10,负载因子为3的HandleTable和ReplaceTable。随后,将魔数(0xACED)和版本标识符(0x0005)写入文件头,用来检测是不是一个序列化对象。

    protected void writeStreamHeader() throws IOException {
        bout.writeShort(STREAM_MAGIC); //写入两个字节:0xAC和0xED
        bout.writeShort(STREAM_VERSION); //写入两个字节:0x00和0x05
    }

    最后根据sun.io.serialization.extendedDebugInfo配置信息决定是否启用调式信息栈。

    private static final boolean extendedDebugInfo =
            java.security.AccessController.doPrivileged(
                new sun.security.action.GetBooleanAction(
                    "sun.io.serialization.extendedDebugInfo")).booleanValue();

    如果extendedDebugInfo为true,则构造方法会构造一个DebugTraceInfoStack,否则置为null。

    构造完ObjectOutputStream对象后,我们一般会随之调用writeObject(Object)方法将对象写入

    public final void writeObject(Object obj) throws IOException {
        //在ObjectOutputStream中这个变量恒为false,只有子类为true
        if (enableOverride) {
            //实现为空,供子类重写用
            writeObjectOverride(obj);
            return;
        }
        try {
            writeObject0(obj, false);
        } catch (IOException ex) {
            if (depth == 0)
                writeFatalException(ex);
            throw ex;
        }
    }

    writeObject方法首先会检查是否是ObjectOutputStream的子类,如果是则调用writeObjectOverride方法,这个方法默认实现为空,需要子类根据实际业务需求定制序列化方法。
    随后调用writeObject0方法

    private void writeObject0(Object obj, boolean unshared) throws IOException {
        //关闭缓冲模式,直接向目标OutputStream写入数据
        boolean oldMode = bout.setBlockDataMode(false);
        depth++;
        try {
            int h;
            //处理以前写过的和不可替换的对象
            //如果obj为null(只有当obj为null时才会返回null)
            if ((obj = subs.lookup(obj)) == null) {
                writeNull();
                return;
            } else if (!unshared && (h = handles.lookup(obj)) != -1) {
                writeHandle(h);
                return;
            } else if (obj instanceof Class) {
                writeClass((Class) obj, unshared);
                return;
            } else if (obj instanceof ObjectStreamClass) {
                writeClassDesc((ObjectStreamClass) obj, unshared);
                return;
            }
    
            Object orig = obj;
            Class<?> cl = obj.getClass();
            //序列化对象对应的Class对象的详细信息
            ObjectStreamClass desc;
            for (;;) {
                Class<?> repCl;
                //获取序列化对象对应的Class对象详细信息,待会会讨论ObjectStreamClass
                desc = ObjectStreamClass.lookup(cl, true);
                //直接break,因为最后(repCl=obj.getClass())==null恒等于true(我也不知道为什么这里要有for循环)
                if (!desc.hasWriteReplaceMethod() ||
                        (obj = desc.invokeWriteReplace(obj)) == null ||
                        (repCl = obj.getClass()) == cl)
                        break;
                    cl = repCl;
            }
            if (enableReplace) {
                //replaceObject用来替换这个对象进行序列化,默认实现为空,一般用于子类重写实现序列化的定制
                Object rep = replaceObject(obj);
                //如果对象被替换了
                if (rep != obj && rep != null) {
                    cl = rep.getClass();
                    //重新查找对应的ObjectStreamClass
                    desc = ObjectStreamClass.lookup(cl, true);
                }
                obj = rep;
            }
    
            //如果对象被替换了(非ObjectOutputStream子类不会发生)
            if (obj != orig) {
                subs.assign(orig, obj);
                if (obj == null) {
                    writeNull();
                    return;
                } else if (!unshared && (h = handles.lookup(obj)) != -1) {
                    writeHandle(h);
                    return;
                } else if (obj instanceof Class) {
                    writeClass((Class) obj, unshared);
                    return;
                } else if (obj instanceof ObjectStreamClass) {
                    writeClassDesc((ObjectStreamClass) obj, unshared);
                    return;
                }
            }
    
            //序列化对象类型为String、数组、枚举时,调用定制的写入方法
            if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum<?>) obj, desc, unshared);
            //一般对象类型的写入(当然需要实现序列化接口)
            } else if (obj instanceof Serializable) {
                writeOrdinaryObject(obj, desc, unshared);
            //如果没有实现序列化接口会抛出异常
            } else {
                if (extendedDebugInfo)
                    throw new NotSerializableException(cl.getName() + "\n" + debugInfoStack.toString());
                else
                    throw new NotSerializableException(cl.getName());
            }
        } finally {
            //结束方法前将方法栈深减去1
            depth--;
            bout.setBlockDataMode(oldMode);
        }
    }

    ObjectStreamClass存储了一个Class对象的信息,其实例变量包括:Class对象,Class名称,serialVersionUID,实现了Serializable接口还是 Externalizable接口,非transient修饰的变量,自定义的writeObject和readObject的Method对象。

    下面来看ObjectStreamClass的lookup方法:

    static ObjectStreamClass lookup(Class<?> cl, boolean all) {
        //如果all为false且cl并没有实现Serializable接口则直接返回null
        if (!(all || Serializable.class.isAssignableFrom(cl))) {
            return null;
        }
        //清除失去Class引用的ObjectStreamClass缓存
        //(缓存的用途是避免反复对同一个Class创建ObjectStreamClass对象)
        processQueue(Caches.localDescsQueue, Caches.localDescs);
        //创建一个临时的WeakClassKey用于从缓存中查找对应的ObjectStreamClass或EntryFuture
        WeakClassKey key = new WeakClassKey(cl, Caches.localDescsQueue);
        //获取保存有ObjectStreamClass或EntryFuture的引用
        Reference<?> ref = Caches.localDescs.get(key);
        Object entry = null;
        //如果引用不为null则直接获取其中的对象给entry
        if (ref != null) {
            entry = ref.get();
        }
        EntryFuture future = null;
        //如果引用的对象被GC
        if (entry == null) {
            //创建一个EntryFuture对象并将软引用newRef指向它
            EntryFuture newEntry = new EntryFuture();
            Reference<?> newRef = new SoftReference<>(newEntry);
            do {
                //从缓存中删除这个失去引用的键值对
                if (ref != null)
                    Caches.localDescs.remove(key, ref);
                //将被newRef引用的EntryFuture添加到缓存(这里使用putIfAbsent而不是put可能是为了防止有其它线程已经添加了)
                ref = Caches.localDescs.putIfAbsent(key, newRef);
                if (ref != null)
                    entry = ref.get();
            //循环直到ref为null或entry不为null
            } while (ref != null && entry == null);
            //如果entry为null
            if (entry == null)
                future = newEntry;
        }
        //如果从缓存中拿到了ObjectStreamClass
        if (entry instanceof ObjectStreamClass) {
            return (ObjectStreamClass) entry;
        }
        //如果从缓存中得到了EntryFuture
        if (entry instanceof EntryFuture) {
            future = (EntryFuture) entry;
            //如果创建这个EntryFuture的线程就是当前线程,即这个EntryFuture
            //是在前面的代码ref = Caches.localDescs.putIfAbsent(key, newRef);语句中设置的
            if (future.getOwner() == Thread.currentThread()) {
                entry = null;
            } else {
                entry = future.get();
            }
        }
        //如果entry为null那么就创建一个新的ObjectStreamClass对象并加入缓存
        if (entry == null) {
            try {
                entry = new ObjectStreamClass(cl);
            } catch (Throwable th) {
                entry = th;
            }
            //设置这个ObjectStreamClass实例
            if (future.set(entry)) {
                Caches.localDescs.put(key, new SoftReference<Object>(entry));
            } else {
                entry = future.get();
            }
        }
        //最后如果entry为ObjectOutputStream则直接返回,否则抛出异常
        if (entry instanceof ObjectStreamClass) {
            return (ObjectStreamClass) entry;
        } else if (entry instanceof RuntimeException) {
            throw (RuntimeException) entry;
        } else if (entry instanceof Error) {
            throw (Error) entry;
        } else {
            throw new InternalError("unexpected entry: " + entry);
        }
    }

    在ObjectStreamClass类的内部类Caches中,存在一个类型为ConcurrentMap的静态成员变量localDescs:

    static final ConcurrentMap<WeakClassKey,Reference<?>> localDescs = new ConcurrentHashMap<>();
    private static final ReferenceQueue<Class<?>> localDescsQueue = new ReferenceQueue<>();

    ObjectStreamClass引入这个缓存主要是为了提高获取类信息的速度,如果反复对一个类的实例们进行序列化操作,那么只需要实例化一个ObjectStreamClass实例并导入这个缓存。
    WeakClassKey继承WeakReference,将一个弱引用指向这个Class对象,当对应的ClassLoader失去引用时,不至于导致垃圾回收器无法回收这个Class对象。
    引用队列localDescsQueue主要用于processQueue方法清除localDescs中无用的缓存。

    至于ObjectStreamClass的内部类EntryFuture的作用,我个人认为是为了实现多线程调用lookup方法而设立的。

    private static class EntryFuture {
        private static final Object unset = new Object();
        private final Thread owner = Thread.currentThread();
        private Object entry = unset;
    
        //entry是ObjectStreamClass实例
        synchronized boolean set(Object entry) {
            if (this.entry != unset)
                    return false;
            this.entry = entry;
            //entry已被设置,唤醒正在调用get方法的线程
            notifyAll();
            return true;
        }
    
        synchronized Object get() {
            boolean interrupted = false;
            while (entry == unset) {
                try {
                    //等待到entry被set为止
                    wait();
                } catch (InterruptedException ex) {
                    interrupted = true;
                }
            }
            //如果被强制打断则返回null
            if (interrupted) {
                AccessController.doPrivileged(
                    new PrivilegedAction<Void>() {
                        public Void run() {
                            Thread.currentThread().interrupt();
                            return null;
                        }
                    });
            }
            //如果是正常被set方法唤醒的则直接返回设置好的ObjectStreamClass
            return entry;
        }
        //返回创建这个EntryFuture的线程
        Thread getOwner() {
            return owner;
        }
    }

    现在回到writeObject0方法中
    在获取到ObjectStreamClass对象后,会判断需要序列化的类是哪种类型。
    下面我们就只分析writeOrdinaryObject方法:

    private void writeOrdinaryObject(Object obj, ObjectStreamClass desc,
             boolean unshared) throws IOException {
        if (extendedDebugInfo)
            debugInfoStack.push(
                    (depth == 1 ? "root " : "") + "object (class \"" +
                    obj.getClass().getName() + "\", " + obj.toString() + ")");
        try {
            //检查ObjectStreamClass对象
            desc.checkSerialize();
            //写入字节0x73
            bout.writeByte(TC_OBJECT);
            //写入对应的Class对象的信息
            writeClassDesc(desc, false);
            handles.assign(unshared ? null : obj);
            if (desc.isExternalizable() && !desc.isProxy()) {
                writeExternalData((Externalizable) obj);
            } else {
                //写入这个对象变量信息及其父类的成员变量
                writeSerialData(obj, desc);
            }
        } finally {
            if (extendedDebugInfo) {
                debugInfoStack.pop();
            }
        }
    }

    writeOrdinaryObject最终会以一种递归的形式写入对象信息。
    writeSerialData方法会将这个实例及其父类基本数据类型写入文件,如果检测到有引用类型,那么会继续调用writeObject0方法写入,直到将这个对象包含的所有信息全部序列化为止。

    暂时就只能分析到这里了。

    展开全文
  • OutputStream:FileOutputStream、ByteArrayOutputStream、ObjectOutputStream 5.字符流     Reader:FileReader、BufferedReader、InputStreamReader    ...
  • ObjectOutputStream的使用

    千次阅读 2014-09-28 10:16:37
    ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。 ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,可以为应用程序提供对...

    一、ObjectOutputStream jdk文档解析

    ObjectOutputStream将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。

    只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。

    writeObject 方法用于将对象写入流中。所有对象(包括 String 和数组)都可以通过 writeObject 写入。可将多个对象或基元写入流中。必须使用与写入对象时相同的类型和顺序从相应 ObjectInputstream 中读回对象。

    还可以使用 DataOutput 中的适当方法将基本数据类型写入流中。还可以使用 writeUTF 方法写入字符串。

    对象的默认序列化机制写入的内容是:对象的类,类签名,以及非瞬态和非静态字段的值。其他对象的引用(瞬态和静态字段除外)也会导致写入那些对象。可使用引用共享机制对单个对象的多个引用进行编码,这样即可将对象的图形恢复为最初写入它们时的形状。  


    二、例子

    1、写一个用于保存到本地的类,并实现serializable接口。

    2、创建一个File对象,指向本地的一个文件。

    3、把刚创建的File对象封装成一个字节流对象FileOutputStream。

    4、把FileOutputStream作为ObjectOutputStream对象的参数并创建ObjectOutputStream对象。

    5、使用writeObject方法把实现了serializable接口的类对象保存到本地。


    实现serializable接口的class

    	public class Student implements Serializable
    	{
    		public int age;
    		public String name;
    	}
    	


    Main方法写法

                    Student student = new MainApp().new Student();
    		student.age = 10;
    		student.name = "xiejunjie";
    		
    
    		File file = new File("obj.txt");
    		if(!file.exists())
    		{
    			file.createNewFile();
    		}
    		FileOutputStream out = new FileOutputStream(file);
    		ObjectOutputStream oos = new ObjectOutputStream(out);
    		oos.writeObject(student);
    		oos.close();
    		out.close();


    序列化后,本地就产生了一个obj.txt文件,打开文件的内容可以看到下面的一些东西,隐隐约约可以看到是刚才的Student对象的内容




    展开全文
  • 在我们讲解对象输出流和对象输入流之前,我们首先要明确两个概念: - 序列化和反序列化: - 将一个特定的数据结构转换为一组字节的过程称之为序列化 ... ObjectOutputStream是一个高级流, 将 Jav

    在我们讲解对象输出流和对象输入流之前,我们首先要明确两个概念:
    - 序列化和反序列化:
    - 将一个特定的数据结构转换为一组字节的过程称之为序列化
    - 将一组字节转换为特定的数据结构的过程称之为反序列化
    - 持久化:
    - 将数据写入硬盘长久保存的过程称之为持久化


    ObjectOutputStream的使用

    ObjectOutputStream是一个高级流, 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。

    注意:只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。


    构造函数

    //为完全重新实现 ObjectOutputStream 的子类提供一种方法,让它不必分配仅由 ObjectOutputStream 的实现使用的私有数据。
    protected ObjectOutputStream();
    
    //创建写入指定 OutputStream 的 ObjectOutputStream。此构造方法将序列化流部分写入底层流;调用者可以通过立即刷新流,确保在读取头部时,用于接收 ObjectInputStreams 构造方法不会阻塞。
    public ObjectOutputStream(OutputStream out);

    常用的方法

    我们这里重点讲述的是将一个对象实例化之后存储到文件中,其他的write()方法请参看API。

    //将指定的对象写入 ObjectOutputStream。对象的类、类的签名,以及类及其所有超类型的非瞬态和非静态字段的值都将被写入。
    public final void writeObject(Object obj);
    

    将对象写入到文件中永久存储

    Person类

    import java.io.Serializable;
    import java.util.ArrayList;
    
    //Person类实现了Serializable接口,所以该类才能被序列化;反之,如果没有实现该接口的类则不能被序列化。
    public class Person implements Serializable{
        /**
         * 序列化的ID,只要加了该版本号,在反序列化的时候不论你的类的属性是否改变,只要是版本号不变那么尽经可能的兼容新版本。
         * 如果版本号改变了,那么反序列化的过程中就会抛出异常。
         */
        private static final long serialVersionUID = 6871740251451383067L;
        private String name;
        private int age;
        private char sex;
        private ArrayList<String> other;
    
        public Person(){
    
        }
        public Person(String name, int age, char sex, ArrayList<String> other) {
            super();
            this.name = name;
            this.age = age;
            this.sex = sex;
            this.other = other;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age)throws Exception {
            this.age = age;
        }
        public char getSex() {
            return sex;
        }
        public void setSex(char sex) {
            this.sex = sex;
        }
        public ArrayList<String> getOther() {
            return other;
        }
        public void setOther(ArrayList<String> other) {
            this.other = other;
        }
        public static long getSerialversionuid() {
            return serialVersionUID;
        }
    
    
    }
    

    将Person对象写入到文件中

    import java.io.FileOutputStream;
    import java.io.ObjectOutputStream;
    import java.util.ArrayList;
    
    import tools.Person;
    
    /**
     * ObjectOutputStream:高级流,对象输出流,只能将支持 java.io.Serializable 接口的对象写入流中
     *
     * 将一个特定的数据结构转换为一组字节的过程称之为序列化
     * 将一组字节转换为特定的数据结构的过程称之为反序列化
     * 将数据写入硬盘长久保存的过程称之为持久化
     *
     * @author Administrator
     *
     */
    public class OOSDemo01 {
        public static void main(String[] args){
            try {
                ArrayList<String> other=new ArrayList<String>();
                other.add("清华大学");
                other.add("软件学院");
                other.add("软件工程");
                Person person=new Person("小明", 22, '男', other);
                FileOutputStream fos=new FileOutputStream("OOS.txt");
                ObjectOutputStream oos=new ObjectOutputStream(fos);
                //将一个对象经行序列化时需要实现Serializable接口
                oos.writeObject(person);
                oos.flush();
                oos.close();
                System.out.println("对象写入成功!");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    ObjectInputStream的使用

    ObjectInputStream也是一个高级流,对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。


    构造函数

    //为完全重新实现 ObjectInputStream 的子类提供一种方式,让它不必分配仅由 ObjectInputStream 的实现使用的私有数据。
    protected ObjectInputStream();
    
    //创建从指定 InputStream 读取的 ObjectInputStream。从流读取序列化头部并予以验证。在对应的 ObjectOutputStream 写入并刷新头部之前,此构造方法将阻塞。
    public ObjectInputStream(InputStream in);
    

    常用的方法

    我们这里;里主要讲述的是将已经序列化之后存储在文件中的对象反序列化的过程,其他的read()方法请参看API。

    //从 ObjectInputStream 读取对象。对象的类、类的签名和类及所有其超类型的非瞬态和非静态字段的值都将被读取。
    public final Object readObject();

    注意看,以上的方法返回的是一个Object,所以我们在反序列化的过程中需要强制转换为我们所需要的类型。

    将已经序列化的对象反序列化

    import java.io.FileInputStream;
    import java.io.ObjectInputStream;
    import java.util.ArrayList;
    
    import tools.Person;
    
    /**
     * ObjectInputStream:高级流,对象输入流
     * ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
     * 有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。
     * @author Administrator
     *
     */
    public class OISDemo01 {
        public static void main(String[] args){
            try {
                FileInputStream fis=new FileInputStream("OOS.txt");
                ObjectInputStream ois=new ObjectInputStream(fis);
                Person person=(Person)ois.readObject();
                System.out.println(person.getName());
                System.out.println(person.getAge());
                System.out.println(person.getSex());
                ArrayList<String> other=person.getOther();
                for (String string : other) {
                    System.out.println(string);
                }
                ois.close();
                System.out.println("反序列化成功!");
            } catch (Exception e) {
                    e.printStackTrace();
            }
            System.exit(0);
        }
    }
    

    运行结果:

    运行结果


    小结

    ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,可以为应用程序提供对对象图形的持久存储。ObjectOutputStream用于序列化对象,ObjectInputStream 用于恢复那些以前序列化的对象(反序列化)。其他用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。


    以上内容只代表我个人的观点,有什么错误的地方请各路大神指正!转载请注明出处!谢谢!

    每天进步一点点!

    展开全文
  • Java objectOutputStream的用法

    万次阅读 2015-11-10 22:38:14
    ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上...
  • ObjectOutputStream

    2010-12-02 13:06:00
    <br />一个对象如果要进行输出,则必须使用ObjectOutputStream类,此类的定义如下:public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants  ...
  • 对象操作流ObjectOutputStream

    千次阅读 2019-02-12 09:57:43
    1.什么是对象操作流 该流可以将一个对象写出,或者读取一个对象到程序中,也就是执行了序列化和反序列化操作。 2.使用方式 前提:需要被序列化和反序列化的类必须实现Serializable 接口。 将对象写出到硬盘上的...
  • 1.5 ObjectOutputStream的用法 马 克-to-win:顾名思义,ObjectOutputStream一定是用来往输出流上写用户自定义的对象的。比如数据库中某表的一行数据对应一个对 象,这时可通过这种方法存在硬盘上。一定要注意...
  • 1).序列化 把对象转化为字节序列,这个过程称之为对象序列化 与之相反的称之为反... <1>.ObjectOutputStream 对象的序列化,通过java.io.Serializable接口实现序列化,如果不序列化则会报NotSerializableException
  • ObjectOutputStream 将 Java 对象的基本数据类型图形写入 OutputStream。可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上...
  • ObjectOutputStream是将一个对象写入文件:如果使用这个类写入对象,这个对象需要序列化(实现接口Serializable) ObjectInputStream是从文件中读一个对象 1.案例:将对象写入文件 //创建对象,类的创建省略,类中有...
  • 对象流,是一组高级流。特点:可以方便的读写java中的任何对象。 * OOS:对象输出流,可以将给定的对象转换为一组字节然后写出。 * OIS:对象输入流,可以将读取回来的一组字节转换为对应的对象。...
  •  对象的输出流: ObjectOutputStream  对象的输入流 : ObjectInputStream 使用: 对象的输出流将指定的对象写入到流(文件等)的过程,也就是将对象序列化的过程, 对象的输入流将指定序列化好的对象从流(文件等)读...
  • 什么是IO流?  byte序列的读写,Java中的IO流是实现输入/输出的基础. Java将数据从源(文件、内存、键盘、网络)读入到内存 中,形成了流,然后...主要有以下几种方式:按照数据流方向、数据处理的单位功能。 不管
  • 可以使用ObjectOutputStream进行对象的序列化操作。 可以使用ObjectInputStream进行对象的反序列化操作。 掌握Externalizable接口的作用及与Serializable接口的实现区别。 掌握transient关键字的作用。 可以序列化一...
  • ObjectInputStream和ObjectOutputStream类创建的对象被称为对象输入流对象输出流。 创建文件输出流代码:
  • 1、对象序列化,类实现Serializable接口 不需要序列化的属性,使用transient声明 2、使用套接字流在主机之间传递对象注意问题: 学习自:Socket同时使用ObjectInputStream和ObjectOutputStream传输序列化对象时的...
  • 在发送了 一个消息 你好 一个消息 我是xxx 之后,另一端只能收到两次你好 解决办法就是在 ObjectOutputStream的对象发送之前调用 reset()方法,然后 flush; 代码如下: package com.net; import jav
  • 2019独角兽企业重金招聘Python工程师标准>>> ...
  • 代码 package ... import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream;...import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.i...
  • 很多时候需要将一些类对象本地化,例如我们训练了某个学习器,学习器在面向对象编程... jdk为我们提供了两个过滤流分别是ObjectOutputStream和ObjectInputStream,前者用于将对象序列化,通常写为.dat文件,后者用...
  • 如果使用上面的代码,先运行Server端,再运行Client端,然后在Client端输入123,回车,再输入456,则Server端会显示: A 对你说:123 A 对你说:123 如果按我代码中注释部分所述,把创建对象放到循环里面,再次运行...
  • package com.qiqiao.test2; import java.io.File; import java.io.FileInputStream; ...import java.io.FileOutputStream;...import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /*...
  • ObjectOutputStream 可能引起的内存泄漏

    千次阅读 2014-07-06 19:18:56
    使用 ObjectOutputStream 来进行对象序列化 相信大多数程序员在使用 Java 进行日常开发工作中,都曾经遇到需要把数据进行序列化的情况,比如写入文件或者写入 socket 流。Java 的类库也提供了丰富工具类供我们使用...
  • ByteArrayOutputStream是把对象序列化成字节流,或者字节数组的形式对吗?可是我用ObjectOutputStream对对象进行序列化的时候,发现最后写成的...还有,ByteArrayOutputSteam可以不依赖于ObjectOutputStream使用吗?
  • 对象可以被表示为一个字节序列,字节序列包括该对象的数据、有关对象的类型信息存储在对象中的数据类型。 将序列化的对象写入文件后,可以从文件中读取出来,并且对它进行反序列化也就是说,对象的类型信息、对象的...

空空如也

1 2 3 4 5 ... 20
收藏数 64,079
精华内容 25,631
关键字:

objectoutputstream