深入Java字符串

本博客为本人原创,如要转发请申明 奋斗


分享一下Java中的字符串,在java中的字符串不是基础数据类型,而是一个引用数据类型,它是一个类。它的底层是用final修辞的,所以一旦定义就不可以改变。其实String底层就是一个Char[]数组。

下面是String的一部分代码:
下面这段代码就可以说明String类型是用final修辞的,而用final修辞的变量为常量,一经声明就不能改变。 这也就说明字符串类型不能改的原因


String类型本质上就是一个char数组一个一个去拼接而成的


也许有很多人说我是在放屁,谁说不可以修改的。请看下面的例子:
例1:
public class Test {

	public static void main(String[] args){
		
		String str = "LuoXiang";
		str = "SB";
		System.out.println(str);
	}
}


其实在javaJVM中有一个专门存储字符串的区域叫作“字符串常量池”,这是JVM为了减少字符对象的重复创建而建立的。 这里有个特别坑的地方要注意,就是里面的字符串不会被gc机制清除(gc:java的垃圾回收器),只要程序还在运行,这个常量池中的字符串就一直存大在,就算这个字符串的引用已经消亡了,但它的字符串是永远不会回收的。
上面的例1字符串表面上虽然改变了,但它在“字符串常量词”中是这样表示的,String str = "luoxiang";它在编译期就存入常量词中,当运行到str = "SB";这行代码时,JVM中的字符常量池会先看看这个池中有没有“SB”这个字符串,如果有就直接指向,否则就创建一个。当创建了"SB"这个字符串时并把它指向了str这个引用。"LuoXiang"还是“Luoxiang”,没有改变,只是指向发生了改变。而这个没有指向的字符串“LuoXiang”是不会回收的。

例2:字符串拼接
public class Test {

	public static void main(String[] args){
		
		String str = "luoxiang";
		String str1 = "luo"+"xiang";
		System.out.println("str和str1的内存地址比较:"+(str == str1));  //true
		System.out.println("str和str1的字面值比较:"+(str.equals(str1)));  //true
	}
}


大家再看看这段代码,字符串str和str2相等,当封“装数据类型”用==去比较两个值的内存地址时,返回结果是:true。这说明str和str2是相同的地址。有的人可能不相信这封装数据类型,可能会问,基础数据类型也是这么写的啊?那好,我就让你心服口服,再来看最后一段代码,是有equals方法去比较的,大家要清楚,equals方法只有是由类或引用数据类型去调用的,如果基础数据类型去调用编译都通不过。
例2代码在javaJVM中是这样来做的,前面我提到了java字符常量池中的字符是不会被gc回收的,当String str = "luoxiang";在编译期就存入常量词中。当执行到这行代码的时候 String str2 = "luo" + "xiang";当它拼接成luoxiang的时候,JVM就会发现在之前就已经定了一个叫“luoxiang”的字符串时,这时它就不再指向当前这个字符串了,而转身指向str这个字符串了,所以我们看内存地址比较是相同的。那个拼接的字符串呢,它不会被gc回收,而是一直在常量池中占着茅坑不拉屎,这时它已经没有任何指向,等同于废代码。所以在开发中禁止使用字符串拼接,因为这样会浪费性能,优其是SQL拼接的时候。 但是为了解决这一系列问题,又产生了StringBuffer和StringBuilder。两个方法表面上看不什么区别,但唯一的区别就是StringBuffer是线程安全的,StringBuilder是线程不安全的。 从某种程度上来说,不使用线程的情况下,StringBuilder的性能高于StringBuffer。

例3:内容相同的字符串
public class Test {

	public static void main(String[] args){
		
		String str = "爱生活";
		String str1 = "爱生活";
		System.out.println("str1没改变前的:"+str1);
		str1 = "我改变了";
		//返回结果是true,说明地址相同
		System.out.println("str和str1的内存地址比较:"+(str == str1));  //true
		//这个说明之前str和str1的地址是相同,但str1的值改变之后,相对的内存地址也改变了
		System.out.println("str改变后的:"+str1);
	
	}
}

这里可以看出,当运行到str2时,JVM发现str2的字符串在常量池中有,它也就不会再创建了,而是将指针指向str了。str2当时是指向str的,可是突然之间str2就改变了,变成了“我改变了”,这时str2也就不再指向str了,但有人会问,内存地址相同,那是不是改变str的值是不是str2的值是不是也会相对的改变呢?对于这个问题,我的回答是no,在Java中已经做过处理了,不会改变。

例4:new出来的字符串
public class Test {

	public static void main(String[] args){
		
		String str = "我是一个人";
		String str1 = new String("我是一个人");
		System.out.println("str和str1内存地址比较:"+(str == str1));  //false
		
	}
}

可以看到str和str1的内存地址不相等,为什么会是这样的结果呢?原因是:String str = new String("我是一个人");它也会在字符常量池中开辟空间,但它和String str1 = "我是一个人";有什么区别呢?请往下看,String str = new String("我是一个人");这行代码它使用了一个new关键字,所谓new,它不管你堆内存中有没有定义,它一定会开辟一个新的空间,并指向str。明白了这点就好办了,它首先会在堆内存空间中开辟一个“我是一个人”的字符串,然后它还会去字符常量池中去寻找有有没有相同字符串,如果有的话,就不会在字符常量池中开辟空间,直接把这个字符串的使用权给str。如果没有的话它就会在字符常量池中创建一个新的字符串,不要忘了new出来的字符串还是一个字符串。所以new出来的字符串在堆内存中还有一个空间,而非new出来的字符串没有堆内存空间。
今天的分享就到这里了,希望能帮助各位初学者一点,感谢大家的阅读,博主知识有限,如果有错误请点评出来 微笑
2017/10/15



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值