国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
談?wù)凧2SE中的序列化

作者:Favo yang
favoyang@yahoo.com

一個(gè)感性的認(rèn)識(shí)

java中處處體現(xiàn)著簡(jiǎn)單的程序設(shè)計(jì)風(fēng)格,序列化作為最常用的功能之一,在java中的設(shè)計(jì)尤為“簡(jiǎn)單”。在ObjectInputStream 和ObjectOutputStream的幫助下,我們可以輕松的實(shí)現(xiàn)序列化。

只要我們的class 實(shí)現(xiàn)了java.io.Serializable接口,就可以利用ObjectOutputStream的writeObject()方法將一個(gè)對(duì)象序列化;利用ObjectInputStream的readObject()方法,可以返回讀出的object對(duì)象。Serializable接口不需要我們實(shí)現(xiàn)任何方法。

以下是一個(gè)例子,它能給我們一個(gè)感性的認(rèn)識(shí):

Serial實(shí)現(xiàn)了就java.io.Serializable接口,是需要序列化的類。我們首先構(gòu)造一個(gè)Serial的對(duì)象serial1然后將其保存(序列化)在一個(gè)文件中,而后再將其讀出(反序列化),并打印其內(nèi)容。

package Stream;

/**
 * @author favo yang
 */

import java.io.*;

public class Serial implements Serializable {

    int company_id;

    String company_addr;

    boolean company_flag;

    public Serial(){}//不同于c++,沒有也可以

    public Serial(int company_id,String company_addr,boolean company_flag) {

        this.company_id=company_id;

        this.company_addr=company_addr;

        this.company_flag=company_flag;

    }

    public static void main(String[] args) {

        Serial serial1 = new Serial(752,"dayer street #5 building 02-287",false);
//構(gòu)造一個(gè)新的對(duì)象

        FileInputStream in=null;

        FileOutputStream out=null;

        ObjectInputStream oin=null;

        ObjectOutputStream oout=null;

        try {

            out = new FileOutputStream("5.txt");

            oout = new ObjectOutputStream(out);

            serial1.serialize(oout);//序列化

            oout.close();

            oout=null;

            in = new FileInputStream("5.txt");

            oin = new ObjectInputStream(in);

            Serial serial2 = Serial.deserialize(oin);//反序列化

            System.out.println(serial2);//打印結(jié)果

        } catch (Exception ex){

            ex.printStackTrace();

        } finally{

            try {

                if (in != null) {

                    in.close();

                }

                if (oin != null) {

                    oin.close();

                }

                if (out != null) {

                    out.close();

                }

                if (oout != null) {

                    oout.close();

                }

            } catch (IOException ex1) {

                ex1.printStackTrace();

            }

        }

    }

    /**
     * deserialize
     */

    public static Serial deserialize(ObjectInputStream oin) throws Exception{

        Serial s=(Serial)oin.readObject();

        return s;

    }

    public String toString() {

        return "DATA: "+company_id+" "+company_addr+" "+company_flag;

    }

    /**

     * serialize

     */

    public void serialize(ObjectOutputStream oout) throws Exception{

        oout.writeObject(this);

    }

}

運(yùn)行結(jié)果:

DATA: 752 dayer street #5 building 02-287 false
正確打印了結(jié)果。

現(xiàn)在序列化的功能還沒有被j2me支持,所以我們?cè)趈2me中是使用DataInputStream、DataoutputStream配合rms或者網(wǎng)絡(luò)實(shí)現(xiàn)序列化。詳細(xì)的介紹請(qǐng)參見:本站的另外兩篇文章:《Java的基本數(shù)據(jù)類型與流》、
《J2ME聯(lián)網(wǎng)中采用序列化機(jī)制》
 

了解序列化帶來的風(fēng)險(xiǎn)與問題 

    我們已經(jīng)看到了序列化的優(yōu)點(diǎn),就是簡(jiǎn)單的序列化的實(shí)現(xiàn)成本很低,或者說沒有什么實(shí)現(xiàn)成本,只要實(shí)現(xiàn)一個(gè)接口就行了。但如果你將上文中的那個(gè)例子簡(jiǎn)單的套用在你的程序中,然后發(fā)表的話,你可有苦頭了。你將會(huì)很快發(fā)現(xiàn)序列化帶來問題:

1.實(shí)現(xiàn)了Serializable接口的類的改動(dòng)變得很難(兼容性問題)。
    如果你想保持向下兼容性的話,這意味著要求新版本反序列化老版本序列化的數(shù)據(jù)流;如果你想保持向上兼容性的話,這意味著要求老版本反序列化新版本的數(shù)據(jù)流。尤其是向下兼容性的問題帶來了讓我們的代碼實(shí)現(xiàn)很難改動(dòng)的問題,為了能在新版本中反序列化老版本的數(shù)據(jù)流,類的實(shí)現(xiàn)必須要保留老版本的實(shí)現(xiàn)過程。這無疑是我們給自己加帶上的一副枷鎖。

2.實(shí)現(xiàn)了Serializable接口的類的測(cè)試成本大大增加了
    可以想象,當(dāng)新版本發(fā)布時(shí),為了保持兼容性,你的測(cè)試量將是版本數(shù)的平方!

3.草率的接受默認(rèn)的序列化方式可能會(huì)帶來性能問題,甚至更糟。
    序列化作為額外的一種“構(gòu)造函數(shù)”可能破壞數(shù)據(jù)的完整性、約束性,甚至,一個(gè)精心偽造的數(shù)據(jù)流所序列化出的對(duì)象可能帶來安全隱患。

了解了這些可能的風(fēng)險(xiǎn)后,我們?cè)谛蛄谢臅r(shí)候遇到了難題,如何避免這些風(fēng)險(xiǎn)呢?我將這些風(fēng)險(xiǎn)概括為以下幾個(gè)方面:

1.    當(dāng)序列化遇到繼承時(shí)引發(fā)的問題?
2.    何時(shí)接受默認(rèn)的java序列化?
3.    兼容性問題
4.    數(shù)據(jù)一致性問題與數(shù)據(jù)約束問題
5.    安全性問題

如何應(yīng)對(duì)這些風(fēng)險(xiǎn)呢?請(qǐng)關(guān)注接下來的內(nèi)容。

當(dāng)序列化遇到繼承…
當(dāng)一個(gè)父類實(shí)現(xiàn)Serializable接口后,他的子類都將自動(dòng)的實(shí)現(xiàn)序列化。

以下驗(yàn)證了這一點(diǎn):

package Serial;
import java.io.Serializable;
public class SuperC implements Serializable {//父類實(shí)現(xiàn)了序列化 
    int supervalue; 
    public SuperC(int supervalue) { 
       this.supervalue = supervalue; 
    } 
    public String toString() { 
       return "supervalue: "+supervalue; 
    }
}

public class SubC extends SuperC {//子類 
    int subvalue; 

    public SubC(int supervalue,int subvalue) { 
       super(supervalue); 
       this.subvalue=subvalue; 
    } 

    public String toString() { 
       return super.toString()+" sub: "+subvalue; 
    }
}

public class Test1 {

    public static void main(String [] args){ 
       SubC subc=new SubC(100,200); 
       FileInputStream in=null; 
        FileOutputStream out=null; 
        ObjectInputStream oin=null; 
        ObjectOutputStream oout=null; 
        try { 
            out = new FileOutputStream("Test1.txt");//子類序列化 
            oout = new ObjectOutputStream(out); 
            oout.writeObject(subc); 
            oout.close(); 
            oout=null;

            in = new FileInputStream("Test1.txt"); 
            oin = new ObjectInputStream(in); 
            SubC subc2=(SubC)oin.readObject();//子類反序列化 
            System.out.println(subc2); 
        } catch (Exception ex){ 
            ex.printStackTrace(); 
        } finally{ 
            …此處省略 
        } 
    }
}

運(yùn)行結(jié)果如下:

supervalue: 100 sub: 200

可見子類成功的序列化/反序列化了。

