目录
- 一、String的创建
- 二、了解字符串类型
- 三、字符串比较相等(==,equals)
- 四、字符串内容比较大小
- 五、字符串查找
- 六、字符串替换
- 七、字符串拆分
- 八、字符串截取
- 九、String类中其它的常用方法
- 十、StringBuffer 和 StringBuilder
一、String的创建
创建字符串的方式有三种:
我们对第一和第二种创建字符串的方法都已经非常熟悉了,那至于为什么第三种能够传入一个字符数组变为字符串,我们可以按住ctrl键点入传入字符数组的String当中看其原码,我们能够发现此时是利用的方法是数组的拷贝,将字符数组的所有字符改为字符串形式。
二、了解字符串类型
按住Ctrl,点击String,进入String
根据上图,我们发现对于
字符串
来说,有两个属性,一个是char 类型的 value数组
(此时这个数组,只是一个变量【引用类型】,没有给这个数组,分配内存。也没有new)。一个是 哈希码(hash)
。
问题:字符串str,重新赋值会不会影响 str2的输出结果?
我们需要搞懂:字符串常量是不能被改变的
例如: String str = “abcd”; 通过引用 str 去将 字符串"abcd" 修改成 “gbcd”.
答案是做不到的,因为被双引号引起来的是字面值常量,常量是不能被修改的。
例题中,str = “author”; 这句代码是将str重新指向一个新的对象(修改str的指向),而不是将原来的字符串对象修改成author。
接下来再看一道例题:
结论:
不是说 转引用 就能改变实参的值。
你要看,到底这个引用干了什么!
三、字符串比较相等(==,equals)
==比较的是对象的身份(比较两个引用中保存的地址是否相同/比较两个引用是否指向同一个对象)
而String的equals方法比较的是两个字符串的内容。但此时又有个疑问:为什么每个定义字符串常量的是一个引用呢?这样就牵扯到了字符串常量池。
字符串常量池
对于“池”这个概念,可能大家还是比较陌生的。比如数据连接池、线程池等等。那这些池的作用的干嘛的呢?是用来提高存储效率的。顾名思义字符串常量池是用来存储字符串常量的。字符串常量池中规定只要有了一个字符串常量就不再存储相同的字符串了。从JDK1.8开始字符串常量池是在堆里的。它本质上是一个哈希表(StringTable)是一个数组。存储字符串常量是,会根据一个映射关系进行存储,这个映射关系需要设计一个哈希函数。(因为字符串常量池是有关于JVM的,需要看其原码才能真正了解字符串常量池是如何操作的,此处不深究其原理也不会影响我们判断引用是否相同)。
字符串常量池中当存储一个字符串常量时会在根据哈希函数计算的某一个位置处产生一个结点,结点是由哈希值、String结点的地址、存储该数组位置处的下一个结点的地址组成的(这在JVM的原码中才能真正了解)。而每一个String结点是由字符型数组value与哈希值hash(默认为0)构成的 (下图所示)。点入String看其原码时就能够会发现这两个变量。此时观察到value数组被final修饰则说明该数组里的字符是不能够被改变的,这就是字符串是一个常量的原因,并且该字符串会转换为字符形式存放在字符数组当中。
1.举例一
结果:
画图解释:
2.举例二
内存布局如下:
3.举例三
内存布局如下:
4.举例四:
手动入池:
我们根据上图知道了代码二中的运行结果是false的,因为str2指向的是new String产生的String对象,而不是存储“hello”的String对象的地址。如果写为下面这个代码,结果会是如何呢?
为什么最后的结果为true呢?
此时调用了String类当中的intern方法,称为手动入池,它能够将str2的指向不再指向new出来的String对象,而是指向了字符串常量池当中已经存储有“hello”字符数组的String对象。
5.举例五
此代码有关字符串的拼接。其实“he”与“llo”在编译时期就已经编译为“hello”了。如果要看编译时期str2是什么字符串,则此时我们先点击Build选项,点入Build Project选项则进行编译(图1)。可以在该类文件的路径(含有.class文件)底下(图2+图3),按住shift键加右键点击powershell窗口,输入反编译指令javap -c
类名则能看到编译时str2是否是已经拼接好的hello。
6.举例六
字符串的拼接会产生一个StringBuffer的类型,通过StringBuffer调用toString方法也转变为String类,此时拼接完后字符串“11”存储在value中,但是不会存储到字符串常量池当中。
通过反编译我们看到的确拼接产生StringBuffer,并且StringBuffer调用toString方法产生一个String类的对象存储“11”。由图1、图2可以完全了解。
图1:
图2:
内存图:
equals( )
对于字符串比较,我们不能直接用“==”,而有三种方法能够对字符串有不同的比较方式。
比较字符串内容:直接调用String类的equals方法,将字符串放入括号当中比较。
比较字符串内容(不分字母大小写):调用String类的equalsIgnoreCase方法。
四、字符串内容比较大小
调用String类当中的compareTo方法。本来String类当中是没有compareTo方法,只不过String类实现了Comparable接口,并且重写了compareTo方法。
它是一个字符一个字符进行比较的。如果str1大于str2则返回str1该字符减去str2该字符的值。例如:
代码1:
因为b的ASCII码值比a的ASCII码值大1,则直接返回-1。(如果是字符不相同则返回它们的ASCII码差值)
代码2:
因为在str2比较结束前与str1的字符值是相同的。因此最后的结果是str1的长度减去str2的长度。
下面是String类的compareTo方法的实现
: