java源码学习-String

String 源码学习

1. String

1.1不变性

不可变指的是类值一旦被初始化,就不能被修改,如果被修改那将会是新的类。

        String s  = "hello";
        s = "world";

从代码上看,s的值好像被修改了但是debug来看


看地址为820

地址变为822

s的内存地址已经被修改了,也就是说 s= "world",看似简单的赋值,其实已经将s的引用指向了新的String。
补充(使用idea dubug模式,@XXX;就是变量的相对内存地址)

查看源码


string源码

通过源码可以看出

  1. string类被final修饰,说明string类不能够被继承,也就是说对任何string的操作方法,都不会被继承覆盖。
  2. string保存数据是byte[]。我们看到value也是被final修饰的,也就是说value一旦被赋值,那内存地址就无法被修改,而且value的权限是private的,外部访问不到,string也没有开放出对value进行赋值的方法,所以说value一旦产生,内存地址根本无法被修改。
    注:(在jdk8中string实现是char[] ,而在jdk9中变更成byte[] ,使用byte数组可以减少一半的内存,byte使用一个字节来存储一个char字符,char使用两个字节来存储一个char字符。只有当一个char字符大小超过0xFF时,才会将byte数组变为原来的两倍,用两个字节存储一个char字符。)

以上两点是string不变性原因,充分利用了final关键字的特性,如果你定义类时也希望是不变的,可以参考string这两点操作。

因为string的不变性,所以string的大多数操作方法,都会返回新的string,下面需要注意

        String str ="hello world";
        // 这种写法是替换不掉的,必须接受 replace 方法返回的参数才行,这样才行:str = str.replace("h","xx");
        str.replace("h","xx");
        System.out.println(str);
        str = str.replace("d", "qq");
        System.out.println(str);

1.2 字符串乱码

在平时进行二进制转化操作时,本地测试没问题,但是运行到其他机器上时,会出现字符串乱码情况,主要原因是在进行二进制转化操作时,并没有强制规定文件编码,而不同的环境默认的文件编码不一致导致。

模拟字符串乱码

输出 : nihao ?? ??

打印的结果为??,这就是常见的乱码表现形式。这时候有人说,是不是我把代码修改成 String s2 = new String(bytes,"ISO-8859-1"); 就可以了?这是不行的。主要是因为 ISO-8859-1 这种编码对中文的支持有限,导致中文会显示乱码。唯一的解决办法,就是在所有需要用到编码的地方,都统一使用 UTF-8,对于 String 来说,getBytes 和 new String 两个方法都会使用到编码,我们把这两处的编码替换成 UTF-8 后,打印出的结果就正常了。

1.3首字母大小写

如果项目被spring托管,有时候我们会通过 application.getBean(className); 这种方式得到SpringBean,这时className必须满足首字母小写。除了该场景,在反射场景下,我们也经常要使类属性的首字母小写。

str.substring(0,1).toLowerCase()+ str.substring(1);

使用 substring 方法,该方法主要是为了截取字符串连续的一部分,substring 有两个方法:

/*beginIndex :开始位置,结束位置为文本末尾 */ 
public java.lang.String substring(int beginIndex)  

/*beginIndex :开始位置,endIndex 结束位置   */ 
public java.lang.String substring(int beginIndex, int endIndex) 

substring 方法的底层使用的是字符数组范围截取的方法 :

Arrays.copyOfRange(字符数组, 开始位置, 结束位置); 

从字符数组中进行一段范围的拷贝。

1.4相等判断

我们判断相等有两种办法,equals 和 equalsIgnoreCase。后者判断相等时,会忽略大小写。

一些面试题在问:如果让你写判断两个 String 相等的逻辑,应该如何写,我们来一起看下 equals 的源码(之前的源码),整理一下思路:

public boolean equals(Object anObject) {
    // 判断内存地址是否相同
    if (this == anObject) {
        return true;
    }
    // 待比较的对象是否是 String,如果不是 String,直接返回不相等
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        // 两个字符串的长度是否相等,不等则直接返回不相等
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            // 依次比较每个字符是否相等,若有一个不等,直接返回不相等
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

目前源码
String重写Object的equals方法,先用“==”判断地址,地址相同则直接返回true;然后再比较类型,类型不同则直接返回false;最后才比较内容。
用于比较两字符串对象是否相等,如果引用相同则返回 true。否则判断比较对象是否为 String 类的实例,是的话转成 String 类型,接着比较编码是否相同,分别以 LATIN1 编码和 UTF16 编码进行比较。
代码如下:

public boolean equals(Object anObject) {
        //判断地址是否相等
        if (this == anObject) {
            return true;
        } else {
            //比较类型
            if (anObject instanceof String) {
                //比较内容
                //转成 String 类型,接着比较编码是否相同,
                //分别以 LATIN1 编 码和 UTF16 编码进行比较
                String aString = (String)anObject;
                if (this.coder() == aString.coder()) {
                    return this.isLatin1() ? StringLatin1.equals(this.value, aString.value) : StringUTF16.equals(this.value, aString.value);
                }
            }

            return false;
        }
    }

由于equals是Objec的方法,意味着任意引用类型对象都可以调用,而且,入参是Object类型,所以,不同类型是可以用equals()方法的,不会像“==”一样编译异常。
注:会遇到的一个小坑,例如:char chr = ‘a’,String str = “a”,我经常会写成str.equals(chr),而且还傻傻的等着返回true,上面说到过,两个不同类型的变量比较,equals()会直接返回false。str.equals(chr+"")倒是可以解决。

1.5替换、删除

有 replace 替换所有字符、replaceAll 批量替换字符串、replaceFirst 替换遇到的第一个字符串三种场景。


替换

replace 有两个方法,一个入参是 char,一个入参是 String,
前者表示替换所有字符,如:str.replace('a','b')
后者表示替换所有字符串,如:str.replace("a","b")两者就是单引号和多引号的区别。

需要注意的是, replace 并不只是替换一个,是替换所有匹配到的字符或字符串哦。

如果是删除 某些字符,也可以使用 replace 方法,把想删除的字符替换成 “” 即可

1.6拆分,合并

拆分我们使用 split 方法,该方法有两个入参数。第一个参数是我们拆分的标准字符,第二个参数是一个 int 值,叫 limit,来限制我们需要拆分成几个元素。如果 limit 比实际能拆分的个数小,按照 limit 的个数进行拆分。


分割

测试数据
输出

从演示的结果来看,limit 对拆分的结果,是具有限制作用的,还有就是拆分结果里面不会出现被拆分的字段。

那如果字符串里面有一些空值呢,空值是拆分不掉的,仍然成为结果数组的一员,如果我们想删除空值,只能自己拿到结果后再做操作。
但 Guava(Google 开源的技术工具) 提供了一些可靠的工具类,可以帮助我们快速去掉空值,如下:

List<String> on = Splitter.on(":")
                .trimResults() //去掉空值
                .omitEmptyStrings() //去掉空格
                .splitToList(s);

输出:

[bbb, aaa, oo, o, asd]

合并我们使用 join 方法,此方法是静态的,我们可以直接使用。方法有两个入参,参数一是合并的分隔符,参数二是合并的数据源,数据源支持数组和 List,在使用的时候,我们发现有两个不太方便的地方:

  1. 不支持依次 join 多个字符串,比如我们想依次 join 字符串 s 和 s1,如果你这么写的话 String.join(",",s).join(",",s1) 最后得到的是 s1 的值,第一次 join 的值被第二次 join 覆盖了;
  2. 如果 join 的是一个 List,无法自动过滤掉 null 值。
// 依次 join 多个字符串,Joiner 是 Guava 提供的 API
Joiner joiner = Joiner.on(",").skipNulls();
String result = joiner.join("hello",null,"china");

List<String> list = Lists.newArrayList(new String[]{"hello","china",null});
// 输出的结果为;
依次 join 多个字符串:hello,china
自动删除 list 中空值:hello,china
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容