`
long_yu2
  • 浏览: 311869 次
社区版块
存档分类
最新评论

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

 
阅读更多


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. publicinterfaceTestInterface{
  2. voiddoMethod();
  3. }
  4. publicclassTestimplementsTestInterface{
  5. publicnativevoiddoMethod();
  6. privatenativeintdoMethodB();
  7.   publicnativesynchronizedStringdoMethodC();
  8.   staticnativevoiddoMethodD();
  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. strictfpinterfaceFPTest{
  2. voidmethodA();
  3. }
  4. classFPClassimplementsFPTest{
  5. publicvoidmethodA(){
  6. }
  7. publicvoidmethodB(){
  8.   }
  9.   publicstrictfpvoidmethodC(){
  10.   }
  11. }
  12. classFPClassB{
  13. strictfpvoidmethodA(){
  14. }
  15. }

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

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

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

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



如果我们运行这段代码,我们会注意到从磁盘中读回(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. FoomyFoo=newFoo();
  2. myFoo.setWidth(37);
  3. myFoo.setHeight(70);


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

java 代码
  1. FileOutputStreamfs=newFileOutputStream("foo.ser");
  2. ObjectOutputStreamos=newObjectOutputStream(fs);
  3. os.writeObject(myFoo);


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

java 代码
  1. FileOutputStreamfs=newFileOutputStream("foo.ser");
b)Make a ObjectOutputStream
java 代码
  1. ObjectOutputStreamos=newObjectOutputStream(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. importjava.io.*;
  2. publicclassBoximplementsSerializable
  3. {
  4. privateintwidth;
  5. privateintheight;
  6. publicvoidsetWidth(intwidth){
  7. this.width=width;
  8. }
  9. publicvoidsetHeight(intheight){
  10. this.height=height;
  11. }
  12. publicstaticvoidmain(String[]args){
  13. BoxmyBox=newBox();
  14. myBox.setWidth(50);
  15. myBox.setHeight(30);
  16. try{
  17. FileOutputStreamfs=newFileOutputStream("foo.ser");
  18. ObjectOutputStreamos=newObjectOutputStream(fs);
  19. os.writeObject(myBox);
  20. os.close();
  21. }catch(Exceptionex){
  22. ex.printStackTrace();
  23. }
  24. }
  25. }

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


分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics