HashSet中的反序列化的问题

[code="java"]
map = (((HashSet)this) instanceof LinkedHashSet ?
new LinkedHashMap(capacity, loadFactor) :
new HashMap(capacity, loadFactor));
[/code]
这是jdk中HashSet中的readObject中的一段代码

为什么这里的this要先强制转换成HashSet类呢?
昨天已问过该问题,昨天是小弟的误解,该this应该是反序列化时的object类,为何要强制转换呢?
Object类难道不能用instanceof识别是哪个类?或者说在readObject类的内部不能识别呢?

所以小弟做了以下尝试
[code="java"]
package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class CharsetTest {

public static void main(String[] args) throws Exception {
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:" + File.separator + "objectFile.obj"));
    // 序列化对象
    Customer customer = new Customer();
    TestCustomer test = new TestCustomer();
    out.writeObject(customer);
    out.writeObject(test);
    out.close();
    // 反序列化对象
    ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:" + File.separator + "objectFile.obj"));
    Object obj1 = in.readObject();
    Object obj2 = in.readObject();
    System.out.println(obj1 instanceof Customer);
    System.out.println(obj1 instanceof TestCustomer);
    System.out.println(obj2 instanceof Customer);
    System.out.println(obj2 instanceof TestCustomer);
    in.close();
}

}

class Customer implements Serializable {

private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {

}

}

class TestCustomer extends Customer implements Serializable
{}
[/code]

结果很明显的:
true
false
true
true

然后基本确定该强制转换的使用基本唯一的可能性是位于readObject中this不能用instanceof识别
再次尝试代码如下
[code="java"]
package test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class CharsetTest {

public static void main(String[] args) throws Exception {
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:" + File.separator + "objectFile.obj"));
    // 序列化对象
    Customer customer = new Customer();
    TestCustomer test = new TestCustomer();
    out.writeObject(customer);
    out.writeObject(test);
    out.close();
    // 反序列化对象
    ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:" + File.separator + "objectFile.obj"));
    Object obj1 = in.readObject();
    Object obj2 = in.readObject();      
    in.close();
}

}

class Customer implements Serializable {

private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
    System.out.println(this instanceof Customer);
    System.out.println(this instanceof TestCustomer);
}

}

class TestCustomer extends Customer implements Serializable
{}
[/code]

结果依然是
true
false
true
true

这下小弟糊涂了,总觉得java源码中所写的都是有必要的,既然用了强制转换,必然有其道理
难道是性能问题?
然后又做了如下尝试
[code="java"]
package test;

public class Test {

public static void main(String[] args) {
    Test test = new Test();
    long beforeTime,nextTime,thisTime,totalTime1,totalTime2;
    totalTime1 = 0;
    totalTime2 = 0;
    int count = 1<<30;
    //测试20次取平均值,两种方法间隔开以消除系统资源导致的时间差
    for(int i = 1;i<=20;i++)
    {
        //不强制转换
        beforeTime = System.currentTimeMillis();
        //执行1<<30次
        for(int m = 1;m<=count;m++)
            test.test();            
        nextTime = System.currentTimeMillis();
        System.out.println(thisTime = nextTime-beforeTime);
        totalTime1 += thisTime;

        //强制转换
        beforeTime = System.currentTimeMillis();
        //执行1<<30次
        for(int m = 1;m<=count;m++)
            test.test2();
        nextTime = System.currentTimeMillis();
        System.out.println(thisTime = nextTime-beforeTime);
        totalTime2 += thisTime;
    }
    System.out.println(totalTime1);
    System.out.println(totalTime1/20);
    System.out.println(totalTime2);
    System.out.println(totalTime2/20);
}

public void test()
{
    if(this instanceof Test)
    {}
}
public void test2()
{
    if((Test)this instanceof Test)
    {}
}

}

[/code]
测试结果如下
3922
3328
3922
3203
3938
3219
4234
4281
3969
3422
4187
3188
4047
3328
4031
3438
4266
3297
4000
3453
4203
3422
4203
3422
4453
3297
4109
3735
4312
3266
3984
3266
3969
3218
3938
3203
3937
3219
3953
3188
81577
4078
67393
3369
看到这结果,原来如此,心想jdk的编写者真是厉害,这点时间都计算得那么精准
但记起上次测试(代码已不见了),没有连续着的,似乎时间是一样的,于是有点怀疑地做了以下测试
[code="java"]
package test;

