栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java常用类:String

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java常用类:String

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

1、不可变性

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

public final class String
    implements java.io.Serializable, Comparable, CharSequence {
    
    private final char value[];

    
    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源码(凑一下字数):

@NotNull
public native String intern();

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

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

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

String str = "123";

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

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

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/643909.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号