一道Java笔试题

问题:"abcdefg".toLowerCase() == "abcdefg"是true还是false

 

答案:

true

这里首先要注意==和equal()的区别:

对于==来讲,对于基本类型和对于对象来说是不同的,对于基本类型,如果两者的值是相同的,则相同。但是对于浮点数来讲,有以下需要注意的:

  • 如果两者之间有一个是NaN,则为false。NaN不等于任何类型,即便是它自己,所以NaN != NaN为true。
  • 对于零来说,正零和负零时相等的,-0.0 == 0.0
  • 正无穷和负无穷仅仅等于它们自己。

对于对象来讲,==相等当且仅当两者都是null(当然是相同的类型)或者指向同一个对象。

而Object类提供了方法equals(),此方法本来也是比较引用是否指向同一个对象的,

    public boolean equals(Object obj) {
    return (this == obj);
    }

然而对于有的类来说,可能有自己逻辑上的相等的概念,而不仅仅是引用的对象是否相同,比如说Integer,当两个数值确实一样,但是是两个对象的时候,我们也希望有一种办法来判定两者在逻辑上是相等的,所以会改写Object的equals函数,

    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

有时候我们创建自己的类,也需要进行逻辑比较,比如一个员工类,比较的时候逻辑上比较其员工号即可,因而我们也要改写equals函数,但要注意一下几点:

  • 首先使用==检查是否指向同一个对象,是则返回true
  • 然后使用instanceof检查参数是否是你要比较的类型,如果不是则返回false
  • 然后将参数转换为正确的类型,来比较关键的字段
  • 然后检查equals方法的对称性(x.equals(y) 则 y.equals(x)),传递性(x.equals(y), y.equals(z)则x.equals(z)),一致性(x.equals(y)在值没有改变的情况下始终相等)
  • 最后别忘了改写hashCode函数,使得你的类在基于散列值的集合类(HashMap, HashSet, Hashtable)中能够正常运作。
  • 不要把equals声明中的Object对象替换为你的类型(equals(Employee e)),因为这样你是重载了equals函数而非重写了equals函数。

下面是一个例子:

public class Employee {

    String id;

    String name;

    double salary;

    public boolean equals(Object o){

        if(this == o)

            return true;

        if(o instanceof Employee){

            Employee other = (Employee)o;

            return id.equals(other.getId());

        }

        return false;

    }

    public int hashCode() {

        return id.hashCode();

    }

    public String getId(){

        return id;

    }

}

如下实验可以证实:

Integer int1= new Integer(1);

Integer int2= new Integer(1);

Integer int3=int1; //int3和int1引用同一个对象

System.out.println("int1==int2 is " + (int1==int2));

System.out.println("int1==int3 is " + (int1==int3));

System.out.println("int1 equal int2 is " + (int1.equals(int2)));

System.out.println("int1 equal int3 is " + (int1.equals(int3)));

打印结果如下:

int1==int2 is false

int1==int3 is true

int1 equal int2 is true

int1 equal int3 is true

那如题目中,两个"abcdefg"是同一个对象吗?这就要提到String的一个性质:String常量池的概念。

以""创建的字符串在编译阶段就放在.class的Constant Pool的CONSTANT_String_info结构中。虚拟机中有以下几个区域:方法区,堆,Java栈,程序计数器,本地方法栈。ClassLoader会把Constant Pool加载到方法区中,当""创建的字符串的时候,会到String常量串池中去查找,并返回其地址赋给对象变量。

以new创建的字符串则是运行时在堆中构造的。

所以题目中的两个"abcdefg"是指向同一个地址的,以下可以证明:

String str1 = "abcdefg";

String str2 = "abcdefg";

String str3 = new String("abcdefg");

System.out.println("str1 == str2 is " + (str1 == str2));

System.out.println("str1 equal str2 is " + (str1.equals(str2)));

System.out.println("str1 == str3 is " + (str1 == str3));

System.out.println("str1 equal str3 is " + (str1.equals(str3)));

结果为:

str1 == str2 is true

str1 equal str2 is true

str1 == str3 is false

str1 equal str3 is true

然而本题目中"abcdefg".toLowerCase()按照String的不可改变性,不是应该生成另外一个对象吗?这就要看toLowerCase的实现了:

如果没有任何字符需要改变,return this;

如果做了改变则,return new String(0, count+resultOffset, result);

由此可知"ABCDEF".toLowerCase() == "abcdef"就是false了。

这里还需要提一下的是String的intern函数:它是在常量池中查找String,如果有就返回,没有就添加到常量池。

String str1 = "123";
String str2 = new String("456");
String str3 = (str1 + str2).intern();
String str4 = (str1 + str2).intern();
String str5 = str1 + str2;
String str6 = str1 + str2;    
System.out.println(str3 == str4);
System.out.println(str5 == str6);

其中str1是在常量池中,str2在堆中,str1+str2由于在编译阶段不能确定其值,因而是在运行阶段创建在堆上(如果是"123" + "456",则"123456"也在常量池中),然而intern函数将"123456"放入常量池中,并返回给str3,同样str4指向常量池中已经有的"123456",而str5,str6都是在堆上的,因而结果为true, false.

你可能感兴趣的:(java,虚拟机)