可序列化理解(转载)

豆腐全家 豆腐全家     2022-08-01     490

关键词:

转载地址:http://rabbitsfish-163-com.iteye.com/blog/1121318

什么是序列化 
java中的序列化(serialization)机制能够将一个实例对象的状态信息写入到一个字节流中,使其可以通过socket进行传输、或者持久化存储到数据库或文件系统中;然后在需要的时候,可以根据字节流中的信息来重构一个相同的对象。序列化机制在java中有着广泛的应用,EJB、 RMI等技术都是以此为基础的。 
Java的序列化机制只序列化对象的属性值,而不会去序列化什么所谓的方法。其实这个问题简单思考一下就可以搞清楚,方法是不带状态的,就是一些指令,指令是不需要序列化的,只要你的JVM classloader可以load到这个类,那么类方法指令自然就可以获得。序列化真正需要保存的只是对象属性的值,和对象的类型。 

正确使用序列化机制 
一般而言,要使得一个类可以序列化,只需简单实现java.io.Serializable接口即可。该接口是一个标记式接口,它本身不包含任何内容,实现了该接口则表示这个类准备支持序列化的功能。 
利用对象序列化可以进行对象的“深复制”。 
类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。 
要允许不可序列化类的子类型序列化,可以假定该子类型负责保存和恢复超类型的公用 (public)、受保护的 (protected) 和(如果可访问)包 (package) 字段的状态。仅在子类型扩展的类有一个可访问的无参数构造方法来初始化该类的状态时,才可以假定子类型有此职责。如果不是这种情况,则声明一个类为可序列化类是错误的。该错误将在运行时检测到。 
对象的默认序列化机制写入的内容是:对象的类,类签名,以及非瞬态和非静态字段的值。其他对象的引用(瞬态和静态字段除外)也会导致写入那些对象。可使用引用共享机制对单个对象的多个引用进行编码,这样即可将对象的图形恢复为最初写入它们时的形状。 
序列化操作不写出没有实现 java.io.Serializable 接口的任何对象的字段。不可序列化的 Object 的子类可以是可序列化的。在此情况下,不可序列化的类必须有一个无参数构造方法,以便允许初始化其字段。在此情况下,子类负责保存和恢复不可序列化的类的状态。经常出现的情况是,该类的字段是可访问的(public、package 或 protected),或者存在可用来恢复状态的 get 和 set 方法。 

一个类是可序列化的,意味着这个类的所有成员变量都必须是可以序列化的。一定注意是所有的成员变量,static变量,还有瞬态transient的除外。 

例如: 
public class SerializableB { 

    int b =10 ; 
    
    public SerializableB(int b){ 
        this.b = b ; 
    } 


public class SerializableA extends SerializableB implements Serializable { 

    public SerializableA(int b) { 
        super(b); 
    } 
    /** 
     * 
     */ 
    private static final long serialVersionUID = 1L; 
    public int a = 1 ; 
      



public class TestSerial { 
    
    

