String的定义
1 | public final class String |
由上面String的定义可知:
- String被
final修饰,所以不能被继承 - String继承
Comparable接口,可以使用Arrays.sort就行排序
String的属性
1 | /**用来储存字符串,可见String的底层是数组实现的 */ |
String的构造方法
1 | /** |
String方法源码分析
length()
1 | public int length() { |
isEmpty()
1 | public boolean isEmpty() { |
charAt(int index)
1 | public char charAt(int index) { |
codePointAt类型
1 | public int codePointAt(int index) { |
codePointCount(int beginIndex, int endIndex)
1 | //与length()方法类似 |
length()方法返回的是使用的是UTF-16编码的字符代码单元数量,不一定是实际上我们认为的字符个数。codePointCount()方法返回的是代码点个数,是实际上的字符个数
例如:
String str = “/uD835/uDD6B”,那么机器会识别它是2个代码单元代理的1个代码点”Z“,故而,length的结果是代码单元数量2,而codePointCount()的结果是代码点数量1.
offsetByCodePoints(int index, int codePointOffset)
1 | /** |
getChars
1 | /** |
equals
1 | /** |
regionMatches
1 | /** |
compareTo类函数和CaseInsensitiveComparator静态内部类
1 | /** |
以上的代码可以看出:
以上的最大问题可以能就是为什么要有个静态内部类,为什么实现了compareTo又有compare,移步到下面,有解答
String实现了comparable接口,重写了compareTo方法,可以用于自己写类进行判断排序,也可以使用collections,Arrays工具类的sort进行排序。只有集合或数组中的元素实现了comparable接口,并重写了compareTo才能使用工具类排序。
CASE_INSENSITIVE_ORDER是一个单例,是String提供为外部的比较器,该比较器的作用是忽视大小写进行比较,我们可以通过Collections或Arrays的sort方法将CASE_INSENSITIVE_ORDER比较器作为参数传入,进行排序。
startWith、endWith类函数
1 | /** |
所以我们知道:
endsWith的实现也是startWith(),作用就是判断前后缀
hashCode()函数
1 | /** |
所以我们可以知道:
hashCode的重点就是哈希函数
String的哈希函数就是循环len次,每次循环体为 31 * 每次循环获得的hash + 第i次循环的字符
indexOf、lastIndexOf类函数
1 | /** |
从上可以看出:
只对外提供了int整形,String字符串两种参数的重载方法(虽然是Int型,其实我们就当做是传char也无所谓,因为虚拟机会帮我们解决这个事情的)
substring()函数
1 | /** |
从上面可以看到:
substring函数是一个不完全闭包的区间,是[beginIndex,end),不包括end位置
subString的原理是通过String的构造函数实现的
concat()函数
1 | /** |
从replace的算法中,我们可以发现,它不是从头开始遍历替换的,而是首先找到第一个要替换的字符,从要替换的字符开始遍历,发现一个替换一个。但是我暂时没有弄清除这样子的好处是什么,节省时间?应该是吧
四种用法,字符全替换字符,表达式全体换字符,表达式只替换第一个字符,字符串替换字符串
matches()和contains()函数
1 | /** |
split()函数
1 | public String[] split(String regex, int limit) { |
join()函数
1 | /** |
Java 1.8加入的新功能,有点跟split对立的意思,是个静态方法
有两个重载方法,一个是直接传字符串数组,另个是传集合。传集合的方式是一个好功能,很方遍将集合的字符串元素拼接成一个字符串。(分割符为 “” ,well, It’s great!!)
trim()函数
1 | /** |
常见去首尾的空值,实际是去除首尾凡是小于32的字符
toString()函数
1 | //emmmmm,这个就不说了吧,就是返回自己 |
toCharArray()函数
1 | /** |
toLowerCase()、toUpperCase()函数
1 | //en,好长,下次再更新吧,先用着吧 |
format()函数
1 | //JAVA字符串格式化 |
从上看:
copyValueOf和valueOf在Java8看来已经是完全没有区别的函数
所有的value的本质都是新new一个String对象
intern()函数
1 | public native String intern(); |
String类中唯一的一条本地方法,既不是用Java语言实现的方法。
比如str.intern(),作用就是去字符串常量池中寻找str字符串,如果有则返回str在常量池中的引用,如果没有则在常量池中创建str对象
相关问题
String类和CharSequencee接口的关系
CharSequence是一个接口,它只包括length(), charAt(int index), subSequence(int start, int end)这几个API接口。继承了CharSequence代表着这是一个有序的字符集合。
String类为何为不可变类型?
从String的定义,我们可以知道,String类是被final修饰的,是一个不可被继承的类
同时我们从字段属性的private final char value[]可以看出,String的本质char数组也是一个不可变的数组对象,且是私有的,不对外公开。
另外String类也没有提供对value进行修改的方法,所以外部也没有途径对String对象的值进行修改
从其他的方法中,我们也可以看到,如果是对字符串进行修改,其本质是new了一个新的String的对象
注意:
String对象是真的无法修改吗?
答案是否定的,还是有途径修改的,那就是通过反射。
因为final的是字符数组,只代表这个value变量的引用不能改变,不代表value指向的对象不可以改变,String只是没有提供修改的途径,即使有也会是不对外公开。但数组中的元素是可以修改的,虽然没有途径,但还是反射就是一个逆天的存在,我们可以通过反射的途径去修改字符数组value的元素的值。
所以String的不可变性仅仅是正常情况下的不可变,但绝非完全的不可变。
String类length与codePointCount的区别
对于普通字符串,这两种方法得到的值是一样的,但对于UniCode编码来说,还是有一点区别。
区别:
length()方法返回的是使用的是UTF-16编码的字符代码单元数量,不一定是实际上我们认为的字符个数。codePointCount()方法返回的是代码点个数,是实际上的字符个数。
例如:
String str = “/uD835/uDD6B”,那么机器会识别它是2个代码单元代理的1个代码点”Z“,故而,length的结果是代码单元数量2,而codePointCount()的结果是代码点数量1.
为什么String已经有一个compareTo方法了,还需要一个静态内部类再实现compare?
这里有一个疑惑,在String中已经有了一个compareTo的方法,为什么还要有一个CaseInsensitiveComparator的内部静态类呢?网上的回答大多数都是说是为了代码复用,我觉得这是有道理的。
首先是一个代码复用的问题,因为String类的compareToIgnoreCase方法实际调用的也是静态内部的compare方法
其次这里采用了饿汉单例模式
个人想法:
我个人觉得,为什么要有个静态内部类呢,首先我们要区别comparable和comparator接口的区别。实现了comparable接口的类,我们就能够本类的内部重写compareTo方法实现大小比较,从而给Collections.sort或Arrays.sort进行排序。而如果一个类没有实现comparable接口也想该类可以排序怎么办,那么就可以创造一个外部比较器去实现comparator接口,重写compare方法。以达到可以给Collections和Arrays使用。
具体区别参考如下链接:
Comparator和Comparable接口的区别
因为String继承了comparable
为什么在忽略大小写比较的时候,通常都会大小写都会比较一次?
也就是说,通常会同位置的字符会全部转为大写比较一次,又转为小写比较一次?这是为什么,不是转换为大写或小写比较一次既可吗?
同时比较了UpperCase和LowerCase,是为了兼容Georgian字符。
https://www.cnblogs.com/yanyichao/p/4493039.html
String类的CopyValueOf 和ValueOf有什么不同吗?
目前Java8的源码来看,没有任何区别。
这里引用百度知道@作者:渊之蓝的一条答案:
我觉得你最大的疑问应该在char数组上吧?比如
char[] test = new char[]{‘a’,‘b’,‘c’};
String a = String.valueOf(test);
String b = String.copyValueOf(test);
有什么区别?答案:没有任何区别!
首先你得知道,String的底层是由char[]实现的:通过一个char[]类型的value属性!早期的String构造器的实现呢,不会拷贝数组的,直接将参数的char[]数组作为String的value属性。然后
test[0] = ‘A’;
将导致字符串的变化。为了避免这个问题,提供了copyValueOf方法,每次都拷贝成新的字符数组来构造新的String对象。但是现在的String对象,在构造器中就通过拷贝新数组实现了,所以这两个方面在本质上已经没区别了。
作者:SnailMann
来源:CSDN
原文:https://blog.csdn.net/SnailMann/article/details/80882719
版权声明:本文为博主原创文章,转载请附上博文链接!