1.String简介
String是java.lang包下的一个类,它不属于基本数据类型,是我们使用频率非常高的一个类。它是一个被final修饰的类,不能够被继承。
从上面的类图可以知道:
1.String实现了Serializable接口,支持序列化
2.String实现了Comparable接口,可以进行比较。
3.String实现了CharSequence接口,实现了length()、charAt()、subSequence()等方法。
2.String的成员变量
1.String类型其实就是一个字符数组,这个字符数组用final修饰了,不能修改。所以API中进行修改字符串的方法都是返回一个新的字符串,需要重新引用才行。
2.String缓存了hash值,不用每次都计算hash值,提高了效率。
3.String类的一些方法
StringAPI中方法太多了,我就不一一介绍了,挑几个有价值的方法的分析一下。
1.Java中char类型是Unicode编码的,我们用的字符串可能是"ISO-8859-1"等其它字符集编码,如果直接使用很有可能会出现乱码的问题。
String的一个构造方法,这个构造方法可以传递一个编码名称,让String可以根据编码名称进行解码,以解决乱码的问题。
通过源码分析,我们可以知道,它是通过StringCoding工具类进行转码操作的。StringCoding这个工具类会通过Charset去判断字符集是否支持,不支持就抛出异常UnsupportedEncodingException,最终会通过相应的Decoder进行解码。
2.String中split()方法,进行字符串的切割。
public String[] split(String regex, int limit) {
char ch = 0;
//特殊情况判断
// 单字符情况下regex不等于正则表达式的元字符(meta character):.$|()[{^?*+\\
//双字符情况下regex第一个字符是反斜杠,第二个字符不是Unicode编码中的数字或字母
if (((regex.value.length == 1 &&
".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
(regex.length() == 2 &&
regex.charAt(0) == '\\' &&
(((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
((ch-'a')|('z'-ch)) < 0 &&
((ch-'A')|('Z'-ch)) < 0)) &&
(ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE))
{
int off = 0;
int next = 0;
boolean limited = limit > 0;
ArrayList<String> list = new ArrayList<>();
while ((next = indexOf(ch, off)) != -1) {
if (!limited || list.size() < limit - 1) {
list.add(substring(off, next));
off = next + 1;
} else {
list.add(substring(off, value.length));
off = value.length;
break;
}
}
//没有匹配到,将原字符串返回
if (off == 0)
return new String[]{this};
//如果没有限制或者小于限制,将剩余的字符串添加进去
if (!limited || list.size() < limit)
list.add(substring(off, value.length));
int resultSize = list.size();
if (limit == 0) {//将结果集最后为""的字符串一个一个删除
while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
resultSize--;
}
}
String[] result = new String[resultSize];
return list.subList(0, resultSize).toArray(result);
}
return Pattern.compile(regex).split(this, limit);
}
从源码中分析可以得知,split()有两个参数,在limit等于0的情况下,从最后一个子字符串往前,所有的空字符串""都会被清除,所以有可能得到的String数组会小于期望值,如果limit>0,会最多切割limit-1次,没有切割的部分直接添加到结果集中。
注意:参数regex是一个 regular-expression的匹配模式,所以特殊字符需要转义,例如"|"这个字符,需要写成"\|"。
3.String类中有一个本地方法intern()
这个方法的作用是:在JDK1.6中,是将该字符串添加到常量池中并且返回指向该常量的引用,如果常量池中已经存在,则直接返回指向该常量的引用,在JDK1.7以后(包括1.7),常量池从方法区的PermGenSpace移动到了堆中,首先判断这个常量是否存在于常量池,如果存在,判断存在内容是引用还是常量,如果是引用,返回引用地址指向堆空间对象,如果是常量,直接返回常量池常量如果不存在,将当前对象引用复制到常量池,并且返回的是当前对象的引用。
详细分析可以参考一下:美团技术团队《深入解析String#intern》
4.String相关的一些问题
为什么经常用String作为HashMap的Key呢?
String缓存了hash code,提高了效率。其次是,String是一个不可变类,如果HashMap使用可以改变的类,会存在数据找不到的问题。例如用一个用一个可变类Student,Student stu = new Student();map.put(stu,value);如果stu的值发生了改变,map中的存储的key也会发生改变。下次通过stu访问map的时候,stu的hash值发生了改变,就找不到stu原来所在的位置了。
遇到的一个BUG,有网友能给出答案吗?
从美团技术团队《深入解析String#intern》这篇文章,我可以很清楚的intern的用法了,在JDK1.6和JDK1.7以后的区别,但是,我在测试的时候,遇到了下面一个难以解释的问题~!
测试环境:JDK1.8,Junit4.10
在同样的环境下进行测试,但是两段类似的代码,执行的结果竟然不一样。
但是将test1()中的代码放到main方法中执行,输出的结果又是true。
根据intern在1.8中的作用,s1.intern()应该是将字符串"1"在内存中的引用放置常量池中,s2="11",s2得到的应该是s1在常量池中的引用,所以s1==s2应该结果为true
为什么在Junit测试中,结果为false?而且就单独"1"会出现这个问题,其他字符都不会。