1、equals和==的区别
==对于基本类型:比较的是值是否相同;引用类型:比较的是引用是否相同,即俩个对象的地址
equals比较的是俩个对象的内容。
显然,当equals为true时,==不一定为true;当==为true时,equals一定为true;
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = "Monday";
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
}
}
结果:
s1==s2
显然:s1 与 s2 引用同一个 String 对象 -- "Monday"!,它们的地址值相等
public class TestString {
public static void main(String[] args) {
String s1 = "Monday"; //常量池中的字符串常量
String s2 = new String("Monday"); //堆内存中的字符串对象
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
if (s1.equals(s2))
System.out.println("s1 equals s2");
else
System.out.println("s1 not equals s2");
}
}
结果:
s1 != s2
s1 equals s2
显然:s1、s2分别引用了两个"Monday"String对象,而s1存在于常量池中,s2存在于堆内存中。
之所以出现s1 equals s2,是因为它们的值相等,String源码中的equals重写了Object的equals方法
Object底下的equals 源码本质上就是==
public boolean equals(Object obj) {
return (this == obj);
}
举例:
class Cat {
public Cat(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Cat c1 = new Cat("王磊");
Cat c2 = new Cat("王磊");
System.out.println(c1.equals(c2)); // false
String 重写了 Object 的 equals 方法,把引用比较改成了值比较。
public boolean equals(Object anObject) {
// 如果对象地址相同直接返回true,方法结束
if (this == anObject) {
return true;
}
// 判断anObject对象是否是String的一个实例
if (anObject instanceof String) {
// 类型转换
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
// 一个循环来检查每一个位置的字符是否一致,有一个不一致就返回false,反之返回true
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
举例:
String s1 = new String("老王");
String s2 = new String("老王");
System.out.println(s1.equals(s2)); // true
intern方法举例:
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = new String("Monday");
s2 = s2.intern();//相当于创建了s1的引用
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
if (s1.equals(s2))
System.out.println("s1 equals s2");
else
System.out.println("s1 not equals s2");
}
}
结果:
s1 == s2
s1 equals s2
程序新建了 s2 之后,又用intern()把他打翻在池子里了,这次 s2 和 s1 引用同样的对象了,成功的减少了内存的占用
如果程序里面有很多的String对象,有很多次的要用到 equals ,更好的办法:把所有的String都intern()到缓冲池去,最好在用到new的时候就进行这个操作,可以减少内存占用。
String s2 = new String("Monday").intern();
2、equals方法
1、默认情况(没有覆盖equals方法)下equals方法都是调用Object类的equals方法,而Object的equals方法主要用于判断对象的内存地址引用是不是同一个地址(是不是同一个对象)。
2 、要是类中覆盖了equals方法,那么就要根据具体的代码来确定equals方法的作用了,覆盖后一般都是通过对象的内容是否相等来判断对象是否相等。
2.1先在同一个包下面定义一个没有覆盖equals方法的Student类:
public class Student {
private int age;
private String name;
public Student() {
}
public Student(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
}
2.2测试代码:
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
public class EqualsTest {
public static void main(String[] args) {
LinkedList<Student> list = new LinkedList<Student>();
Set<Student> set = new HashSet<Student>();
Student stu1 = new Student(3,"张三");
Student stu2 = new Student(3,"张三");
System.out.println("stu1 == stu2 : "+(stu1 == stu2));
System.out.println("stu1.equals(stu2) : "+stu1.equals(stu2));
list.add(stu1);
list.add(stu2);
System.out.println("list size:"+ list.size());
set.add(stu1);
set.add(stu2);
System.out.println("set size:"+ set.size());
}
}
运行结果:
stu1 == stu2 : false
stu1.equals(stu2) : false
list size:2
set size:2
结果分析:Student类没有覆盖equals方法,stu1调用equals方法实际上调用的是Object的equals方法。所以采用对象内存地址是否相等来判断对象是否相等。因为是两个新对象所以对象的内存地址不相等,所以stu1.equals(stu2) 是false。
2.3 覆盖一下equals方法(age和name属性),让Student类其通过判断对象的内容是否相等来确定对象是否相等。
//学生类
public class Student {
private int age;
private String name;
public Student() {
}
public Student(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
运行结果:
stu1 == stu2 : false
stu1.equals(stu2) : true
list size:2
set size:2
结果分析:Student类覆盖了equals方法,比较的是俩个对象的内容是否相同
3、intern()方法讲解
3.1.String实现了常量池技术
public class TestStringIntern {
public static void main(String[] args) {
String s0="StringIntern"; //常量池中创建了字符串常量
String s1="StringIntern";
String s2="String" + "Intern";
System.out.println( s0==s1 );
System.out.println( s0==s2 );
}
}
结果:
true
true
解释:
程序在运行的时候会创建一个字符串缓冲池,字符串常量,他们在编译器就被确定了,当使用 s1= "StringIntern" 这样的表达是创建字符串的时候,程序首先会在这个String缓冲池中寻找相同值的对象,在程序中,s0先被放到了池中,所以在s1被创建的时候,程序找到了具有相同值的 s1将 s1 引用 s0所引用的对象"StringIntern"
3.2.intern方法
存在于.class文件中的常量池,在运行期被jvm装载,并且可以扩充。String的intern()方法就是扩充常量池的一个方法,当一个String的实例str调用intern()方法是,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其引用;如果没有,则在常量池中增加一个Unicode等于该实例str的字符串并返回它的引用。
注意:new String()创建的字符串不是常量,是在堆中生成的对象
public class TestStringIntern {
public static void main(String[] args) {
String s0="StringIntern";
String s1=new String("StringIntern"); //堆中创建对象
String s2=new String("StringIntern");
System.out.println( s0==s1 );
s1.intern(); //在常量池里扩展常量,发现已经存在了
s2=s2.intern(); //把常量池中“StringIntern”的引用赋给s2
System.out.println( s0==s1);
System.out.println( s0==s1.intern() );
System.out.println( s0==s2 );
}
}
结果:
false
flase
true
true
注意:
如果开始时候常量池中不存在当前字符串常量,使用new String()生成的对象str,调用intern()方法,则是在常量池中新添加一个值为str的字符串的常量,而不是复制地址。
public class TestStringIntern {
public static void main(String[] args) {
String s0="StringIntern"; //常量池中创建一个字符串
String s1=new String("StringIntern"); //堆中创建一个字符串对象
String s2=new String("StringIntern");
System.out.println( s0==s1 ); //常量池与堆中地址的比较
s1.intern(); //在常量池里扩展常量,发现已经存在了,所以给一个引用
s2=s2.intern(); //把常量池中“StringIntern”的引用赋给s2
String s3=s1.intern();//把常量池中“StringIntern”的引用赋给s3,s3在常量池中
System.out.println( s0==s1); //常量池与堆中地址的比较
System.out.println( s0==s1.intern() ); //常量池与常量池地址的比较
System.out.println( s0==s2 ); //常量池与常量池中地址的比较
System.out.println( s1==s3 ); //堆与常量池中地址的比较
}
}
结果:
false
false
true
true
false