怎管讓子類實(shí)現(xiàn)序列化看起來是一件很簡(jiǎn)單的事情,但有的時(shí)候,往往我們不能夠讓父類實(shí)現(xiàn)Serializable接口,原因是有時(shí)候父類是抽象的(這并沒有關(guān)系),并且父類不能夠強(qiáng)制每個(gè)子類都擁有序列化的能力。換句話說父類設(shè)計(jì)的目的僅僅是為了被繼承。

要為一個(gè)沒有實(shí)現(xiàn)Serializable接口的父類,編寫一個(gè)能夠序列化的子類是一件很麻煩的事情。java docs中提到:

“To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype‘s public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class‘s state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime. ”

也就是說,要為一個(gè)沒有實(shí)現(xiàn)Serializable接口的父類,編寫一個(gè)能夠序列化的子類要做兩件事情:

其一,父類要有一個(gè)無參的constructor;

其二,子類要負(fù)責(zé)序列化(反序列化)父類的域。

我們將SuperC的Serializable接口去掉,而給SubC加上Serializable接口。運(yùn)行后產(chǎn)生錯(cuò)誤:

java.lang.Error: Unresolved compilation problem: 
    Serializable cannot be resolved or is not a valid superinterface 
    at Serial.SubC.<init>(SubC.java:15) 
    at Serial.Test1.main(Test1.java:19)
Exception in thread "main"

    果真如docs中所說的一樣,父類缺少無參構(gòu)造函數(shù)是不行的。

    接下來,按照docs中的建議我們改寫這個(gè)例子:
public abstract class SuperC  { 
    int supervalue; 
    public SuperC(int supervalue) { 
       this.supervalue = supervalue; 
    }
    public SuperC(){}//增加一個(gè)無參的constructor 
    public String toString() { 
       return "supervalue: "+supervalue; 
    }
}

public class SubC extends SuperC implements Serializable { 
    int subvalue;

    public SubC(int supervalue,int subvalue) { 
       super(supervalue); 
       this.subvalue=subvalue; 
    } 
    
    public String toString() { 
       return super.toString()+" sub: "+subvalue; 
    } 
    
    private void writeObject(java.io.ObjectOutputStream out) 
    throws IOException{ 
       out.defaultWriteObject();//先序列化對(duì)象 
       out.writeInt(supervalue);//再序列化父類的域 
    } 
    private void readObject(java.io.ObjectInputStream in) 
    throws IOException, ClassNotFoundException{ 
       in.defaultReadObject();//先反序列化對(duì)象 
       supervalue=in.readInt();//再反序列化父類的域 
    }
}

運(yùn)行結(jié)果證明了這種方法是正確的。在此處我們用到了writeObject/ readObject方法,這對(duì)方法如果存在的話,序列化時(shí)就會(huì)被調(diào)用,以代替默認(rèn)的行為(以后還要探討,先了解這么多)。我們?cè)谛蛄谢瘯r(shí),首先調(diào)用了ObjectOutputStream的defaultWriteObject,它使用默認(rèn)的序列化行為,然后序列化父類的域;反序列化的時(shí)候也一樣。

歸納一下:

目的 行為
為一個(gè)實(shí)現(xiàn)Serializable接口的父類,編寫一個(gè)能夠序列化的子類 子類將自動(dòng)的實(shí)現(xiàn)序列化
為一個(gè)沒有實(shí)現(xiàn)Serializable接口的父類,編寫一個(gè)能夠序列化的子類 1, 父類要有一個(gè)無參的constructor;2, 子類要先序列化自身,然后子類要負(fù)責(zé)序列化父類的域

何時(shí)接受默認(rèn)的java序列化行為

首先要了解java默認(rèn)的序列化行為,java將一切關(guān)于對(duì)象的信息都保存了下了,也就是說,有些時(shí)候那些不需要保存的也被保存了下來。一般情況下,我們僅僅需要保存邏輯數(shù)據(jù)就可以了。不需要保存的數(shù)據(jù)我們可以用關(guān)鍵字transient標(biāo)出。

以下是一個(gè)例子:

import java.io.*;

public class Serial implements Serializable {

    int company_id;

    String company_addr;

    transient boolean company_flag;

}

    則company_flag字段將不會(huì)參與序列化與反序列化,但同時(shí)你也增加了為他初始值的責(zé)任。這也是序列化常常導(dǎo)致的問題之一。因?yàn)樾蛄谢喈?dāng)于一個(gè)只接受數(shù)據(jù)流的public構(gòu)造函數(shù),這種對(duì)象構(gòu)造方法是語(yǔ)言之外的。但他仍然是一種形式上的構(gòu)造函數(shù)。如若你的類不能夠通過其他方面來保證初始化,則你需要額外的提供readObject方法,首先正常的反序列化,然后對(duì)transient標(biāo)示的字段進(jìn)行初始化。

    在不適合的時(shí)候,使用java默認(rèn)的序列化行為可能會(huì)帶來速度上的影響,最糟糕的情況是,可能導(dǎo)致溢出。在某些數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)中,經(jīng)常會(huì)充斥著各種的循環(huán)引用,而java的默認(rèn)序列化行為,并不了解你的對(duì)象結(jié)構(gòu),其結(jié)果就是java試圖通過一種昂貴的“圖遍歷”來保存對(duì)象狀態(tài)??上攵?,不但慢而且可能溢出。這時(shí)候你就要提供自己的readObject,來代替默認(rèn)的行為。

兼容性問題
兼容性歷來是復(fù)雜而麻煩的問題。

不要兼容性:

    首先來看看如果我們的目的是不要兼容性,應(yīng)該注意哪些。不要兼容性的場(chǎng)合很多,比如war3每當(dāng)版本升級(jí)就不能夠讀取以前的replays。

    兼容也就是版本控制,java通過一個(gè)名為UID(stream unique identifier)來控制,這個(gè)UID是隱式的,它通過類名,方法名等諸多因素經(jīng)過計(jì)算而得,理論上是一一映射的關(guān)系,也就是唯一的。如果UID不一樣的話,就無法實(shí)現(xiàn)反序列化了,并且將會(huì)得到InvalidClassException。

    當(dāng)我們要人為的產(chǎn)生一個(gè)新的版本(實(shí)現(xiàn)并沒有改動(dòng)),而拋棄以前的版本的話,可以通過顯式的聲名UID來實(shí)現(xiàn):

    private static final long serialVersionUID=????;

你可以編造一個(gè)版本號(hào),但注意不要重復(fù)。這樣在反序列化的時(shí)候老版本將得到InvalidClassException,我們可以在老版本的地方捕捉這個(gè)異常,并提示用戶升級(jí)的新的版本。

當(dāng)改動(dòng)不大時(shí),保持兼容性(向下兼容性的一個(gè)特例):

    有時(shí)候你的類增加了一些無關(guān)緊要的非私有方法,而邏輯字段并不改變的時(shí)候,你當(dāng)然希望老版本和新版本保持兼容性,方法同樣是通過顯式的聲名UID來實(shí)現(xiàn)。下面我們驗(yàn)證一下。

    老版本:

import java.io.*;

public class Serial implements Serializable {

    int company_id;

    String company_addr;

       public Serial1(int company_id, String company_addr) { 
           this.company_id = company_id; 
           this.company_addr = company_addr; 
    }

public String toString() {

        return "DATA: "+company_id+" "+

company_addr;

    }

}

    新版本

import java.io.*;

public class Serial implements Serializable {

    int company_id;

    String company_addr;

       public Serial1(int company_id, String company_addr) { 
           this.company_id = company_id; 
           this.company_addr = company_addr; 
    }

public String toString() {

        return "DATA: "+company_id+" "+ company_addr;

    }

    public void todo(){}//無關(guān)緊要的方法

}

首先將老版本序列化,然后用新版本讀出,發(fā)生錯(cuò)誤:

java.io.InvalidClassException: Serial.Serial1; local class incompatible: stream classdesc serialVersionUID = 762508508425139227, local class serialVersionUID = 1187169935661445676

接下來我們加入顯式的聲名UID:

private static final long serialVersionUID=762508508425139227l;

再次運(yùn)行,順利地產(chǎn)生新對(duì)象

DATA: 1001 com1

如何保持向上兼容性:

    向上兼容性是指老的版本能夠讀取新的版本序列化的數(shù)據(jù)流。常常出現(xiàn)在我們的服務(wù)器的數(shù)據(jù)更新了,仍然希望老的客戶端能夠支持反序列化新的數(shù)據(jù)流,直到其更新到新的版本。可以說,這是半自動(dòng)的事情。

    跟一般的講,因?yàn)樵趈ava中serialVersionUID是唯一控制著能否反序列化成功的標(biāo)志,只要這個(gè)值不一樣,就無法反序列化成功。但只要這個(gè)值相同,無論如何都將反序列化,在這個(gè)過程中,對(duì)于向上兼容性,新數(shù)據(jù)流中的多余的內(nèi)容將會(huì)被忽略;對(duì)于向下兼容性而言,舊的數(shù)據(jù)流中所包含的所有內(nèi)容都將會(huì)被恢復(fù),新版本的類中沒有涉及到的部分將保持默認(rèn)值。利用這一特性,可以說,只要我們認(rèn)為的保持serialVersionUID不變,向上兼容性是自動(dòng)實(shí)現(xiàn)的。

    當(dāng)然,一但我們將新版本中的老的內(nèi)容拿掉,情況就不同了,即使UID保持不變,會(huì)引發(fā)異常。正是因?yàn)檫@一點(diǎn),我們要牢記一個(gè)類一旦實(shí)現(xiàn)了序列化又要保持向上下兼容性,就不可以隨隨便便的修改了?。?!

    測(cè)試也證明了這一點(diǎn),有興趣的讀者可以自己試一試。

如何保持向下兼容性:

       一如上文所指出的,你會(huì)想當(dāng)然的認(rèn)為只要保持serialVersionUID不變,向下兼容性是自動(dòng)實(shí)現(xiàn)的。但實(shí)際上,向下兼容要復(fù)雜一些。這是因?yàn)?,我們必須要?duì)那些沒有初始化的字段負(fù)責(zé)。要保證它們能被使用。

    所以必須要利用 
    private void readObject(java.io.ObjectInputStream in) 
    throws IOException, ClassNotFoundException{ 
       in.defaultReadObject();//先反序列化對(duì)象 
       if(ver=5552){//以前的版本5552 
           …初始化其他字段 
        }else if(ver=5550){//以前的版本5550 
          …初始化其他字段 
        }else{//太老的版本不支持 
        throw new InvalidClassException(); 
    }

    細(xì)心的讀者會(huì)注意到要保證in.defaultReadObject();能夠順利執(zhí)行,就必須要求serialVersionUID保持一致,所以這里的ver不能夠利用serialVersionUID了。這里的ver是一個(gè)我們預(yù)先安插好的final long ver=xxxx;并且它不能夠被transient修飾。所以保持向下的兼容性至少有三點(diǎn)要求:

         1.serialVersionUID保持一致
         2.預(yù)先安插好我們自己的版本識(shí)別標(biāo)志的final long ver=xxxx; 
         3.保證初始化所有的域

討論一下兼容性策略:

       到這里我們可以看到要保持向下的兼容性很麻煩。而且隨著版本數(shù)目的增加。維護(hù)會(huì)變得困難而繁瑣。討論什么樣的程序應(yīng)該使用怎么樣的兼容性序列化策略已經(jīng)超出本文的范疇,但是對(duì)于一個(gè)游戲的存盤功能,和對(duì)于一個(gè)字處理軟件的文檔的兼容性的要求肯定不同。對(duì)于rpg游戲的存盤功能,一般要求能夠保持向下兼容,這里如果使用java序列化的方法,則可根據(jù)以上分析的三點(diǎn)進(jìn)行準(zhǔn)備。對(duì)于這樣的情況使用對(duì)象序列化方法還是可以應(yīng)付的。對(duì)于一個(gè)字處理軟件的文檔的兼容性要求頗高,一般情況下的策略都是要求良好的向下兼容性,和盡可能的向上兼容性。則一般不會(huì)使用對(duì)象序列化技術(shù),一個(gè)精心設(shè)計(jì)的文檔結(jié)構(gòu),更能解決問題。

數(shù)據(jù)一致性問題、約束問題 
    要知道序列化是另一種形式上的“public構(gòu)造函數(shù)”,但他僅僅構(gòu)造起對(duì)象,而不作任何的檢查,這樣人很不舒服,所以必要的檢查是必須的,這利用了readObject()

private void readObject(java.io.ObjectInputStream in) 
    throws IOException, ClassNotFoundException{ 
       in.defaultReadObject();//先反序列化對(duì)象
…進(jìn)行檢查與初始化 
    }

出于結(jié)構(gòu)化的考慮,通常使用一個(gè)名為initialize的函數(shù),負(fù)責(zé)檢查與初始化,如果失敗拋出異常。要保持檢查與初始化是很容易被忘記的,這常常導(dǎo)致問題。另一個(gè)問題在于當(dāng)父類沒有加入readObject()的時(shí)候,子類很容易忘記要調(diào)用對(duì)應(yīng)的initialize函數(shù)。這仿佛回到了當(dāng)初為什么要引入構(gòu)造函數(shù)的問題,原因就是防止子類忘記調(diào)用初始化函數(shù)引發(fā)各種問題。所以,如果要保持?jǐn)?shù)據(jù)一致性,一定要加入readObject()。

安全問題
安全性的話題超出了本文的范疇,但是你應(yīng)該要知道,有可能一個(gè)攻擊者會(huì)對(duì)你的類準(zhǔn)備一個(gè)惡意的數(shù)據(jù)流企圖生成一個(gè)錯(cuò)誤的類。當(dāng)你需要確保你的對(duì)象數(shù)據(jù)安全的話,你一般可以利用上面的方法來檢查,并初始化,但對(duì)于某些引用不好檢查。解決方法就是對(duì)重要的部件進(jìn)行保護(hù)性拷貝。這里推薦一個(gè)好方法,它不用保護(hù)性拷貝個(gè)別的域,而是直接保護(hù)性拷貝整個(gè)對(duì)象。這就是:
    Object readResolve() throws ObjectStreamException;
    這個(gè)方法的用途就是,他會(huì)緊接著readObject()調(diào)用。它將會(huì)利用返回的對(duì)象代替原來反序列化的對(duì)象。也就是原來readObject()反序列化的對(duì)象將會(huì)被立即的丟棄。

Object readResolve() throws ObjectStreamException{

    return new Serial2(this.xxx1,this.xxx2);// xxx1、xxx2是剛剛反序列化得來的,這是一種保護(hù)性拷貝

}       
這樣的話雖然在時(shí)間上有所浪費(fèi),但是對(duì)于特別的重要而安全的類,可以使用這種方法。如果數(shù)據(jù)一致性問題、約束問題通過逐一檢查來解決很麻煩,也可以利用這種方法,但要考慮好成本,和注意下面的局限性。        利用readResolve()有一個(gè)明顯的缺點(diǎn),就是當(dāng)父類實(shí)現(xiàn)了readResolve(),子類將變得無叢下手。如果一個(gè)保護(hù)的或者是公有的父類的readResolve()存在,并且子類也沒有改寫它,將會(huì)使得子類反序列化的時(shí)候最終得到一個(gè)父類的對(duì)象,這既不是我們要得結(jié)果,也不容易發(fā)現(xiàn)這種錯(cuò)誤。而讓子類重寫readResolve()無疑是一個(gè)負(fù)擔(dān)。也就是說對(duì)于要繼承的類而言,實(shí)現(xiàn)readResolve()來保護(hù)類不是一個(gè)好方法。我們只能利用第一種方法寫一個(gè)保護(hù)性的readObject()。

    所以我的建議是:一般情況下,只有對(duì)于final的類采用readResolve()來進(jìn)行保護(hù)。

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Serializable兼容性問題及serialVersionUID的使用
java對(duì)象序列化學(xué)習(xí)筆記_Love Sun,Love Life
Java Serializable
Java對(duì)象的序列化和反序列化實(shí)踐
Serializable接口
java序列化(Serializable)的作用和反序列化
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服