用好字符串,提防陷阱

最基础的知识总是最容易忘记的,所以在回顾一下字符串吧,加深记忆


1、定义字符串的两种最常见的形式

String s1 = new String("字符串"); (1)
String s2 = "字符串";   (2)

那这两种有什么区别呢

实际上,由(1)种首先JVM会查看一下字符串池中有没有"字符串"这个直接量,没有的话,就在字符串池创建相应的直接量对应的字符串对象,然后再由new String()构造器返回的字符串对象,所以他实际上创建2个字符串对象

而第(2)种则只是检查字符串池中有没有"字符串"这个直接量,没有的话,就在字符串池创建相应的直接量对应的字符串对象

所以,可见,直接量创建的方式要比new方式创建效率高(其他封装类型也有相识的情况)


public class Test1
{
	public static void main(String[] args)
	{
		String s1 = "字符串";
		
		String s2 = "字符串";
		
		//输出true
		System.out.println(s1 == s2);
	}
}


所以,由于s1 和 s2 都是指向字符串变量中的“字符串”,所以他们的物理地址相等,所以 == 为true


2、字符串连接表达式可以在编译是确定下来,那么JVM会在编译是计算字符串变量的值,并让它指向字符串池中对应的字符串

public class Test2
{
	public static void main(String[] args)
	{
		String s1 = "字符串10";
		
		String s2 = "字" + "符串" + 10;
		
		//输出true
		System.out.println(s1 == s2);
	}
}

由于s1 、s2 在编译阶段已经指向了同一个直接量,所以运行时 == 为true


3、字符连接要是涉及到方法、变量(不是宏替换 final)时,编译时无语确定他的实际值

public class Test3
{
	public static void main(String[] args)
	{
		int len = 3;
		
		String s1 = "字符串的长度:3";
		String s2 = "字符串的长度"+"字符串".length();
		String s3 = "字符串的长度:" + len;
		
		//两个都输出false
		System.out.println(s1 == s2);
		System.out.println(s1 == s3);
	}
}


由于编译时,无法确定s2、s3变量的值,所以 == 为false;


然而,当变量为宏替换时,在编译阶段就已经替换为相应的值,也就是值已经确定

public class Test4
{
	public static void main(String[] args)
	{
		final int len = 3;

		String s1 = "字符串的长度:3";
		String s2 = "字符串的长度:" + len;

		//输出true
		System.out.println(s1 == s2);
	}
}

4、不可变长字符串

String s = "字符串" + "的长度" + "3";

看一下这段代码产生几个变量?

实际上,只有一个,因为JVM会在编译是就计算出s的值为“字符串的长度为3”,所以将该字符串存放在字符串池中,并让s指向它


那在看一下代码

public class Test5
{
	public static void main(String[] args)
	{
		String s = "字符串";    // 1
		System.out.println(System.identityHashCode(s));
		
		s = s + "的长度为:";    // 2
		System.out.println(System.identityHashCode(s));
		
		s = s + 3;             //3 
		System.out.println(System.identityHashCode(s));
		
	}
}

结果:


明明是同一个变量,为什么结果确实不相同的呢?

原来String字符串是不可变长的,也就是在第1句代码执行时,jvm将“字符串”直接量存入字符串迟,再将s指向它;在第2句执行时,jvm又生成“字符串的长度为”的直接量,再将s指向它,于是字符串池就有两个字符串变量了;第3句执行时字符串池就有三个变量了;

所以指向对象不同,hashcode肯定不同了,这种情况还要注意,因为在垃圾回收时,字符串池的变量不会被回收,很容易造成内存泄漏


所以,推荐使用StringBuffer、StringBuilder

两者的区别就是StringBuffer大部分方法是线程安全的,而效率也会低一点了

String由于不可变长,字符串改变时,指向变量改变,所以也是线程安全的了


public class Test6
{
	
	public static void main(String[] args)
	{
		StringBuilder s = new StringBuilder("字符串");
		System.out.println(System.identityHashCode(s));
		
		s.append("的长度");
		System.out.println(System.identityHashCode(s));
		
		s.append(3);
		System.out.println(System.identityHashCode(s));
		
		s.replace(0, 1, "23");
		System.out.println(s);
		System.out.println(System.identityHashCode(s));
	}
}

结果:



总结:

1、String是不可变长的,同时他的new出来的对象会产生两个对象

2、在字符串连接比交多的地方,建议使用StringBuffer、StringBuilder







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值