Java程序中测试两个变量是否相等有两种方式:一种是利用==运算符,另一种是利用equalso方法。
当使用 == 来判断两个变量是否相等时,如果两个变量是基本类型变量,且都是数值类型(不一定要求数据类型严格相同,有的会自动类型转换),则只要两个变量的值相等,就将返回true;但对于两个引用类型变量,只有它们指向同一个对象时,==判断才会返回true, ==不可用于比较类型上没有父子关系的两个对象。
下面程序示范了使用 ==来判断两种类型变量是否相等的结果:
public class EqualsTest1 { public static void main(String[] args) { int int1 = 66; float float1 = 66.0f; System.out.println(int1 == float1);//输出true char ch1 = 'B'; System.out.println(int1 == ch1);//输出true String s1 = new String("Hello"); String s2 = new String("Hello"); System.out.println(s1 == s2);//输出false System.out.println(s1.equals(s2));//true //由于java.lang.String与EqualTest1类没有继承关系 //所以下面语句导致编译错误 //System.out.println("Hello" == new EqualsTest1()); } }
运行上面程序,可以看到66、 66.0f和‘B’相等。但对于 s1 和 s2 ,因为它们都是引用类型变量,它们分别指向两个通过new关键字创建的String对象,因此s1和s2两个变量不相等。对初学者而言, String还有一个非常容易迷惑的地方: "hello"直接量和new String(“hello”)有什么区别呢?当Java程序直接使用形如"hello"的字符串直接量(包括可以在编译时就计算出来的字符串值)时, JVM将会使用常量池来管理这些字符串;当使用new String(“hello”)时, JVM会先使用常量池来管理"hello"直接量,再调用String类的构造器来创建一个新的String对象,新创建的String对象被保存在堆内存中。换句话说, new String(“hello”)一共产生了两个字符串对象。
常量池(constant pool )专门用于管理在编译时被确定并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口中的常量,还包括字符串常量。
下面程序示范了JVM使用常量池管理字符串直接量的情形:
public class ConstantPoolTest { public static void main(String[] args){ String s1 = "Jvm常量池"; String s2 = "Jvm"; String s3 = "常量池"; //s4与s5后面的字符串值可以在编译时就确定下来 //s4与s5直接引用常量池中的"Jvm常量池" String s4 = "Jvm" + "常量池"; String s5 = "Jvm" + "常量" + "池"; // s6后面的字符串值不能在编译时就确定下来 //不能引用常量池中的字符串 String s6 = s2 + s3; //使用new调用构造器将会创建一个新的String对象 //s7引用堆内存中新创建的string对象 String s7 = new String("Jvm常量池"); System.out.println(s1 == s4);//true System.out.println(s1 == s5);//true System.out.println(s1 == s6);//false System.out.println(s1 == s7);//false } }
JVM常量池保证相同的字符串直接量只有一个,不会产生多个副本。例子中的s1,s4,s5所引用的字符串可以在编译期就确定下来,因此它们都将引用常量池中的同一个字符串对象。
使用new String()创建的字符串对象是运行时创建出来的,它被保存在运行时内存区(即堆内存)
内,不会放入常量池中。
在很多时候,程序判断两个引用变量是否相等时,也希望有一种类似于“值相等”的判断规则,并不严格要求两个引用变量指向同一个对象。例如对于两个字符串变量,可能只是要求它们引用字符串对象里包含的字符序列相同即可认为相等。此时就可以利用String对象的equals()方法来进行判断,例如上面程序中的s1.equals(s7)将返回true.
equals()方法是Object类提供的一个实例方法,因此所有引用变量都可调用该方法来判断是否与其他引用变量相等。但使用这个方法判断两个对象相等的标准与使用一运算符没有区别,同样要求两个引用变量指向同一个对象才会返回true。因此这个Object类提供的equals()方法没有太大的实际意义,如果希望采用自定义的相等标准,则可采用重写equals方法来实现。
String已经重写了Object的equals()方法, String的equals()方法判断两个字符串相等的标准是:只要两个字符串所包含的字符序列相同,通过equals)比较将返回true,否则将返回false.
很多书上经常说equals()方法是判断两个对象的值相等,这个说法并不准确,什么叫对象的值呢?对象的值如何相等?实际上,重写equals)方法就是提供自定义的相等标准,你认为怎样是相等,那就怎样是相等,一切都是你做主!在极端的情况下,你可以让Person对象和Dog对象相等。
class Person{ private String name; private String idStr; public Person(){}; public Person(String name, String idStr) { this.name = name; this.idStr = idStr; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getIdStr() { return idStr; } public void setIdStr(String idStr) { this.idStr = idStr; } //此处重写equals()方法,自定义相等的标准 @Override public boolean equals(Object obj) { if(this == obj) return true; if (obj != null || obj.getClass() == Person.class) { Person person = (Person) obj; if(this.getIdStr().equals(person.getIdStr())){ return true; } } return false; } } public class EqualsTest2 { public static void main(String[] args) { Person person1 = new Person("张三" , "159798"); Person person2 = new Person("张伟" , "159798"); Person person3 = new Person("张四" , "265448"); System.out.println(person1.equals(person2));//true System.out.println(person2.equals(person3));//false } }
上面程序重写Person类的equals()方法,指定了Person对象和其他对象相等的标准:另一个对象必须是Person类的实例,且两个Person对象的idStr相等,即可判断两个Person对象相等。在这种判断标准下,可认为只要两个Person对象的身份z字符串相等,即可判断相等。
通常而言,正确地重写equals)方法应该满足下列条件:
(1)自反性:对任意x, x.equals(x)一定返回true.
(2)对称性:对任意x和y,如果y.equals(x)返回true,则x.equals(y)也返回true.
(3)传递性:对任意x, y, z,如果x.equals(y)返回ture, y.equals(z)返回true,则x.equals(z)一定返回
true.
(4)一致性:对任意x和y,如果对象中用于等价比较的信息没有改变,那么无论调用x.equals(y)多少次,返回的结果应该保持一致,要么一直是true,要么一直是false.
(5)对任何不是null的x, x.equals(null)一定返回false.
Object默认提供的equals()只是比较对象的地址,即Object类的equals()方法比较的结果与==运算符比较的结果完全相同。因此,在实际应用中常常需要重写equals()方法,重写equals方法时,相等条件是由业务要求决定的,因此equals()方法的实现也是由业务要求决定的。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)