    public static void main(String[] args) throws IOException, ClassNotFoundException { 
        SerializableA a = new SerializableA(100); 
        String file = "C:/b.out" ; 
        FileOutputStream out = new FileOutputStream(file) ; 
        ObjectOutputStream objOut = new ObjectOutputStream(out); 
        System.out.println(a.b) ; 
        System.out.println(a.a) ; 
        objOut.writeObject(a); 
        objOut.flush(); 
        objOut.close(); 
        
//        FileInputStream in = new FileInputStream("C:/b.out"); 
//        ObjectInputStream objIn = new ObjectInputStream(in); 
//        SerializableA b =  (SerializableA)objIn.readObject(); 
//        System.out.println(b.b) ; 
//        System.out.println(b.a) ; 
        
    } 



这时候你要是序列化时,由于父类不可序列化,故序列化没有问题,但是反序列化出问题了。 
Exception in thread "main" java.io.InvalidClassException: com.steven.serializable.SerializableA; com.steven.serializable.SerializableA; no valid constructor 
    at java.io.ObjectStreamClass.checkDeserialize(Unknown Source) 
    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source) 
    at java.io.ObjectInputStream.readObject0(Unknown Source) 
    at java.io.ObjectInputStream.readObject(Unknown Source) 
    at com.steven.serializable.TestSerial.main(TestSerial.java:27) 
Caused by: java.io.InvalidClassException: com.steven.serializable.SerializableA; no valid constructor 
    at java.io.ObjectStreamClass.<init>(Unknown Source) 
    at java.io.ObjectStreamClass.lookup(Unknown Source) 
    at java.io.ObjectStreamClass.initNonProxy(Unknown Source) 
    at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source) 
    at java.io.ObjectInputStream.readClassDesc(Unknown Source) 
    ... 4 more 
首先SerializableB  不可序列化。其次反序列化时,由于SerializableB  没有提供一个默认的无参构造器,故报错。你只需要加一个无参构造器构就是正确的。子类会负责调用父类的无参构造器来恢复不可序列化的类的状态。 

Enum 常量的序列化不同于普通的 serializable 或 externalizable 对象。enum 常量的序列化形式只包含其名称;常量的字段值不被传送。为了序列化 enum 常量,ObjectOutputStream 需要写入由常量的名称方法返回的字符串。与其他 serializable 或 externalizable 对象一样,enum 常量可以作为序列化流中后续出现的 back 引用的目标。用于序列化 enum 常量的进程不可定制;在序列化期间,由 enum 类型定义的所有类特定的 writeObject 和 writeReplace 方法都将被忽略。类似地,任何 serialPersistentFields 或 serialVersionUID 字段声明也将被忽略,所有 enum 类型都有一个 0L 的固定的 serialVersionUID。 
基本数据(不包括 serializable 字段和 externalizable 数据)以块数据记录的形式写入 ObjectOutputStream 中。块数据记录由头部和数据组成。块数据部分包括标记和跟在部分后面的字节数。连续的基本写入数据被合并在一个块数据记录中。块数据记录的分块因子为 1024 字节。每个块数据记录都将填满 1024 字节,或者在终止块数据模式时被写入。调用 ObjectOutputStream 方法 writeObject、defaultWriteObject 和 writeFields 最初只是终止所有现有块数据记录。 
Enum 常量的反序列化不同于普通的 serializable 或 externalizable 对象。Enum 常量的序列化形式只包含其名称;不传送常量的字段值。要反序列化 enum 常量,ObjectInputStream 需要从流中读取常量的名称;然后将 enum 常量的基本类型和接收到的常量名称作为参数,调用静态方法 Enum.valueOf(Class, String) 获取反序列化的常量。与其他 serializable 或 externalizable 对象一样,enum 常量可以作为序列化流中随后出现的反向引用的目标。不可以自定义 enum 常量的反序列化进程:在反序列化期间,enum 类型所定义的任何与类有关的 readObject、readObjectNoData 和 readResolve 方法都将被忽略。类似地,任何 serialPersistentFields 或 serialVersionUID 字段声明也将被忽略(所有 enum 类型都有一个固定的 0L 的 serialVersionUID)。 

如下例定义了类Person,并声明其可以序列化。 


Java 代码 1.public class Person implements java.io.Serializable {}  
public class Person implements java.io.Serializable {} 


序列化机制是通过java.io.ObjectOutputStream类和java.io.ObjectInputStream类来实现的。在序列化 (serialize)一个对象的时候,会先实例化一个ObjectOutputStream对象,然后调用其writeObject()方法;在反序列化(deserialize)的时候,则会实例化一个ObjectInputStream对象,然后调用其readObject()方法。下例说明了这一过程。 

Java 代码 1.public void serializeObject(){  
2.     String fileName = "ser.out";  
3.     FileOutputStream fos = new FileOutputStream(fileName);  
4.     ObjectOutputStream oos = new ObjectOutputStream(fos);  
5.     oos.writeObject(new Person());  
6.     oos.flush();  
7.}  
8.  
9.public void deserializeObject(){  
10.     String fileName = "ser.out";  
11.     FileInputStream fos = new FileInputStream(fileName);  
12.     ObjectInputStream oos = new ObjectInputStream(fos);  
13.     Person p = oos.readObject();  
14.}  
public void serializeObject(){ 
     String fileName = "ser.out"; 
     FileOutputStream fos = new FileOutputStream(fileName); 
     ObjectOutputStream oos = new ObjectOutputStream(fos); 
     oos.writeObject(new Person()); 
     oos.flush(); 


public void deserializeObject(){ 
     String fileName = "ser.out"; 
     FileInputStream fos = new FileInputStream(fileName); 
     ObjectInputStream oos = new ObjectInputStream(fos); 
     Person p = oos.readObject(); 


上例中我们对一个Person对象定义了序列化和反序列化的操作。但如果Person类是不能序列化的话,即对不能序列化的类进行序列化操作,则会抛出 java.io.NotSerializableException异常。 
JVM中有一个预定义的序列化实现机制,即默认调用 ObjectOutputStream.defaultWriteObject() 和 ObjectInputStream.defaultReadObject() 来执行序列化操作。如果想自定义序列化的实现,则必须在声明了可序列化的类中实现 writeObject()和readObject()方法。 

几种使用情况 
一般在序列化一个类A的时候,有以下三种情况: 
[list=3] 
•类A没有父类,自己实现了Serializable接口 
•类A有父类B,且父类实现了Serializable接口 
•类A有父类B,但父类没有实现Serializable接口,自己实现了Serializable接口 
[/list] 
对于第一种情况,直接实现Serializable接口即可。 
对于第二种情况,因为父类B已经实现了Serializable接口,故类A无需实现此接口;如果父类实现了writeObject()和 readObject(),则使用此方法,否则直接使用默认的机制。 
对于第三种情况,有一点要特别注意,在父类B中一定要有一个无参的构造函数,这是因为在反序列化的过程中并不会使用声明为可序列化的类A的任何构造函数,而是会调用其没有申明为可序列化的父类B的无参构造函数。 

序列化机制的一些问题 

      •性能问题 

 

      为了序列化类A一个实例对象,所需保存的全部信息如下: 

 

      1. 与此实例对象相关的全部类的元数据(metadata)信息;因为继承关系,类A的实例对象也是其任一父类的对象。因而,需要将整个继承链上的每一个类的元数据信息,按照从父到子的顺序依次保存起来。 

 

      2. 类A的描述信息。此描述信息中可能包含有如下这些信息:类的版本ID(version ID)、表示是否自定义了序列化实现机制的标志、可序列化的属性的数目、每个属性的名字和值、及其可序列化的父类的描述信息。 

 

      3. 将实例对象作为其每一个超类的实例对象,并将这些数据信息都保存起来。 

 

      在RMI等远程调用的应用中,每调用一个方法,都需要传递如此多的信息量;久而久之,会对系统的性能照成很大的影响。 

 

      •版本信息 

 

      当用readObject()方法读取一个序列化对象的byte流信息时,会从中得到所有相关类的描述信息以及示例对象的状态数据;然后将此描述信息与其本地要构造的类的描述信息进行比较,如果相同则会创建一个新的实例并恢复其状态,否则会抛出异常。这就是序列化对象的版本检测。JVM中默认的描述信息是使用一个长整型的哈希码(hashcode)值来表示,这个值与类的各个方面的信息有关,如类名、类修饰符、所实现的接口名、方法和构造函数的信息、属性的信息等。因而,一个类作一些微小的变动都有可能导致不同的哈希码值。例如开始对一个实例对象进行了序列化,接着对类增加了一个方法,或者更改了某个属性的名称,当再想根据序列化信息来重构以前那个对象的时候,此时两个类的版本信息已经不匹配,不可能再恢复此对象的状态了。要解决这个问题,可能在类中显示定义一个值,如下所示: 



      Java 代码 1.private static final long serialVersionUID = ALongValue;  

 

      private static final long serialVersionUID = ALongValue; 



      这样,序列化机制会使用这个值来作为类的版本标识符,从而可以解决不兼容的问题。但是它却引入了一个新的问题,即使一个类作了实质性的改变,如增加或删除了一些可序列化的属性,在这种机制下仍然会认为这两个类是相等的。 



一种更好的选择 
作为实现Serializable接口的一种替代方案,实现java.io.Externalizable接口同样可以标识一个类为可序列化。 
Externalizable接口中定义了以下两个方法: 

Java 代码 1.public void readExternal(ObjectInput in);  
2.public void writeExternal(ObjectOutput out);  
public void readExternal(ObjectInput in); 
public void writeExternal(ObjectOutput out); 

这两个方法的功能与 readObject()和writeObject()方法相同,任何实现了Externalizable接口的类都需要这实现两个函数来定义其序列化机制。 
使用Externalizable比使用Serializable有着性能上的提高。前者序列化一个对象,所需保存的信息比后者要小,对于后者所需保存的第3个方面的信息,前者不需要访问每一个父类并使其保存相关的状态信息,而只需简单地调用类中实现的writeExternal()方法即可。 

问题总结: 
1、实现Serializable回导致发布的API难以更改,并且使得package-private和private 
这两个本来封装的较好的咚咚也不能得到保障了 
2、Serializable会为每个类生成一个序列号,生成依据是类名、类实现的接口名、 
public和protected方法,所以只要你一不小心改了一个已经publish的API,并且没有自 
己定义一个long类型的叫做serialVersionUID的field,哪怕只是添加一个getXX,就会 
让你读原来的序列化到文件中的东西读不出来(不知道为什么要把方法名算进去?) 
3、不用构造函数用Serializable就可以构造对象,看起来不大合理,这被称为 
extralinguistic mechanism,所以当实现Serializable时应该注意维持构造函数中所维 
持的那些不变状态 
4、增加了发布新版本的类时的测试负担 
5、1.4版本后,JavaBeans的持久化采用基于XML的机制,不再需要Serializable 
6、设计用来被继承的类时,尽量不实现Serializable,用来被继承的interface也不要 
继承Serializable。但是如果父类不实现Serializable接口,子类很难实现它,特别是 
对于父类没有可以访问的不含参数的构造函数的时候。所以,一旦你决定不实现 
Serializable接口并且类被用来继承的时候记得提供一个无参数的构造函数 
7、内部类还是不要实现Serializable好了,除非是static的,(偶也觉得内部类不适合 
用来干这类活的) 
8、使用一个自定义的序列化方法 
9、不管你选择什么序列化形式,声明一个显式的UID: 

private static final long serialVersionUID = randomLongValue; 

10、不需要序列化的东西使用transient注掉它吧,别什么都留着 

11、writeObject/readObject重载以完成更好的序列化 


最近在看web sevice 方面的东西,顺便看了下序列化,懂了不少啊 : 

从MarshalByRefObject派生的类和有[Serializable]的类都可以跨越应用程序域作为参数传递。 
从MarshalByRefObject派生的类按引用封送,有[Serializable]标志的类,按值封送。 
如果此类即从MarshalByRefObject派生,也有[Serializable]标志也是按引用封送。 


MarshalByRefObject和Serializable 
序列化有3种情况: 

1.序列化为XML格式: 
在webservice里,写个web method,传个自定义类做参数,就是这种情况。系统会帮你搞定,把自定义的类转换为默认XML格式。 
2.序列化为2进制: 
要加[Serializable]标志,可以把私有变量和公共变量都序列化。 
3.序列化为soap格式: 
需要实现ISerializable接口,定义序列化函数ISerializable.GetObjectData,和还原序列化的构造函数。 


Java的序列化算法                    序列化算法一般会按步骤做如下事情:                    ◆将对象实例相关的类元数据输出。                    ◆递归地输出类的超类描述直到不再有超类。                    ◆类元数据完了以后,开始从最顶层的超类开始输出对象实例的实际数据值。                    ◆从上至下递归输出实例的数据                    我们用另一个更完整覆盖所有可能出现的情况的例子来说明:                    1.class parent implements Serializable {  2. 3.       int parentVersion = 10;  4. 5.}  6. 7.   8. 9.class contain implements Serializable{  10. 11.       int containVersion = 11;  12. 13.}  14. 15.public class SerialTest extends parent implements Serializable {  16. 17.       int version = 66;  18. 19.       contain con = new contain();  20. 21.   22. 23.       public int getVersion() {  24. 25.              return version;  26. 27.       }  28. 29.       public static void main(String args[]) throws IOException {  30. 31.              FileOutputStream fos = new FileOutputStream("temp.out");  32. 33.              ObjectOutputStream oos = new ObjectOutputStream(fos);  34. 35.              SerialTest st = new SerialTest();  36. 37.              oos.writeObject(st);  38. 39.              oos.flush();  40. 41.              oos.close();  42. 43.       }  44. 45.}                                                           这个例子是相当的直白啦。SerialTest类实现了Parent超类,内部还持有一个Container对象。 

序列化后的格式如下: 

AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65 

73 74 05 52 81 5A AC 66 02 F6 02 00 02 49 00 07 

76 65 72 73 69 6F 6E 4C 00 03 63 6F 6E 74 00 09 

4C 63 6F 6E 74 61 69 6E 3B 78 72 00 06 70 61 72 

65 6E 74 0E DB D2 BD 85 EE 63 7A 02 00 01 49 00 

0D 70 61 72 65 6E 74 56 65 72 73 69 6F 6E 78 70 

00 00 00 0A 00 00 00 42 73 72 00 07 63 6F 6E 74 

61 69 6E FC BB E6 0E FB CB 60 C7 02 00 01 49 00 

0E 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E 78 

70 00 00 00 0B 

我们来仔细看看这些字节都代表了啥。开头部分,见颜色: 

1.AC ED: STREAM_MAGIC. 声明使用了序列化协议. 
2.00 05: STREAM_VERSION. 序列化协议版本. 
3.0x73: TC_OBJECT. 声明这是一个新的对象.  
序列化算法的第一步就是输出对象相关类的描述。例子所示对象为SerialTest类实例, 
因此接下来输出SerialTest类的描述。见颜色: 

1.0x72: TC_CLASSDESC. 声明这里开始一个新Class。 
2.00 0A: Class名字的长度. 
3.53 65 72 69 61 6c 54 65 73 74: SerialTest,Class类名. 
4.05 52 81 5A AC 66 02 F6: SerialVersionUID, 序列化ID,如果没有指定, 
则会由算法随机生成一个8byte的ID. 
5.0x02: 标记号. 该值声明该对象支持序列化。 
6.00 02: 该类所包含的域个数。 
接下来,算法输出其中的一个域,int version=66;见颜色: 

1.0x49: 域类型. 49 代表"I", 也就是Int. 
2.00 07: 域名字的长度. 
3.76 65 72 73 69 6F 6E: version,域名字描述. 
然后,算法输出下一个域,contain con = new contain();这个有点特殊,是个对象。 
描述对象类型引用时需要使用JVM的标准对象签名表示法,见颜色: 

1.0x4C: 域的类型. 
2.00 03: 域名字长度. 
3.63 6F 6E: 域名字描述,con 
4.0x74: TC_STRING. 代表一个new String.用String来引用对象。 
5.00 09: 该String长度. 
6.4C 63 6F 6E 74 61 69 6E 3B: Lcontain;, JVM的标准对象签名表示法. 
7.0x78: TC_ENDBLOCKDATA,对象数据块结束的标志 
.接下来算法就会输出超类也就是Parent类描述了,见颜色: 

1.0x72: TC_CLASSDESC. 声明这个是个新类. 
2.00 06: 类名长度. 
3.70 61 72 65 6E 74: parent,类名描述。 
4.0E DB D2 BD 85 EE 63 7A: SerialVersionUID, 序列化ID. 
5.0x02: 标记号. 该值声明该对象支持序列化. 
6.00 01: 类中域的个数. 
下一步,输出parent类的域描述,int parentVersion=100;同见颜色: 

1.0x49: 域类型. 49 代表"I", 也就是Int. 
2.00 0D: 域名字长度. 
3.70 61 72 65 6E 74 56 65 72 73 69 6F 6E: parentVersion,域名字描述。 
4.0x78: TC_ENDBLOCKDATA,对象块结束的标志。 
5.0x70: TC_NULL, 说明没有其他超类的标志。. 
到此为止,算法已经对所有的类的描述都做了输出。下一步就是把实例对象的实际值输出了。这时候是从parent Class的域开始的,见颜色: 

1.00 00 00 0A: 10, parentVersion域的值. 
还有SerialTest类的域: 

1.00 00 00 42: 66, version域的值. 
再往后的bytes比较有意思,算法需要描述contain类的信息,要记住, 
现在还没有对contain类进行过描述,见颜色: 

1.0x73: TC_OBJECT, 声明这是一个新的对象. 
2.0x72: TC_CLASSDESC声明这里开始一个新Class. 
3.00 07: 类名的长度. 
4.63 6F 6E 74 61 69 6E: contain,类名描述. 
5.FC BB E6 0E FB CB 60 C7: SerialVersionUID, 序列化ID. 
6.0x02: Various flags. 标记号. 该值声明该对象支持序列化 
7.00 01: 类内的域个数。 
.输出contain的唯一的域描述,int containVersion=11; 

1.0x49: 域类型. 49 代表"I", 也就是Int.. 
2.00 0E: 域名字长度. 
3.63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E: containVersion, 域名字描述. 
4.0x78: TC_ENDBLOCKDATA对象块结束的标志. 
这时,序列化算法会检查contain是否有超类,如果有的话会接着输出。 

1.0x70:TC_NULL,没有超类了。 
最后,将contain类实际域值输出。 

1.00 00 00 0B: 11, containVersion的值. 

转载序列化和反序列化

#摘要序列化和反序列化几乎是工程师们每天都要面对的事情,但是要精确掌握这两个概念并不容易:一方面,它们往往作为框架的一部分出现而湮没在框架之中;另一方面,它们会以其他更容易理解的概念出现,例如加密、持... 查看详情

转载线程安全和线程不安全理解

本文章为转载文章,如有侵权,请联系我。转载出处:http://blog.csdn.net/ghevinn/article/details/37764791线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读... 查看详情

个人对autoresetevent和manualresetevent的理解(转载)

...个方法:WaitOne、Set和Reset。这三个方法的官方定义并不好理解,什么终止、非终止,乱七八糟的。在这里,我们以一种通俗易懂的概念来说明。 二、比喻如果把每个线程比作一辆汽车的话,AutoResetEvent和ManualRes 查看详情

序列化:基本理解和文档

】序列化:基本理解和文档【英文标题】:Serialization:Basicunderstandinganddocumentation【发布时间】:2020-08-2210:05:35【问题描述】:在流中,对任何对象的第一次引用会导致被序列化或外部化的对象以及该对象的句柄。对该对象的后续... 查看详情

傅立叶变换的深入理解转载数字信号处理

傅立叶变换的深入理解2007年10月05日星期五16:41专题讨论四:关于傅里叶变换的讨论[精彩]有奖征集:大家讨论一下傅里叶变换相关的内容:1变换的目的,意义,应用。2傅里叶级数与傅里叶变换的差别和联系3连续傅里叶变换,离... 查看详情

反射和可序列化

...类型,然后就可以调用类型的方法或访问字段和属性了。序列化:序列化简单理解成把对象转换为容易传输的格式的过程。  比如,可以序列化一个对象,然后使用HTTP通过Interne 查看详情

(转载)(官方)ue4--图像编程----fshadercache

...ache功能。 控制台命令描述r.UseShaderCaching[0/1]着色器反序列化中早提交,不为请求式。追踪束缚着色器态,使它们在早提 查看详情

datetime.datetime 不是 JSON 可序列化的 [重复]

】datetime.datetime不是JSON可序列化的[重复]【英文标题】:datetime.datetimeisnotJSONserializable[duplicate]【发布时间】:2016-06-2213:15:31【问题描述】:我在Python中有一个类,用于检索表中的所有列并返回包含此数据的JSON。问题是这些列中... 查看详情

我怎样才能制作一个只采用可序列化对象的方法?

】我怎样才能制作一个只采用可序列化对象的方法?【英文标题】:HowcanImakeamethodtotakeonlyserializableobjects?【发布时间】:2019-05-2118:35:40【问题描述】:虽然有一个名为ISerializable的接口,但当一个类型被标记为[Serializable]attribute时... 查看详情

xml——通俗易懂去理解

总能看见XML文件,自己也会用到,但是并没有办法深刻理解“可扩展标记语言”这几个字,网上查阅后依旧谢谢知乎!作者:华子链接:https://www.zhihu.com/question/31353595/answer/60826602来源:知乎著作权归作者所有。商业转载请联系... 查看详情

转载轻松理解js闭包

ClosuresallowJavaScriptprogrammerstowritebettercode.Creative,expressive,andconcise.WefrequentlyuseclosuresinJavaScript,and,nomatteryourJavaScriptexperience,youwillundoubtedlyencounterthemtimeandagain. 查看详情

天蓝色专用 sql 池 (sqldw) 可序列化表提示语句奇怪的行为

】天蓝色专用sql池(sqldw)可序列化表提示语句奇怪的行为【英文标题】:azurededicatedsqlpool(sqldw)serializabletablehintstatementoddbehavior【发布时间】:2021-11-0918:23:24【问题描述】:平台:天蓝色产品:专用sql池(sqldw)如果有人知道的话,我... 查看详情

转载理解矩阵

原文:理解矩阵(三)      理解矩阵(一)        理解矩阵(二)       这两篇文章发表于去年的4月。在第二部分结束的时候,我说:   &n... 查看详情

c#序列化和反序列化《转载》

...用总体说明 .netframework的类库中提供了三个可以用于序列化和反序列化的类,分别为BinaryFormatter、SoapFormatter和XmlSerializer。 BinaryFormatter的命名空间为System.Runtime.Serialization.Formatters.Binary,位于程序集mscorlib.dll中。&n 查看详情

简诉java的特性(转载)

...语言,不止吸收了C++语言的多种优点,还坚持了C++里难以理解的多继承、指针等概念,因此Java语言它带有功能强大和简单好用的两个特征。Java语言作为静态面向对象编程语言的代表,很好地实现了面向对象理论,程序员可以以... 查看详情

javaobject序列化与单例模式[转载]

JavaObject序列化与单例模式[转载]@author Hollis    本文将通过实例+阅读Java源码的方式介绍序列化是如何破坏单例模式的,以及如何避免序列化对单例的破坏。单例模式,是设计模式中最简单的一种。通过单例模式... 查看详情

常量折叠(转载)

...折叠”这里时,突然困惑了,当时学习这点知识时的理解是:可折叠的常量像宏一样,在预编译阶段对常量的引用一律被替换为常量所对应的值,就和普通的宏替换没什么区别,并且,编译器不会为该常量分配空间。现在回... 查看详情

[转载]理解html语义化

声明:本文转载于:freeyiyi1993博客。原文地址:http://www.cnblogs.com/freeyiyi1993/p/3615179.html1、什么是HTML语义化?<基本上都是围绕着几个主要的标签,像标题(H1~H6)、列表(li)、强调(strongem)等等>  根据内容的结构化(内... 查看详情