【原创】Java和C#下String类型中的==和equals的原理与区别

一、Java下

1、几个例子

public static void main(String[] arge) {

        String str1 = new String("1234");
        String str2 = new String("1234");
        System.out.println("①new String()方式下==:" + (str1 == str2));
        System.out.println("②new String()方式下equals:" + str1.equals(str2));

        String str3 = "1234";
        String str4 = "1234";
        System.out.println("③赋值常量方式下==:" + (str3 == str4));
        System.out.println("④赋值常量方式下equals:" + str3.equals(str4));
//3String val="1234";String str5 = val;String str6 = val;System.out.println("⑤赋值变量方式下==:" + (str5 == str6));System.out.println("⑥赋值变量方式下equals:" + str5.equals(str6)); 
 }

    运行输出:    

①new String()方式下==:false
②new String()方式下equals:true
③赋值常量方式下==:true
④赋值常量方式下equals:true
⑤赋值变量方式下==:true
⑥赋值变量方式下equals:true

2、代码分析

  (0)首先看下Java中String类里的equals是何如实现的  

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

     (1)==是关系运算符,如果比较的双方是值类型,则比较的是两个值是否相等;如果比较双方是引用类型的对象,则比较的是两个对象的引用地址是否相同。

  (2)Java中String是引用类型。

  (3)第一个例子中,str1和str2是实例化出来的两个不同引用的对象,用==比较的对象的引用地址是否相同,所以结果为false;根据equals方法实现的代码可知,equals比较的是字符串的值是否相同,因为都是“1234”,所以毫无疑问,equals()比较返回true。

  (4)第二个例子中,是将同样的一个常量值"1234"分别赋值给str3、str4。实际上,当代码编译时,虚拟机会提取出值"1234",定义个常量(暂时叫t吧),将"1234"赋值给t,将t赋值给str3,然后将t赋值为str4,所以str3和str4指向的是同一块地址,结果就是==比较为true,equals和前面一样,依然是true。

  (5)第三个例子中,String val="1234";是将常量赋值给变量val,val指向这个"1234"常量的内存地址,String str5=val;是将val的引用赋值给了str5,String str6=val;也是将val的引用赋值给了str6。最终str5和str6同val一样,都指向了同“1234”这个常量内存地址。结果就是==比较为true。equals没有变化,还是true。

3、总结

(1) Java中的==,就是个关系运算符,始终遵守着它自己的规则,即:值类型比较久比较值,引用类型比较就比较内存地址。在String类中也是如此。

(2) String类中的equals()方法是重写了Object类的equals()方法,始终是比较两个字符串是否一样。

(3) 对于第二个例子,在编译器编译代码时,指定的字符串是个常量表达式,String类型的是引用类型,编译器先将常量表达式的内存地址赋值给一个常量,之后用到同样的字符串时,首先查看当前作用域中是否存在了同值的常量,如果存在就使用这个常量,导致指向了同一个内存地址。

二、C#下

1、几个例子

        static void Main(string[] args)
        {

            String str1 = ', });
            String str2 = ', });
            Console.WriteLine("①new String()方式下==:" + (str1 == str2));
            Console.WriteLine("②new String()方式下equals:" + str1.Equals(str2));

            String str3 = ";
            String str4 = ";
            Console.WriteLine("③赋值常量方式下==:" + (str3 == str4));
            Console.WriteLine("④赋值常量方式下equals:" + str3.Equals(str4));

            String val = ";
            String str5 = val;
            String str6 = val;
            Console.WriteLine("⑤赋值变量方式下==:" + (str5 == str6));
            Console.WriteLine("⑥赋值变量方式下equals:" + str5.Equals(str6));

            Console.ReadLine();
        }

运行输出:

①new String()方式下==:True
②new String()方式下equals:True
③赋值常量方式下==:True
④赋值常量方式下equals:True
⑤赋值变量方式下==:True
⑥赋值变量方式下equals:True

2、代码分析

  (0) String类的Equals方法的实现,是实现对字符串是否相同的比较。

