String底层由char数组组成
jdk1.7后,永久代被元空间替换,字符串常量池从方法区移动到java堆中
重要方法如下:
一、多构造方法
1.以String为参数的构造方法
2.以char数组为参数的构造方法
3.以StringBuffer为参数的构造方法
4.以StringBuilder为参数的构造方法
5.byte数组和int数组为参数的构造方法
二、equals方法(接收object类型的参数,返回boolean)
1.直接对比内存地址
2.对比equals里的参数是不是String类型(instanceof String)
3.对比字符串长度
4.循环对比char数组
(扩展,equalsIgnoreCaseequalsIgnoreCase,先是对比地址,然后对比长度,最后全转大写循环校验)
三、compareTo(接受String类型,返回int类型)
返回值为int类型(正数,负数,0)
循环对比char数组相同索引的值(length为两者间短的),遇到不同的char时返回char与char的差
返回length-lenght
(扩展,compareToIgnoreCase,循环(length为两者间短的)对比同索引char的值,如果不同则转成大写,如果不同则转成小写,如果不同则返回相减值,如果循环完了还没返回就返回长度差)
当equals返回true或compareTo返回0时表示字符串完全相等。
四、常用方法
indexOf(查询字符串首次出现下标位置)
lastIndexOf(查询字符串最后一次出现的下标位置)
contains(查询字符串是否包含另一个字符串)
return indexOf>-1
toLowerCase(转成小写)
toUpperCase(转成大写)
大小写转换底层写了一堆的case,效率快但是可读性差(但是做底层的大部分还是追求效率)
length(查询字符串长度)
trim(去首尾空格)
trim用的是双指针法,遍历出首尾的ascii小于等于空格的(空格是32 ,小于32的都是人为不识别的字符),返回substring
replace(替换字符串中的子串)
循环找到是否存在需要替换,不存在直接返回当前字符串,存在则new char[],然后遍历替换,new String(char[])返回
split(根据输入值分割字符串,返回字符串数组)
indexOf找到分隔符,然后根据索引返回substring,放进ArrayList里,然后toArray强转成String数组
join(字符串数组转成字符串)
stra.join(strb)
底层是new StringBuilder(stra),然后用iterator迭代strb的字符数组,append(底层是System.arraycopy)
底层是循环调用StringBuilder的append
valueOf(返回非String类型的String格式)
各基本类型调用其包装类型的toString(获取对应的字符数组然后调用String的new String(char[]),boolean则是直接三目运算符写死,引用类型调用他的toString方法
toCharArray(返回字符数组)
创建一个新数组,System.arrayCopy,返回
concat(返回String+String)
底层是System.arraycopy
substring(返回子字符串)
new String(value,start,end);
五、常见问题
为什么String要用final修饰
安全、高效
1、不可变类能够缓存结果,传参时不需要考虑值是否被修改,如果是可变类,则有可能要重新拷贝一个值进行传参,在性能上有可能有损失
2、安全,系统加载的时候需要校验一些值,如果校验完了,值被修改,可能会出现系统崩溃等问题
当字符串不可变时,才可以实现字符串常量池,缓存字符串,提高运行效率。
==和equals的区别
==对于基本类型是比较值是否相等,对于引用类型则是对比引用地址是否相等
如果类没有重写equals,则是调用Object的equals,对比的是地址
String、StringBuffer、StringBuilder的区别
1、String是不可变的,字符串拼接的时候事实上是调用了StringBuilder的append。
2、StringBuilder/StringBuffer有append和insert方法,由于StringBuffer用了Synchronized,效率低,于是在jdk1.5出现了StringBuilder
String中的intern的含义
如果常量池中没有,则新建一个到常量池,然后返回对象指向常量池中。(jdk1.7以后改成常量池中存放堆中地址)
如果有,则直接返回常量池中对象地址。
String类型在jvm中如何存储,编译器对String做了哪些优化
String str1 = "java";
String str2 = new String("java");
在jvm中存储的方式截然不同jdk1.8中,str1会先访问常量池,如果有则返回,如果没有就在常量池新建并返回
而str2是直接在堆上创建一个变量,只有调用intern,才会存储在常量池中
String str1 = "ja" + "va";
String str2 = "java";
从反编译可以看到,str1被编译成了字符串"java",因此str1 == str2 是true;