Java String 扫盲

1.相关概念

1.开篇

今天整理代码的时候突然看到String 和StringBuilder引发了一些思考,我们都知道二者都是对字符串的管理, 并且二者都提供很多对字符串增删改查等等的功能。但是重来都不建议当改动大(复杂)的字符串用String 来处理。这个是为什么呢。查找了一些资料和翻看了源码,整理了下,如果有哪里说错的,望大神出来指点

2.什么是字符串

字符串是什么,通俗易懂的来说就是由多个字符(0-9,a-z,A-Z,符号)组成的一系列内容。Java是通过char的数组进行管理这一串字符的。

2.String

1.定义:

TheStringclass represents character strings. All string literals in Java programs, such as"abc", are implemented as instances of this class.Strings are constant; their values cannot be changed after they are created.
StringApi

直接翻看Java api可以找到String类的所有信息,String是个类就是复合类型(不是标准类型),但是String 这个类是个常量类,只要创建了 String,他的值是没有办法改变的(所以一担对用String来操作字符串会里面开辟一个新的字符串出来)。而这里的常量不是指的整个String 而是说的String的字面量。

String a = "abc"            //这里的"abc"会被放到.class 文件中的常量池中
String b=new String("abc")  //String的另外一种情况,b指向的并不是常量池,是堆

2.常量

1.常量的基本概念

说了这么多常量,常量到底是什么呢?就是简单通过字面的解释,创建了不能改变也不会改变的量。由于是主要讲String所以基本常量的解释就不多说了,找了一篇挺好的文章,大家可以看下
介绍了java中的常量池

2.String 常量

由于String 有2种申明的方法,这2种方法表面上看上去一样,其实背后所做的操作都不一样,所以有必要拿出来记录下(来咯大量的例子来咯):

 1 String s1 = "Hello";
 2 String s2 = "Hello";
 3 String s3 = "Hel" + "lo";
 4 String s4 = "Hel" + new String("lo");
 5 String s5 = new String("Hello");
 6 String s6 = s5.intern();
 7 String s7 = "H";
 8 String s8 = "ello";
 9 String s9 = s7 + s8;
10           
11 System.out.println(s1 == s2);  // true
12 System.out.println(s1 == s3);  // true
13 System.out.println(s1 == s4);  // false
14 System.out.println(s1 == s9);  // false
15 System.out.println(s4 == s5);  // false
16 System.out.println(s1 == s6);  // true
刚入门的小伙伴看到这些肯定头晕了,不急慢慢一条解释是为什么:
  1. s1==s2 十分好理解,由于==是判断地址(不是判断值,判断地址地址地址重要的事情说三遍), 当编译的时候,系统自动在常量池里面存入了"Hello"这个值,由于常量池有复用的功能,自然就把这个常量的地址给了s2这个引用。
  2. s1==s3,这个其实和11 差不多注意一点系统老聪明了,常量池会自动优化拼接,拼接完发现一样就把原来的常量地址直接给了s3所以返回true
  3. s1==s4,s4有一部分属于常量池有一部分编译的时候系统根本不知道是啥,所以只能等到运行的时候把常量池里面的取出来然后带着新的字符串在堆种开辟了一块新的空间存放,千万不要问我存在堆的哪里,因为我也不知道阿!!鬼知道存哪里了阿但是肯定是新的一块。没毛病的~~
  4. s1==s9, 这个很有意思阿,为什么会不同呢,因为系统在编译的时他只知道s7 s8是个变量,他压根不知道里面有啥,他赋完地址就忘了阿!!只好等到运行的时候到s7 s8里面取值然后在堆种开辟新的空间。
  5. s4 == s5 不多讲.
    6.s1 == s6,肯定有刚入门的人问s5.intern(); 这个是啥,这个就是把堆中的值放到常量池里面,同理常量池里面有复用的功能放进去的时候发现一样就直接把原来的地址拿出来~~所以还是一样的

还有一些情况什么“+”号拼接啦,上面推荐的文章都有,不多说了
上面的例子,图片就不画了,因为小编好懒的阿不愿意画图

2.String 源码分析

说了String在对字符串进行修改的时候会创建一个新的String,由于好奇背后怎么实现的,小编就随便着了一个例子:

        String a = new String("abca");
        String b=a.replace("a","e");
        System.out.println(a);     //abca
        System.out.println(b);    //ebce

对~就是随便拿了一个a.replace()来做分析,不知道这个method是干嘛的小伙伴自行去api网站看~
千万别问我 a的值为什么不改变,因为告诉你们字面量是常量不会变不会变所以需要一个新的String来保存结果


    public String replace(CharSequence var1, CharSequence var2) {
        return Pattern.compile(var1.toString(), 16).matcher(this).replaceAll(Matcher.quoteReplacement(var2.toString()));
    }

