Java常用类:String

说到Java中最常用的类,那么一定是非String类莫属了。刚学Java写hello world就用到了。虽然看起来简单但是还是有很多的细节需要注意。正因为常用我们才需要更加关注它的细节,这样我们才能用好。

1、不可变性

学过一段时间Java后一定听过String的这个特性:不可变性。为啥不可变呢?先来点源码(jdk1.8)看看:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

看到这个源码我们首先要注意的有2点,这两点其实也说明了String不可变的原因:
1、String类是final类型的,也就是说String类无法被继承,final修饰的类其实他的方法也是final类型的,只不过没有显式的声明,也就是无法通过继承来覆写String的方法。
2、String中用于存储字符串的value值其实是char类型的数组,或者说它底层的数据结构其实是char数组;并且这个数组是私有的(private)同样被final修饰。这就说明这个值是对外不可见的,并且一旦初始化内存地址就不能改变了,注意这里只是说内存地址不可变,但是String类想要修改value这个数组内部的值是可以的,比如value是{‘1’,‘2’},那么完全可以修改value为{‘0’,‘2’},但是String类并没有提供这种方法,为的就是保持String不可变。其他的源码没有贴,其实String用来修改字符串的方法,其实都是返回了一个新的字符串,并不是修改value的值。

2、replace、replaceAll和replaceFirst

replace:替换字符串中所有的字符或字符串
replaceAll:和replace一样,但是支持正则表达式
replaceFirst:替换字符串中第一次出现的内容,支持正则表达式
Java中替换空字符串也需要注意一下:

//首尾空格替换
String str = " x xxx x  ";
System.out.println(str.trim());
// 所有空格替换
String str = " x xxx x  ";
System.out.println(str.replaceAll(" ",""));
// 所有空白字符替换
String str = " x x	xx x  ";
System.out.println(str.replaceAll("\\s*",""));

3、split函数

split使用的时候也要注意一下,split函数结尾空串会被舍弃。这里给出几行代码思考一下运行看看:

String o = "oooooooo";
System.out.println(Arrays.asList(o.split("o")));
String o1 = "qoooooooo";
System.out.println(Arrays.asList(o1.split("o")));
String o2 = "ooooqoooo";
System.out.println(Arrays.asList(o2.split("o")));
String o3 = "qooooooooq";
System.out.println(Arrays.asList(o3.split("o")));
//如果需要保留结尾空字符串可以设置split函数第二个参数为-1
String o1 = "qoooooooo";
System.out.println(Arrays.asList(o1.split("o"-1)));

这里还有一点需要关注的是split函数是根据正则去匹配的,这种方式在处理比较大并且复杂的字符串时有可能会因为贪婪匹配导致回溯,导致cpu过高。所以在使用split函数处理非常复杂的字符串时一定要多加小心,可以采用其他的方式来代替的就用其他方式代替。

3、使用String时的+=问题

在使用String时尽量避免再循环中使用+=的操作进行拼接字符串,而是使用StringBuilder或者StringBuffer中的append方法代替。
为什么呢?因为我们都知道String对象是不可变的,在循环中去使用+=去拼接字符串会产生大量的字符串对象,而这些字符串对象很可能是没有意义的(就是只会在你这个循环中用到,是一种中间的状态,创建大量的字符串无疑是一种极大的资源开销,好钢用在刀刃上),而StringBuilder和StringBuffer中的append方法来拼接字符串的话就不会有这种问题,归根结底是因为在这两个类底层中是通过修改char[]的方式来达到修改字符串目的的,最后再将修改好的char数组转换成字符串,这样的好处就是我只是对char数组这一个对象动了手脚,并不会创建那么多中间状态的对象从而节省了系统资源开销,提升了运行速率。stringbuilder和stringbuffer的区别就是stringbuilder不是线程安全的,而stringbuffer是线程安全的,使用场景不同而已。看源码stringbuffer中的append方法是加了synchronized关键字的。

4、String的intern()方法

intern()方法是String类中的本地(native)方法。看下jdk1.8源码(凑一下字数):

