Java基础-String
平常基本类型+String是在代码中使用最多的地方。分析下String特别的地方。
字符串替换
String的字符串替换有3个方法:replace、replaceFirst、replaceAll
replace
1 | public String replace(char oldChar, char newChar) |
String.replace有两个重载方法,一个参数是char、一个是CharSequence
第一个replace是通过原有String中char数组生成一个新数组,然后把新数组中的oldChar替换为newChar,返回一个新的String
- 注:如果oldChar==newChar返回的是this。
第二个relace参数是CharSequence,String是其实现类,所以平常一般传的参数是String(不过StringBuffer、StringBuilder也是该接口实现类)。该replace实现虽然是通过Pattern,但是在使用Pattern的时候,设置了Pattern.LITERAL和Matcher.quoteReplacement
,所以转义是不起效的。
所以对于replace而言,就是 替换所有 符合的字符,无转义,无正则表达式(设置Pattern转义失效)。
replaceAll
1 | public String replaceAll(String regex, String replacement) { |
replaceAll就是纯粹的通过正则表达式替换
replaceFirst
1 | public String replaceFirst(String regex, String replacement) { |
再使用replace或者replaceAll的时候,是替换所有匹配的字符串,如果不需要匹配全部,可以通过replaceFirst尝试值替换第一个。不过replaceFirst的匹配模式也是通过正则表达式。
1 | //示例 |
参考:java字符串的替换replace、replaceAll、replaceFirst的区别详解
String “+”
在Java中是不支持运算符重载的,但是在平常使用过程中拼接字符串时,我们都是通过+
来对字符串做相关操作,这是一种错觉。
1 | //例子 |
在上述代码中使用的+
拼接的字符串,之后通过javac
编译代码生成class文件,通过命令javap -c Main.class
对class进行反汇编。得到如下:
1 | Compiled from "Main.java" |
上述返回编译后可以看到编译成Class后实际是通过StringBuilder来进行拼接的字符串。
如果代码修改为如下:
1 | public class Main{ |
在for循环中拼接字符串,通过反编译可以得到:
1 | Compiled from "Main.java" |
可以看Code标记33,goto5,这里表示for循环,在Code标记11地方会创建一个StringBuilder,也就是每次for循环都会创建一个StringBuilder对象,比较浪费,可以通过在for循环外卖创建StringBuilder后在for循环内通过append拼接字符串。
参考:Java 深究字符串String类(1)之运算符”+”重载
String不可变
在String中通过char[]表达一个字符串,char数组被final修饰,这也表示了单String被初始化后char数组就不能在被修改。而且String类也是final修饰,表示该类不可被继承。
正常情况下不可以修改,不过因为final修饰的是数组,数组引用不可修改,但是数组的值却可以修改,可以通过反射修改char数组中的值。
1 | public static void main(String[] args) throws Exception { |
通过反射修改String内char数组的值,最终输出的字符串一样,但是其hashCode不一样。
把String设计为不可变有几大优点:
1、在Java内存中存在一块常量池,String a = "whh";new String("whh").intern()
这种创建的对象会存入常量池中,当再次创建一个相同的对象时,会直接指向常量池中的地址,因为String在使用过程中非常频繁,可以通过常量池复用相同的字符串。
2、在String创建完毕后,该字符串的hash在创建时就已经计算完毕,方便直接使用,特别是Set中特别有用。如果String可变,在使用Set时,如果先存入"aaa", "bbb"
,之后修改"aaa"
对象为"bbb"
,因为String可变,所以在Set中现在是"bbb", "bbb"
,出现了重复数据。
3、并发安全,因为String不可变,所有可以在多线程中共享。
注:String.intern是如果常量池存在着返回常量池中的对象,如果常量池不存在则把当前对象放入常量池(在常量池判断存在时类似equals比较)。
在平常使用String a="whh"
时,创建的String存在与常量池中,如果使用new String("whh")
,因为使用了new
关键字,所以会告诉JVM在堆中开辟一块内存,用于存放创建的对象,所以在使用==
时比较的事内存地址,一个在堆中一个在常量池中所以不会相等。