其实这个方法很简单,就辣么一行,但是这一行把他详细看还是可以看到很多东西的,首先java在替换字符串的时候用的是正则表达式
什么是正则表达式咧(sjsu的小伙伴你们46b lab会教)附上链接:
正则表达式

首先创建了一个正则表达式的规则,没有错就是穿进去要修改的字符,然后在创建了一个matcher,matcher(this)是用来存放匹配的结果(参数代表要匹配的字符串,String的话当然就是自己本身去匹配了)
有没有匹配到,有没有找到对应的内容等等 附上链接:
Matcher

这里重点说一下这二个
matcher.find(); //部分匹配,通俗啊点讲就是查找这个字符串里面有没有匹配到内容,然后定位到剩下匹配到的内容
matcher.matches(); //全部匹配,就是把整串东西和规则进行匹配
Matcher.quoteReplacement(var2.toString()) //去除转义字符"\"和"$"

重点看replaceAll的实现:

public String replaceAll(String var1) {
        this.reset();
        boolean var2 = this.find();
        if(!var2) {
            return this.text.toString();
        } else {
            StringBuffer var3 = new StringBuffer();

            do {
                this.appendReplacement(var3, var1);
                var2 = this.find();
            } while(var2);

            this.appendTail(var3);
            return var3.toString();
        }
    }

这一串简单讲一下:
如果find()没有结果的话直接返回text(String的字面量),如果有匹配到
那就说明要替换了,那么这里是重点了,java开辟了一个新的StringBuffer!!(暂时理解为一个新的char[]).然后把一个接一个的把字符赋值上去,然后匹配的地方赋值新的值,就可以看出,String在做替换的操作的时候确实开辟了一个新的空间,而且看这段代码也可以看出为什么替换了2个a 因为他会一直找找找直到最后 懂吧~~

replace我重点看了其它的方法扫了下也差不多用到了正则表达式啦,有兴趣的小伙伴可以看看String其它的实现方法

3.StringBuilder

开篇就讲了StringBuilder也是用来管理字符串,但是他的最大区别就是可以改变里面的值,他不是常量,直接上重要代码:

public final class StringBuilder extends AbstractStringBuilder implements Serializable, CharSequence {
    static final long serialVersionUID = 4383685877147921099L;

    public StringBuilder() {
        super(16);
    }

    public StringBuilder(int var1) {
        super(var1);
    }

    public StringBuilder(String var1) {
        super(var1.length() + 16);
        this.append(var1);
    }

    public StringBuilder(CharSequence var1) {
        this(var1.length() + 16);
        this.append(var1);
    }

StringBuilder继承了AbstractStringBuilder然后引入了系列化和char数列的接口

StringBuilder a= new StringBuilder("abc");

/*对应的构造方法*/ 
public StringBuilder(String var1) {
        super(var1.length() + 16);
        this.append(var1);
    }

我们直接看AbstractStringBuilder的构造方法因为StringBuilder的构造方法也没做什么事阿:

 AbstractStringBuilder(int var1) {
        this.value = new char[var1];
    }

可以看得出直接申明了一个char的数组但是重要的是他的大小是原本的大小+16,这个是为什么呢,因为说过Stringbuilder是可以改变原来的值所以可以在char[]里面添加更多的东西.当StringBuilder 对象的Length(字符串的长度)属性值超过Capacity属性的长度时,StringBuilder 对象内部会重新构造一个字符数组Capacity属性会变为新的大小

返回StringBuilder里面:

/*对应的构造方法*/ 
public StringBuilder(String var1) {
        super(var1.length() + 16);
        this.append(var1);
    }
==>
    public StringBuilder append(String var1) {
        super.append(var1);
        return this;
    }
==>
public AbstractStringBuilder append(String var1) {
        if(var1 == null) {
            return this.appendNull();
        } else {
            int var2 = var1.length();
            this.ensureCapacityInternal(this.count + var2);
            var1.getChars(0, var2, this.value, this.count);
            this.count += var2;
            return this;
        }
    }

append是往char[]数组里面加东西,分析一下,首先看下有没有值过来没有直接返回,然后如果有值,获取长度,然后对长度进行判断
有没有超过容量

 private void ensureCapacityInternal(int var1) {
        if(var1 - this.value.length > 0) {
            this.value = Arrays.copyOf(this.value, this.newCapacity(var1));
        }

    }

就像前面说的超过了,会有一个新的最大空间,看一下value是啥

char[] value;

然后就是往这个数组里面放内容了,把count(字符串的大小)给改变了
把Stringbuilder的append的方法分析了,其它方法可以自己去研究下都不难很容易懂的

结尾

第一次写技术整理,如果有写错的地方望大家指出我可以尽早改掉以免误人子弟~哈哈 我觉得我没说错啦~就是给一些入门的小伙伴扫扫盲 刚好今天整理到这些了 有兴趣的翻了下源码啦

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

推荐阅读更多精彩内容