Java中String的理解

Java中String的理解


最近在读String的源码,看了些String的文章,自己对String作了下总结记录下来。

1.String为什么是不可变的?


String是final类,不可继承,其方法也不可被覆盖,避免从子类操纵父类属性;String的值保存在private final char[]数组中,本质是一个字符数组,私有则外部不可访问和修改,final引用则引用(或说引用的值)不变。引用可以简单地认为是堆上对象的首地址。String内部的private int hash,缓存hash值,hashCode和equals用来比较对象相等,缓存hash可以减少运算时间提高效率。但是,String的值char[]可以通过反射改变。String不可变是出于安全和使用效率考虑的。

2.String的split,substring,replace,replaceAll等方法有哪些注意点?


charAt,toLowerCase,toUpperCase,trim,toCharArray等常用方法并没有太多注意点。

split(String regex,int limit):regex,切片的正则;limit,切片数。

split(String regex):regex,切片的正则。从这里可以看出切片是使用正则来切片的,而且要注意的是,尾部切片得到的空字符串""(注意没有空格)是不会出现的结果的字符串数组中的。

思考:

String s=“,1,2,,3,,,”;

String[] arr=s.split(",");

arr的值是什么?

结果是{"",“1”,“2”,“”,“3”}。如果制定切片数limit则会按切片数切片。

substring(int beginIndex,int endInx):beginIndex是包含的,end不包含。

replace():有两个方法,接收char替换,接收字符序列(字符串)替换。

replaceAll(String regex,String replacement):用replacement替换所有的符合regex正则的子串。

3.String,StringBuilder,StringBuffer


对String的修改总是返回新的String,原String不变。

StringBuiler非线程安全,StringBuffer线程安全。StringBuffer认为线程安全是其方法使用synchronized同步,同时也会承担其带来的开销。

对大量修改字符串的操作推荐使用StringBuilder。

4.toString()方法,String与“+”操作符,对象与Object的toString方法


所有类都间接继承来自Object,所有对象实例都是Objcet的实例,如果不重写,则继承其toString(),hashCode(),equals()方法。打印日志,系统输出等带有“显示”含义的对实例的表示都是调用其toString()方法。默认的toString方法时Class Name+"@"+十六进制的hashCode值。String的“+”可以看做所谓的运算符重载,但是Java没有运算符重载。String的“+”,和数值的“+”有所不同,一般情况String的“+"就是join操作把字符或字符串拼接。

看下面例子:

System.out.println(s+5+6);
System.out.println(5+6+s);
System.out.println(5+s+6);
System.out.println(s+(5+6));```
思考输出结果。
<br>结果是:
<br>hello56
<br>11hello
<br>5hello6
<br>hello11
<br>应避免此类写法或者加上括号指定运算顺序。

##5.String在JVM的存储
<br>JVM在创建我们常见的字符串常量,比如String s=“abcd”,和字符串对象,比如String s=new  String("abcd")时行为有所区别。
<br>字符串常量:比如String s="abcd",创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。常量池中不会不存在两个相同的字符串。这里线程栈内String类的引用s指向常量池中的“abcd”对象,s的值简单理解为常量池中的“abcd”对象的地址。
<br>字符串对象:String  s=new String(“abcd”);注意这里没有上面String s="abcd"的先决条件。在类加载时,常量池会创建“abcd”对象,指向代码时在堆上创建一个String对象,该String对象保存到常量池“abcd”对象的引用,s保存到堆上该String对象的引用。
创建字符串总是先去常量池找是否已有当前字符串对象,没有才去堆上分配,在堆上新分配的也不会加到常量池

<br>思考:
```String s1="ABC";
String s2="ABC";
String s3=new String("ABC");
System.out.println(s1==s2);
System.out.println(s1==s3);```
<br>输出结果是什么?
<br>结果是:true
<br>false
<br>new String()总是会在堆上开辟新的内存,但该块内存存储的字符串信息会先去常量池查查找,如果常量池有,则直接存下常量池的字符串的引用;如果没有则在堆上保存字符串信息。
如下图:
![](https://img2018.cnblogs.com/blog/1476330/201903/1476330-20190313114258718-1875513481.jpg)

这里具体可以参考这篇文章:
[深入理解Java中的String](https://www.cnblogs.com/xiaoxi/p/6036701.html)
##6.String,对象,集合的判空和判null
<br>对于某个对象判null,直接s==null即可。在日常学习工作中,很多情况都是对空字符串,空集合的判断。
<br>集合的判空不能用==null,应该用工具类,其内部实现一般是判null或元素数为0则为空集合。
<br>对于一个指定为String类型的引用s,如果判断空字符串?
<br>思考:
<br>String s=null;
<br>System.out.println(s);
<br>输出:null

<br>思考:
<br>System.out.println("".equals." ");
<br>输出:false
<br>对于字符串的判空还是要借助工具类才能保证全面性。思路是:判null,trim去除头尾空格再判空,而且对于“null”本身这个字符串应该视为<br>是空字符串,在某些序列化结果和数据库返回结果,null会被直接传送返回,应该剔除这种情况。在日常学习工作中,判空,判null,判等这些<br>操作一定要特别注意。


<br><br>对String的一些思考和理解暂时先写到这里,以后如果有新思考会更新到这篇。
posted @ 2019-03-13 11:43  Warspite  阅读(713)  评论(0编辑  收藏  举报