Java的final关键字与String的内部比较方法

最近抽了点时间温故,一些零零散散的问题仍是整理了起来。我决定把一些曾经坑过本身的问题写成博客文章,给学弟学妹们一个警示吧。java

今天的故事从一个例子开始:shell

@Test
	public void testFinal()
	{
		String s1="happyBKsOffer";
		String s2="happyBKs";
		final String s3="happyBKs";//s3.replace("h", "H");
		String s4=s2+"Offer";
		String s5=s3+"Offer";
		if (s1 == s4) {
			System.out.println("s1==s4");
		} else {
			System.out.println("s1!=s4");
		}
		if (s1 == s5) {
			System.out.println("s1==s5");
		} else {
			System.out.println("s1!=s5");
		}
		
		if (s1.equals(s4)) {
			System.out.println("s1 equals s4");
		} else {
			System.out.println("s1 not equals s4");
		}
		if (s1.equals(s5)) {
			System.out.println("s1 equals s5");
		} else {
			System.out.println("s1 not equals s5");
		}
		
		
	}

若是你看到这个代码以为莫名其妙而且伴随着一点心虚,以为不都是同样的嘛,那么恭喜你,看完了今天的例子,你就不用再往坑里跳了。若是你一眼就看出了其中的坑,那么也恭喜你,能够在我博客下方评论处BB了,“这也算个问题,太基础了”。(本文出自:http://my.oschina.net/happyBKs/blog/493904,请自行代表出处)数组

实际的运行结果以下:安全

s1!=s4
s1==s5
s1 equals s4
s1 equals s5

有没有一点出乎您的意料呢?app


那么这究竟是怎么了,s4和s5都是"happBKs"与"Offer"的凭借,为何再==和equals下的结果不相同呢?性能

咱们先说说==和equals方法的区别。这两种判断两个值是否相等的方式其实存在本质的不一样。==在断定基本数据类型时,没有什么不一样,就是判断连个基本数据类型的值是否相等;在断定对象数据类型时,会根据对象的类中覆盖的equals方法来断定对象是否相等(若是没有从新实现类的equals方法,运行时会断定两个对象的地址是否指向同一个对象)。优化

这时候,也许纯洁无邪的孩子们会说:“我明白了,若是是int类型,那么equals和==没什么区别,Integer类型则不是”。这时候,就会有人在旁边阴笑,内心想:“拆箱和装箱都不懂,呵呵。。。结果确定都同样”spa

我给个例子:.net

@Test
	public void testInt()
	{
		int a=1,b=1;
		if (a == b) {
			System.out.println("a==b");
		} else {
			System.out.println("a!=b");
		}
		
		
//		if (a.equals(b)) {
//			System.out.println("a equals b");
//		} else {
//			System.out.println("a not equals b");
//		}
		
		Integer c=new Integer(1),d=new Integer(1);
		if (c == d) {
			System.out.println("c==d");
		} else {
			System.out.println("c!=d");
		}
		
		
		if (c.equals(d)) {
			System.out.println("c equals d");
		} else {
			System.out.println("c not equals d");
		}
		
		
		Integer e=1,f=1;
		if (e == f) {
			System.out.println("e==f");
		} else {
			System.out.println("e!=f");
		}
		
		
		if (e.equals(f)) {
			System.out.println("e equals f");
		} else {
			System.out.println("e not equals f");
		}
	}

那么结果会怎么样呢?线程

a==b
c!=d
c equals d
1
e==f
e equals f

是的,笑别人单纯的人眼睛没有瞎,真的结果就如同小朋友们料想的那么单纯。好了,别捶胸顿足故做恍然大悟了,单纯的结果后面有着不单纯的缘由。

首先int的==比较没有什么可多说的。为何Integer类型的c和d,e和f在equals时会有两种不一样的记过呢?这是由于java编译器的优化机制造就的:c和d各自初始化了一个对象类型,因此在==比较时,他们指的不是一个对象,因此==比较是不一样的,而Integer的equals方法已经重写,比较的是二者实际的整型数值,因此是相等的。e和f的确用到了装箱的拆箱,可是java编译器在编译时作了优化,将两个常量1装箱后优化为一个对象,这样e和f指向同一个Integer对象了,因此在==时是相同的。

回到咱们刚才的实例中。s2和s3虽然都是"happyBKs",可是s2是final类型,s3是通常的类型。在java编译器编译时,一样会对常量数值作一些优化,编译器会将s4=s2+"Offer"进行优化,由于s2是常量、"Offer"也是常量,因此,s4会在编译后直接被转换成s4="happyBKsOffer",而s1的初始化赋值也是常量"happyBKsOffer",因此编译器会再次将它们优化为一个String对象,s1和s4指向同一个String对象,因此二者在==是相同的。而s5=s3+"Offer",s3不是final的,因此必须在运行时计算,这样s5只有在运行时候才能生成一个对象,确定与s1的那个不是同一个String对象,因此s1与s5在==比较时确定是不一样的。

明白了吧。也许你仍是很揪心,咱们这里只给出一个建议吧:那就是,尽可能养成用equals的好习惯!


好最后,咱们队final 自己再作个总结:final能够修饰类、方法和变量。

修饰类

当用final修饰一个类时,代表这个类不能被继承。也就是说,若是一个类你永远不会让他被继承,就能够用final进行修饰。final类中的成员变量能够根据须要设为final,可是要注意final类中的全部成员方法都会被隐式地指定为final方法。

  在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在之后不会用来继承或者出于安全的考虑,尽可能不要将类设计为final类。

修饰方法

  使用final方法的缘由有两个。第一个缘由是把方法锁定,以防任何继承类修改它的含义;第二个缘由是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。可是若是方法过于庞大,可能看不到内嵌调用带来的任何性能提高。在最近的Java版本中,不须要使用final方法进行这些优化了。

  所以,若是只有在想明确禁止 该方法在子类中被覆盖的状况下才将方法设置为final的。

  注:类的private方法会隐式地被指定为final方法。

修饰变量

  修饰变量是final用得最多的地方,也是本文接下来要重点阐述的内容。首先了解一下final变量的基本语法:

  对于一个final变量,若是是基本数据类型的变量,则其数值一旦在初始化以后便不能更改;若是是引用类型的变量,则在对其初始化以后便不能再让其指向另外一个对象。


String类型咱们也作一个补充总结:

String不是基本类型,而是对象类型,而且,其内部哟一个final的char数组,所以String的方法中不提供修改String中内容字符的方法。即使是subString、replace等看似修改了String的方法,其实只是返回了一个新的String对象,而原数组没有变化。这样作的目的有性能的、线程安全诸多方面的考虑,之后我会专门开一个文章讲这个。

相关文章
相关标签/搜索