public override bool Equals(object obj)
{
    if (this == null)
    {
        throw new NullReferenceException();
    }
    string strB = obj as string;
    if (strB == null)
    {
        return false;
    }
    if (this == obj)
    {
        return true;
    }
    if (this.Length != strB.Length)
    {
        return false;
    }
    return EqualsHelper(this, strB);
}

private static unsafe bool EqualsHelper(string strA, string strB)
{
    int length = strA.Length;
    fixed (char* chRef = &strA.m_firstChar)
    {
        fixed (char* chRef2 = &strB.m_firstChar)
        {
            char* chPtr = chRef;
            char* chPtr2 = chRef2;
            )
            {
                if (*(((int*) chPtr)) != *(((int*) chPtr2)))
                {
                    return false;
                }
                ))) != *((())))
                {
                    return false;
                }
                ))) != *((())))
                {
                    return false;
                }
                ))) != *((())))
                {
                    return false;
                }
                ))) != *((())))
                {
                    return false;
                }
                chPtr += ;
                chPtr2 += ;
                length -= ;
            }
            )
            {
                if (*(((int*) chPtr)) != *(((int*) chPtr2)))
                {
                    break;
                }
                chPtr += ;
                chPtr2 += ;
                length -= ;
            }
            );
        }
    }
}

    (1)String类中对==关系运算符进行了重写,也是实现了对字符串是否相同的比较。

public static bool operator ==(string a, string b)
{
    return Equals(a, b);
}

public static bool Equals(string a, string b)
{
    if (a == b)
    {
        return true;
    }
    if ((a == null) || (b == null))
    {
        return false;
    }
    if (a.Length != b.Length)
    {
        return false;
    }
    return EqualsHelper(a, b);
}

private static unsafe bool EqualsHelper(string strA, string strB)
{
    int length = strA.Length;
    fixed (char* chRef = &strA.m_firstChar)
    {
        fixed (char* chRef2 = &strB.m_firstChar)
        {
            char* chPtr = chRef;
            char* chPtr2 = chRef2;
            )
            {
                if (*(((int*) chPtr)) != *(((int*) chPtr2)))
                {
                    return false;
                }
                ))) != *((())))
                {
                    return false;
                }
                ))) != *((())))
                {
                    return false;
                }
                ))) != *((())))
                {
                    return false;
                }
                ))) != *((())))
                {
                    return false;
                }
                chPtr += ;
                chPtr2 += ;
                length -= ;
            }
            )
            {
                if (*(((int*) chPtr)) != *(((int*) chPtr2)))
                {
                    break;
                }
                chPtr += ;
                chPtr2 += ;
                length -= ;
            }
            );
        }
    }
}

  (2)C#下String类也是引用类型,它通过重写Equals方法和==关系运算,最终都是通过EqualsHelper方法进行对字符串的比较,所以Equals和==实质上是相同的,没有区别。

  (3) 根据以上分析,所以返回值都是True,都是根据字符串是否相同进行比较,跟对象引用没有关系,导致结果是让人感觉String是个值类型,其实是微软通过对String进行改造,使之像是个值类型而已。

3、总结

    (1) C#下String类中的==已经不是一个纯粹的关系运算符了,它的作用是比较两个字符串是否一样的,同Equals方法一致。

    (2) 为什么微软将==改写,而不是保持==是个关系运算符的本质呢?

      MSDN上的话是:"尽管 string 是引用类型,但定义相等运算符(== 和 !=)是为了比较 string 对象(而不是引用)的值。 这使得对字符串相等性的测试更为直观。"

      但是我想,这个根本原因是理念不同导致的。大家都知道C#是后来借鉴Java来设计的,当初要修改Java的String设计,对==进行重写,初衷是为了更方便用户的理解,所以就算稍微违反一些语言程序的统一性、完成性、独立性等等各种性,也要修改这个设计,这就是一个商业产品的理念:"用户体验"更重要。

 

上一篇:系统间通信(9)——通信管理与RMI 下篇


下一篇:用android的无障碍辅助权限做一个AutoPy