public class Test {

public static void main(String[] args) {
    Test test = new Test();
    long beforeTime,nextTime,thisTime,totalTime1,totalTime2;
    totalTime1 = 0;
    totalTime2 = 0;
    int count = 1<<30;
    //测试20次取平均值,两种方法间隔开以消除系统资源导致的时间差
    for(int i = 1;i<=20;i++)
    {
        //不强制转换
        beforeTime = System.currentTimeMillis();
        //执行1<<30次
        for(int m = 1;m<=count;m++)
            test.test();            
        nextTime = System.currentTimeMillis();
        System.out.println(thisTime = nextTime-beforeTime);
        totalTime1 += thisTime;

        //强制转换
        beforeTime = System.currentTimeMillis();
        //执行1<<30次
        for(int m = 1;m<=count;m++)
            test.test2();
        nextTime = System.currentTimeMillis();
        System.out.println(thisTime = nextTime-beforeTime);
        totalTime2 += thisTime;
    }
    System.out.println(totalTime1);
    System.out.println(totalTime1/20);
    System.out.println(totalTime2);
    System.out.println(totalTime2/20);
}

public void test2()
{
    if(this instanceof Test)
    {}
}
public void test()
{
    if((Test)this instanceof Test)
    {}
}

}

[/code]
其实也就是把上一段代码Test中的两个方法换个位置,结果却很崩溃~
3890
3219
3844
3125
3844
3156
3781
3125
3844
3109
3813
3094
3781
3125
3828
3125
3844
3125
3875
3140
3828
3141
3828
3125
3860
3140
3844
3125
3797
3140
3829
3140
3797
3078
3797
3156
3828
3125
3829
3140
76581
3829
62653
3132
结果基本完全一样~意外地引发出另一个奇怪的问题来了
但之前的问题尚未解决,只能暂且放下了
于是改了下方法测
[code="java"]
package test;

public class Test2 {

public static void main(String[] args) {
    Test2 test = new Test2();
    long beforeTime,nextTime,thisTime,totalTime1,totalTime2;
    totalTime1 = 0;
    totalTime2 = 0;
    int count = 1<<30;
    //测试20次取平均值
    //不强制转换
    for(int i = 1;i<=20;i++)
    {           
        beforeTime = System.currentTimeMillis();
        //执行1<<30次
        for(int m = 1;m<=count;m++)
            test.test();            
        nextTime = System.currentTimeMillis();
        System.out.println(thisTime = nextTime-beforeTime);
        totalTime1 += thisTime;
    }

    //测试20次取平均值
    //强制转换
    for(int i = 1;i<=20;i++)
    {       
        beforeTime = System.currentTimeMillis();
        //执行1<<30次
        for(int m = 1;m<=count;m++)
            test.test2();
        nextTime = System.currentTimeMillis();
        System.out.println(thisTime = nextTime-beforeTime);
        totalTime2 += thisTime;
    }
    System.out.println(totalTime1);
    System.out.println(totalTime1/20);
    System.out.println(totalTime2);
    System.out.println(totalTime2/20);
}

public void test2()
{
    if(this instanceof Test2)
    {}
}
public void test()
{
    if((Test2)this instanceof Test2)
    {}
}

}
[/code]
结果:
3797
3797
3781
3922
3797
3813
3812
3797
3812
3797
3797
3813
3812
3797
3813
3796
3797
3813
3781
3844
3109
3110
3109
3094
3109
3141
3093
3110
3094
3125
3125
3093
3110
3109
3094
3109
3125
3110
3125
3125
76188
3809
62219
3110
继续把两个方法互换(这里不贴代码了),再试
3796
3813
3781
3781
3797
3844
3813
3796
3797
3813
3781
3797
3812
3797
3828
3782
3797
3796
3797
3782
3109
3094
3109
3109
3110
3109
3094
3109
3110
3093
3125
3094
3110
3109
3094
3109
3109
3079
3109
3109
76000
3800
62093
3104
忽然有了种想死的感觉
继续
[code="java"]
package test;

public class Test2 {

public static void main(String[] args) {
    Test2 test = new Test2();
    long beforeTime,nextTime,thisTime,totalTime1,totalTime2;
    totalTime1 = 0;
    totalTime2 = 0;
    int count = 1<<30;
    //测试20次取平均值
    //不强制转换
    for(int i = 1;i<=20;i++)
    {           
        beforeTime = System.currentTimeMillis();
        //执行1<<30次
        for(int m = 1;m<=count;m++)
            test.test();            
        nextTime = System.currentTimeMillis();
        System.out.println(thisTime = nextTime-beforeTime);
        totalTime1 += thisTime;
    }

    /*//测试20次取平均值
    //强制转换
    for(int i = 1;i<=20;i++)
    {       
        beforeTime = System.currentTimeMillis();
        //执行1<<30次
        for(int m = 1;m<=count;m++)
            test.test2();
        nextTime = System.currentTimeMillis();
        System.out.println(thisTime = nextTime-beforeTime);
        totalTime2 += thisTime;
    }*/
    System.out.println(totalTime1);
    System.out.println(totalTime1/20);
    /*System.out.println(totalTime2);
    System.out.println(totalTime2/20);*/
}

public void test()
{
    if(this instanceof Test2)
    {}
}
public void test2()
{
    if((Test2)this instanceof Test2)
    {}
}

}