/*Returns a canonical representation for the string object.
A pool of strings, initially empty, is maintained privately by the class String.
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
当调用intern方法时,如果字符串常量池中已经包含一个用equals方法判定相等的字符串(简单说就是你要创建的字符串常量池中已存在),那么就返回常量池中的字符串;否则就把这个字符串加入常量池,并返回这个字符串对象的引用。
It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.
All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java™ Language Specification.
Returns:
a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.*/
@NotNull
public native String intern();

其实jdk中的注释已经很好的解释了intern方法的使用方法,关键的部分我也做了翻译(4级水平),只是这里涉及到了一个知识点就是字符串常量池:

因为我们已经知道String对象的不可变性,这也是Java中构建字符串常量池的基础,因为常量就是不可变的量,Java中引入字符串常量池主要目的就是节省创建对象和内存的系统开销用以节约资源(不需要为相同的字符串开辟更多的内存空间,举个栗子:就是你饿了想吃面包,有俩选择一个就是去超市直接买做好的,一个就是去面包店要求现烤。这时候常量池就像是超市),如果同一个字符串对象一会儿变成面包,一会儿又变成了矿泉水,那么这个常量池将变得没有意义。

其实字符串常量池我们几乎每天都在使用,只是可能你不会意识到而已,举个最简单不过的栗子:

String str = "123";

就上面这个不起眼的代码其实就已经在使用字符串常量池了,Java对String = xxx这个操作做了优化,就拿上面的这个栗子来说,我写了上述代码,要把123这个字符串赋值给str,1、Java会先创建一个名为str的引用,2、然后从字符串常量池中查找如果已经存在123,那么直接把内存地址返回给str;3、如果没找到,那就在字符串常量池中创建123这个对象,然后再把内存地址返回给str。看着是不是和intern方法很像?其实这就是String str = new String("123").intern();的简化写法。

还有一点需要注意的是jdk1.7及之前字符串常量池是在方法区(永久代)的,jdk1.8取消了方法区用元空间代替了,这时候字符串常量池就被放在了堆中并不是一起转移到元空间哦。

其实Java用常量池节省资源开销的做法不光体现在String中,其它地方也用到了常量池,比如Long会缓存-128-127这256个数字。这也启发了我们平时在开发中如果遇到类似的问题可以用这种方式来进行优化或者提供解决思路。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java常用String是一个用于表示字符串的String是不可变的,意味着一旦创建了一个String对象,它的值就不能改变。String提供了许多方法来处理字符串,包括字符串的拼接、截取、替换、查找等操作。 常用String方法包括: 1. 字符串拼接:可以使用"+"操作符或者String的concat()方法将两个字符串连接起来。例如,str1 + str2 或者 str1.concat(str2)。 2. 字符串长度:使用String的length()方法可以获取字符串的长度。例如,str.length()。 3. 字符串查找:使用String的indexOf()方法可以在字符串中查找指定字符或子字符串的位置。例如,str.indexOf('a') 或者 str.indexOf("abc")。 4. 字符串截取:使用String的substring()方法可以从一个字符串中截取指定位置的子字符串。例如,str.substring(3) 或者 str.substring(0, 5)。 5. 字符串转换:String提供了许多方法来进行字符串与其他数据型之间的转换。例如,将一个字符串转换为整数可以使用Integer的parseInt()方法,将一个整数转换为字符串可以使用String的valueOf()方法。 6. 字符串比较:String提供了equals()方法用于比较两个字符串是否相等。例如,str1.equals(str2)。 7. 字符串替换:使用String的replace()方法可以将字符串中的指定字符或子字符串替换为新的字符或字符串。例如,str.replace('a', 'b') 或者 str.replace("abc", "def")。 8. 字符串大小写转换:使用String的toLowerCase()和toUpperCase()方法可以将字符串转换为全小写或全大写形式。例如,str.toLowerCase() 或者 str.toUpperCase()。 这些是String的一些常用方法,可以满足大多数字符串处理的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值