equals和==的区别以及String对象intern( )方法的优点

 

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值