[/code]
结果
3797
3875
3813
4156
3797
3797
3812
3828
3829
3796
3813
3828
3812
3797
3813
3812
3828
3797
3828
3813
76641
3832
again:
[code="java"]
package test;

public class Test2 {

public static void main(String[] args) {
    Test2 test = new Test2();
    long beforeTime,nextTime,thisTime,totalTime1,totalTime2;
    totalTime1 = 0;
    totalTime2 = 0;
    int count = 1<<30;
    //测试20次取平均值
    //不强制转换
    for(int i = 1;i<=20;i++)
    {           
        beforeTime = System.currentTimeMillis();
        //执行1<<30次
        for(int m = 1;m<=count;m++)
            test.test2();           
        nextTime = System.currentTimeMillis();
        System.out.println(thisTime = nextTime-beforeTime);
        totalTime1 += thisTime;
    }

    /*//测试20次取平均值
    //强制转换
    for(int i = 1;i<=20;i++)
    {       
        beforeTime = System.currentTimeMillis();
        //执行1<<30次
        for(int m = 1;m<=count;m++)
            test.test2();
        nextTime = System.currentTimeMillis();
        System.out.println(thisTime = nextTime-beforeTime);
        totalTime2 += thisTime;
    }*/
    System.out.println(totalTime1);
    System.out.println(totalTime1/20);
    /*System.out.println(totalTime2);
    System.out.println(totalTime2/20);*/
}

public void test()
{
    if(this instanceof Test2)
    {}
}
public void test2()
{
    if((Test2)this instanceof Test2)
    {}
}

}
[/code]
结果
3812
3891
3828
3813
3828
3843
3813
3812
3797
3828
3813
3797
3828
3844
3812
3828
3813
3828
3812
3797
76437
3821
终于得到了理想中的结果~
然而强制转换的问题仍然没有答案,两个的时间几乎完全一样
到底强制转换为何用?
然后又引发了另一个问题:
为何会在前两次测试中会出现时间差?而且间隔开或者连在一起都会出现这时间差,这又是什么原因?
小弟学java还没多久,请教高手了

刚才又测试了一下,把test函数改成:

[code="java"] public void test() {
int j;
for (int i = 0; i < 5; i++)
j = 9;
}[/code]

三个测试的时间几乎一样了。

结论:

1 强制转换和不强制转换时间差不多,没有明显差别
2 如果是普通语句,放在第一个位置和不放在第一个位置执行时间差不多
3 如果是你提供的test函数,放在第一个位置执行的时间明显要长,个人觉得可能和instance 操作符有关,因为看不到源码,暂时也不好找原因了,总之是比较奇怪的。

[quote]强制转换的问题仍然没有答案[/quote]

经过仔细思考,强制转换还是有必要的:

[code="java"] map = (((HashSet)this) instanceof LinkedHashSet ?
new LinkedHashMap(capacity, loadFactor) :
new HashMap(capacity, loadFactor));[/code]

考虑上面的语句,如果产生的反序列化对象不属于HashSet,或者就是其他一个非常普通的对象,但是不强制转换,此时执行:

[quote]this instanceof LinkedHashSet[/quote]
就会返回false, 那么系统仍然会执行后面的语句,生成一个HashMap:

new HashMap(capacity, loadFactor);

这是一个错误的流程。

但是加上强制转换后,在执行强制转换
(HashSet)this 时,由于类型不匹配,系统会抛一个ClassCastException异常,表示“程序员把一个错误的对象序列化成HashSet”,这种错误信息是有必要的,程序员可以根据这些信息来纠正错误。

[quote]这种情况需要什么条件才能发生呢?
1.对象需要序列化
2.序列化代码有bug,程序员没发现
3.有误的代码错误地执行成功,并且成功地序列化了[/quote]

1, 2, 3都是可能的, 2的可能性最大。

[quote]另外,另一个问题呢?为什么会有如此大的时间差?谢谢您的指点 [/quote]

能简单介绍一下你的代码,什么和什么时间差大, 不好意思,你的代码太长了。

[quote]会发现强制转换或者不强制转换,结果不一致,执行1<<30次的结果一种约为4000,一种约为3200
而且这不是关键,重要的是,我把前后顺序调换了之后,结果仍然不变
也就是说造成时间不同的不是因为强制转换与否,而是因为顺序,前者会慢一点,后者会快一点
小弟第一轮测试是两种轮流着来,也就是执行一次1<<30次的强制转换,然后一次非强制,结果一样
后面是连续20次强制,再连续20次非强制,结果仍然一样
这样说不知道说清楚了没? [/quote]

恩,了解了,而且试了试,的确如你所说,事实上,时间执行长短和强制与否无关,第一个执行的时间最长(我加了第三个测试,时间和第二个一样),我也挺奇怪,到底是什么原因使得第一次调用花的时间比的后面的调用少这么多,费解啊!

哦,说错了,是第一次调用花的时间比的后面的调用时间多很多。

[quote]java真的很神秘啊~[/quote]

呵呵,的确。