前言
在Java 程序中要判断两个变量是否相等有两种方式 ,一种是利用=运算符,另一种是利用 equals() 方法。
==和 equals 方法
当使用==来判断两个变量是否相等时,如果两个变量是基本类型变量 ,且都是数值类型(不一定要求数据类型严格相同) ,则只要两个变量的值相等,就将返回 true。
但对于两个引用类型变量,只有它们指向同一个对象时,==判断才会返回 true。==不可用于比较类型上没有父子关系的两个对象。下面程序示范了使用来判断两种类型变量是否相等的结果。
public class EqualTest
{
public static void main(String[] args)
{
int it = 65;
float fl = 65.0f;
// 将输出true
System.out.println("65和65.0f是否相等?" + (it == fl));
char ch = 'A';
// 将输出true
System.out.println("65和'A'是否相等?" + (it == ch));
String str1 = new String("hello");
String str2 = new String("hello");
// 将输出false
//分别指向两个通过 new 关键字创建的 String 对象
System.out.println("str1和str2是否相等?"
+ (str1 == str2));
// 将输出true
//String重写的equals()方法是判断两个符串所包含的字符序列(两个对象的值)相等
System.out.println("str1是否equals str2?"
+ (str1.equals(str2)));
// 由于java.lang.String与EqualTest类没有继承关系,
// 所以下面语句导致编译错误
// System.out.println("hello" == new EqualTest());
}
}
“hello” 直接量和 newString(“hello”) 有什么区别呢?
当 Java 程序直接使用形如"hello" 的字符串直接量( 包括可以在编译时就计算出来的字符串值)时,JVM 将会使用常量池来管理这些字符串;当使用 new String(“hello”) 时,JVM 会先使用常量池来管理"hello" 直接量 ,再调用String 类的构造器来创建一个新的 String 对象,新创建的 String 对象被保存在堆内存中。换句话说,new String(“hello”) 共产生了两个字符串对象。
提示:常量池专门用于管理在编译时被确定并被保存在已编译的.class文件中的一些数据,它包括了关于类、方法、接口中的常量,还包括字符串常量。
下面程序示范了JVM 使用常量池管理字符串直接量的情形。
public class StringCompareTest
{
public static void main(String[] args)
{
// s1直接引用常量池中的"疯狂Java"
String s1 = "疯狂Java";
String s2 = "疯狂";
String s3 = "Java";
// s4后面的字符串值可以在编译时就确定下来
// s4直接引用常量池中的"疯狂Java"
String s4 = "疯狂" + "Java";
// s5后面的字符串值可以在编译时就确定下来
// s5直接引用常量池中的"疯狂Java"
String s5 = "疯" + "狂" + "Java";
// s6后面的字符串值不能在编译时就确定下来,
// 不能引用常量池中的字符串
String s6 = s2 + s3;
// 使用new调用构造器将会创建一个新的String对象,
// s7引用堆内存中新创建的String对象
String s7 = new String("疯狂Java");
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()创建的字符串对象是运行时创建出来的,它被保存在运行时内存区(即堆内存)内,不会放入常量池中。
Object 默认提供的 equals() 只是比较对象的地址,即 Object 类的 equals()方法比较的结果与==运算符比较的结果完全相同。因此,在实际应用中常常需要重写 equals 方法,比如想表示对象的内容相同时,重写 equals 方法时,相等条件是由业务要求决定的,因此, equals()方法的实现也是由业务要求决定的。
那上面我们提到了equals()方法的重写,不得不提到hashCode方法。只是简单介绍以下。
返回一个hash code码,Integer,内存地址有关的hash算法
如果equals返回true,hashCode相同,重写hashCode
public class Emp {
private String str;
public Emp(String str){
this.str = str;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((str == null) ? 0 : str.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Emp other = (Emp) obj;
if (str == null) {
if (other.str != null)
return false;
} else if (!str.equals(other.str))
return false;
return true;
}
}
public class TestEmp {
public static void main(String[] args) {
Object obj ;
Emp emp1 = new Emp("hello");
Emp emp2 = new Emp("hello");
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(emp1==emp2);
System.out.println(str1==str2);
System.out.println(emp1.equals(emp2));
System.out.println(str1.equals(str2));
System.out.println(emp1.hashCode());
System.out.println(emp2.hashCode());
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
}
}
结果:
false
false
true
true
99162353
99162353
99162322
99162322
public class Demo {
static void test1(){
String str = "a";
String str2 = str +"b";
//底层为 str2 = new StringBuilder().append("b").toString();
//以上代码一共有几个对象1 2 3 4 ?
System.out.println(str2=="ab");//false
String str3 = "hello";
System.out.println(str3=="hello");//true
String str4 = new String("hello");
System.out.println(str4 == str3);//false
//字符串不可变
String str5 = new String("hello");
str5 = str5.concat("!!!");//字符串连接
System.out.println(str5);//hello!!!
}
public static void main(String[] args) {
test1();
}
}