再学java基础(4)Object流、Serializable序列化、transient

来源:互联网 发布:python 调用c dll 编辑:程序博客网 时间:2024/06/08 18:18
import java.io.*;public class TestObjectIO {public static void main(String args[]) throws Exception {T t = new T();t.k = 8; // k 原来是 15,现在 改成 8 了。FileOutputStream fos = new FileOutputStream("d:/share/java/io/testobjectio.dat");ObjectOutputStream oos = new ObjectOutputStream(fos); // 可以把 Object 类 写进去。oos.writeObject(t);oos.flush();oos.close();FileInputStream fis = new FileInputStream("d:/share/java/io/testobjectio.dat");  // 读取出来。ObjectInputStream ois = new ObjectInputStream(fis);T tReaded = (T)ois.readObject();System.out.println(tReaded.i + " " + tReaded.j + " " + tReaded.d + " " + tReaded.k);}}class T implements Serializable{int i = 10;int j = 9;double d = 2.3;transient int k = 15;}


Java关键字(keywords) 

    abstract    default    if            private      this 
    boolean     do         implements    protected    throw 
    break       double     import        public       throws 
    byte        else       instanceof    return       transient 
    case        extends    int           short        try 
    catch       final      interface     static       void 
    char        finally    long          strictfp     volatile 
    class       float      native        super        while 
    const       for        new           switch 
    continue    goto       package       synchronized


以上是java specifications中定义的keywords,一共48个,其中常见的三个看似是关键字的true, false, null,都不是关键字,而是作为一个单独标识类型。 
其中,不常用到的关键字有:const,goto,native,strictfp,transient,volatile。 
constgoto为java中的保留字。 
1. native 
native是方法修饰符。Native方法是由另外一种语言(如c/c++,FORTRAN,汇编)实现的本地方法。因为在外部实现了方法,所以在java代码中,就不需要声明了,有点类似于借口方法。Native可以和其他一些修饰符连用,但是abstract方法和Interface方法不能用native来修饰。 
Example: 
Java代码  收藏代码
  1. public interface TestInterface {  
  2.      void doMethod();  
  3. }  
  4. public class Test implements TestInterface {  
  5.     public native void doMethod();  
  6.     private native int doMethodB();  
  7.   public native synchronized String doMethodC();  
  8.   static native void doMethodD();  
  9. }  

为什么需要使用native method?请参考: 
http://www.iteye.com/topic/72543  java Native Method初涉 
2. strictfp 
修饰类和方法,意思是FP-strict,精确浮点,符合IEEE-754规范的。当一个class或interface用strictfp声明,内部所有的float和double表达式都会成为strictfp的。Interface method不能被声明为strictfp的,class的可以。 
Example: 
Java代码  收藏代码
  1. strictfp interface FPTest {  
  2.      void methodA();  
  3. }  
  4. class FPClass implements FPTest {  
  5.     public void methodA() {  
  6.     }  
  7.     public void methodB() {  
  8.   }  
  9.   public strictfp void methodC() {  
  10.   }  
  11. }  
  12. class FPClassB {  
  13.     strictfp void methodA() {  
  14.     }  
  15. }  

3.transient 
变量修饰符。标记为transient的变量,在对象存储时,这些变量状态不会被持久化。当对象序列化的保存在存储器上时,不希望有些字段数据被保存,为了保证安全性,可以把这些字段声明为transient。 
4. volatile 
volatile修饰变量。在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。 
看看Java Language Specification中的例子。 
条件:一个线程不停的调用方法one(),一个线程不停的调用方法two()。我测试过多次,这种情况好像一直没有出现。 
Java代码  收藏代码
  1. class Test {  
  2.     static int i = 0, j = 0;  
  3.     static void one() { i++; j++; }  
  4.     static void two() {  
  5.         System.out.println("i=" + i + " j=" + j);  
  6.     }  
  7. }  

结果偶尔会出现j大于i的情况,因为方法没有同步,所以会出现i和j可能不是一次更新。一种防止这种情况发生的办法就是声明两个方法为synchronized 的。 
Java代码  收藏代码
  1. class Test {  
  2.     static int i = 0, j = 0;  
  3.     static synchronized void one() { i++; j++; }  
  4.     static synchronized void two() {  
  5.         System.out.println("i=" + i + " j=" + j);  
  6.     }  
  7. }  

这样可以防止两个方法同时被执行,还可以保证j和i被同时更新,这样一来i和j的值一直是一样的。 
另外一种途径就是把i和j声明为volatile。 
Java代码  收藏代码
  1. class Test {  
  2.     static volatile int i = 0, j = 0;  
  3.     static void one() { i++; j++; }  
  4.     static void two() {  
  5.         System.out.println("i=" + i + " j=" + j);  
  6.     }  
  7. }  

        Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。

package com.itm.Io;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.util.Date;public class LoggingInfo implements java.io.Serializable {private Date loggingDate = new Date();private String uid;private transient String pwd;
// transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候///,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。LoggingInfo(String user, String password) {uid = user;pwd = password;}public String toString() {String password = null;if (pwd == null) {password = "NOT SET";} else {password = pwd;}return "logon info: /n   " + "user: " + uid + "/n   logging date : "+ loggingDate.toString() + "/n   password: " + password;}public static void main(String[] args) {LoggingInfo logInfo = new LoggingInfo("MIKE", "MECHANICS");System.out.println(logInfo.toString());try {ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("logInfo.out"));o.writeObject(logInfo);o.close();} catch (Exception e) {// deal with exception}// To read the object back, we can writetry {ObjectInputStream in = new ObjectInputStream(new FileInputStream("logInfo.out"));LoggingInfo logInfo1 = (LoggingInfo) in.readObject();System.out.println(logInfo1.toString());} catch (Exception e) {// deal with exception}}}


如果我们运行这段代码,我们会注意到从磁盘中读回(read——back (de-serializing))的对象打印password为"NOT SET"。这是当我们定义pwd域为transient时,所期望的正确结果。
现在,让我们来看一下粗心对待transient域可能引起的潜在问题。假设我们修改了类定义,提供给transient域一个默认值,
代码如下:
 
public class GuestLoggingInfo implements java.io.Serializable 

    private Date loggingDate = new Date(); 
    private String uid; 
    private transient String pwd; 
    
    GuestLoggingInfo() 
    { 
        uid = "guest"; 
        pwd = "guest"; 
    } 
    public String toString() 
    { 
        //same as above 
     } 

现在,如果我们串行化GuestLoggingInfo的一个实例,将它写入磁盘,并且再将它从磁盘中读出,我们仍然看到读回的对象打印password 为 "NOT SET"。当从磁盘中读出某个类的实例时,实际上并不会执行这个类的构造函数,而是载入了一个该类对象的持久化状态,并将这个状态赋值给该类的另一个对象


可以借鉴一下:http://blog.csdn.net/moreevan/article/details/6697777。

序列化的作用:

1、序列化是干什么的?
       简单说就是为了保存在内存中的各种对象的状态(也就是实例变量,不是方法),并且可以把保存的对象状态再读出来。虽然你可以用你自己的各种各样的方法来保存object states,但是Java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。

2、什么情况下需要序列化   
    a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
    b)当你想用套接字在网络上传送对象的时候;
    c)当你想通过RMI传输对象的时候;

3、当对一个对象实现序列化时,究竟发生了什么?
    在没有序列化前,每个保存在堆(Heap)中的对象都有相应的状态(state),即实例变量(instance ariable)比如:
   

java 代码
  1. Foo  myFoo = new Foo();  
  2. myFoo .setWidth(37);  
  3. myFoo.setHeight(70);  

      
       当 通过下面的代码序列化之后,MyFoo对象中的width和Height实例变量的值(37,70)都被保存到foo.ser文件中,这样以后又可以把它 从文件中读出来,重新在堆中创建原来的对象。当然保存时候不仅仅是保存对象的实例变量的值,JVM还要保存一些小量信息,比如类的类型等以便恢复原来的对 象。

java 代码
  1. FileOutputStream fs = new FileOutputStream("foo.ser");  
  2. ObjectOutputStream os = new ObjectOutputStream(fs);  
  3. os.writeObject(myFoo);  


4、实现序列化(保存到一个文件)的步骤
       
a)Make a FileOutputStream            

java 代码
  1. FileOutputStream fs = new FileOutputStream("foo.ser");    
       b)Make a ObjectOutputStream            
java 代码
  1. ObjectOutputStream os =  new ObjectOutputStream(fs);   

       c)write the object

java 代码
  1. os.writeObject(myObject1);  
  2. os.writeObject(myObject2);  
  3. os.writeObject(myObject3);  

    d) close the ObjectOutputStream

java 代码
  1. os.close();  



5、举例说明

java 代码
  1. import java.io.*;
  2.   
  3. public class  Box implements Serializable  
  4. {  
  5.     private int width;  
  6.     private int height;  
  7.   
  8.     public void setWidth(int width){  
  9.         this.width  = width;  
  10.     }  
  11.     public void setHeight(int height){  
  12.         this.height = height;  
  13.     }  
  14.   
  15.     public static void main(String[] args){  
  16.         Box myBox = new Box();  
  17.         myBox.setWidth(50);  
  18.         myBox.setHeight(30);  
  19.   
  20.         try{  
  21.             FileOutputStream fs = new FileOutputStream("foo.ser");  
  22.             ObjectOutputStream os =  new ObjectOutputStream(fs);  
  23.             os.writeObject(myBox);  
  24.             os.close();  
  25.         }catch(Exception ex){  
  26.             ex.printStackTrace();  
  27.         }  
  28.     }  
  29.       
  30. }  

6、相关注意事项
    a)序列化时,只对对象的状态进行保存,而不管对象的方法;
    b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
    c)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
    d)并非所有的对象都可以序列化,,至于为什么不可以,有很多原因了,比如:
        1.安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行rmi传输  等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的。
       2. 资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存,也无法对他们进行重新的资源分  配,而且,也是没有必要这样实现。