00:00
看一下咱今天这几道问题问题,这几道题呢,都是关于这个字符串的,嗯,这个可见这个字符串的一个重要性啊,这个咱们在开发当中,字符串呢也是用的比较多的,所以关于string的使用的话呢,大家相对来讲比较熟一些,那我们来看看这几道问题,第一道说呢,这几行代码在内存当中是怎么呈现的啊,涉及到内存的一个解析,这个需要大家掌握。啊需要大家掌握,嗯,我看大家都在这画啊,这个大部分同学呢,画的都是对的啊,个别呢画的这个细节上稍微差一点,来我们直接我就在这画一个简图了,这个因为咱们前天讲的时候呢,都画的比较详细了,这呢是这个占空间,这个空间方法区,我这叫方法区,这个方法区的话呢,呃,里边呢,我们说提到一个叫字符串常量池,但是这种格局咱们说呢,它实际上呢,不一定都是这样的是吧,咱们不是讲这个GM的这个内存结构的时候呢,提到过说最二常量池呢,在JDK6的时候是放在这个方法区的,JDK7的时候呢,是不是就放在堆里了,八的时候呢,放在叫圆空间里了,哎,六的话你叫方法区,或者叫这个永久带也行,永久区也行,就是它有变化啊,这个呢,它是有变化,所以这块呢,咱们画的一个呢,嗯,算是一个比较这个这个大家通常说的这两个版本啊,你要是画的。
01:24
他说稍微再在这个确定一点的话呢,就占堆这块呢,我就不叫,非得说是方法区里边叫常量池了,我就直接叫常量池啊,那在六当中呢,那就算叫永泳带了,在八当中呢,这就叫一个圆空间啊,七的话呢,那那按说应该放进去了。哎,就是我们就先画成这样的一个格局的线啊,一会儿负极的时候呢,再稍微说一下,那么在这种格局下呢,我们S1S2作为直接呢,通过这种自变量的方式给负的值,那都是在这个常量池当中的S1指向常量池里边,如果呢,最初常量池没有这个hello,我们就自己造一个,造了一个hello,把这个hello的首地址值给了我们这个S1,那接着呢,S2S2的话呢,也是通过这种字面量的方式呢,直接赋的值,那仍然也是在常量池里边去找呃去去赋值啊,那因为呢,我们常量池当中,咱们讲过常量池里边呢,是不可能存在两个内容完全一样的这个字符串的,所以说呢,如果你还是哈呢,大家就共用一个,哎,那把这个已经存在的哈的地址呢,给了S2,它俩呢,地址是一样,指向呢,也是常量池里边的唯一的一个,Hello,行,这个呢,所有同学我看都能画出来。在接下来呢,我。
02:42
我们这个叫S3 S3那仍然是作为我们这个占空间中的一个变量了,然后它通过这个new的方式,通过这个new的方式呢去造对象,那new都得在堆里边,所以这块呢,堆空间需要画出来,这个大家都画出来了,然后堆空间new的这个string呢,有一个首地址值,把这个地址值给了S3 S3呢指向的是堆空间中的这个区域。
03:09
嗯,然后这个hello啊,这个大家有写的就不对了,有同学呢,直接把hello就写这了。那这个哈呢,其实不在这对这呢,咱们是对应它里边内部的有一个Y6的一个一个属性,这个属性呢,是一个叉型的数组,那这个数组的话呢,它又是一个引用类型变量了,它又存的是个地址值,这个地址值呢,其实这个数据不是本身就在这儿的啊,而是呢,在我们这个常量池里边找到这个hello,把这个hello的地址呢,放到我们这个位置上。哎,这样的一个格局,这样的一个格局,注意一下,那么在接下来呢,这个S1呢,加等于一个word s1呢是个变量,咱们讲了说变量如果做这个拼接操作,就是这个呢,这种写法跟这个S1等于S1加上这个word,这种写法和这种写法呢是一样的,所以就是在咱们这个内存结构上来看是一样的啊,因为呢,右边有变量参与了,所以怎么着啊。
04:12
所以新建一个,那这时候有同学写的又不对了,我这换成个蓝笔啊,怎么不对了呢,这个S1呢,它直接就在这个位置这样写,呃,这个常量池呢。长认池在这哈,他又在这去新建了一个叫这个这个hello word,把这个呢,指针就没有了,这样对吗?这个写法就不对了,这个写法不对啊,你要是这样写的话呢,那如果我要是告诉你,我又又声明这个S4,这个S4呢就叫做hello word,那我问你这个S4和这个S1它俩等等吗?你要是你这样画的是不是就成等等了,对,事实上呢,它俩是不是false啊嗯,那这个SSE应该在哪画对,应该在我们这个堆里边,咱们这边是讲那个结论,所是string这块呢,大家看着听着挺嗨的是吧,还得挺都挺清楚挺清楚,真到落地落地让自己去判断写的时候呢,发现错了,哎不能这个眼高手低啊,这个有些呢,需要你理解了,还得亲自呢去操作一下这个SSE呢,我们这是通过变量的方式做这种拼接操作的,我们说这种方式呢,一定要在堆控件中去创建啊,所以说这个呢。
05:27
橙色的这个操作呢,是不对的啊,不能这样写,实际上这个时候呢,你是在这个堆里边。堆里边的话呢,这个这种写法呢,其实你就把它想象成跟我们这个new的写法呢,是一样的就行了,所以在这里边呢,呃,仍然是新建了一个对空间的对象啊,然后呢,把这个S1呢,就指向了你新的这个对象的这个手地日值,然后这呢还是这个Y6,呃,然后这个Y6呢,还指向啊,指向这个常量池里边没有了,没有没有,你是不是就再去重新造一个啊叫hello word。这个样子。
06:00
哎,就是一定要注意这个S1呢,后来这个操作一定是在堆里边啊,去拗了这几项这块呢,不能写错了啊,这个错了相应的这个分就没有了,所以呢,你看下边这个,下边我写这个哪一道题,第三道题哈,第三道题这块顺便咱们就说了,说这个string s等于new一个string hello在内存中创建了几个对象几个呀,两个对象一个呢,就是我们在堆里边new的这个,另外的话呢,就是你对应在常量池这块,常量池里边呢,放在它本质上这个核心的字符串,这个内容没有的话呢,去拗一个,呃,是这样的一种方式。行,这个呢,大家都需要掌握啊,这是我们说的第一道问题,下来呢,自己再画一画,下一个string的不可变性,磁针的不可变性怎么去理解,找同学来说一下。嗯,那这个卢云鹤,嗯,你说说这个的不可变形,你咋理解的。
07:18
嗯,声音稍微大一点。改变,嗯,就是这个改变指的都有哪些操作,算是要试图去改变它呀,啊就是不清楚我刚才问的这问题是吧?嗯嗯。行,嗯,这个事儿说的是对的啊,当然就是呢,也稍微关注一下,到底都有哪些通常的操作,就是刚才说的没问题,说呢就是我们这个string呢,这不指向这个常量池里边这个数据了,说这个数据的话呢,我们其实对它不能做任何的修改,凡是呢,你想对它进行修改呢,必须得新造一个数据啊,去存储你这个新的内容,这呢就提为体现为呢叫不可变性,就对于它来讲是不可变的,嗯,这个修改呢,体现为什么呢?比如你想这个,呃,往那后面呢,再给它拼接一个,这个不行,新造一个,然后呢,我这个内容不想叫它了,我想换一个新的字符串,这个呢也得去新造一个,说呢,我想这个也不往后拼接了,我也不是说想换了,我想把这里边,比如你叫ABC,我想把这个C呢,换成是个M,这也不行,也都得心造,就是凡是对你现有这个字符串,这个操作,任何操作都必须去新造,是这个意思,其实呢,大家想想我们为什么这样定义呢?其实也有道理啊,你像我们这有S1,这有S2,它俩呢,假设都指向常量池里边。
08:45
叫ABC,对于咱们这个,如果一个程序员他不是特别清楚的话,比如就像这种方式哈,如果你关注底层源码了,你知道他俩指向的是同一个结构,同一个结构,但是呢,你要是不关注这个事的话呢,你可能还会误认为它其实是两个hello啊,就是它不会影响你接下接下来这个操作,比如说我把这个S1呢,我想改成这个就叫word了,那这时候呢,如果我再去输出一些S2。
09:15
你发现S2呢,还叫hello,就是你从这个代码上来看,诶你看你叫这个值,你叫这个值,我把S1改了,S2呢,诶肯定不变呀,哎,这个呢,就是他如果不知道内存结构的话呢,他会这样认为,其实结果上也不错,嗯,就是我们S1S2呢,在这个内存当中呢,他俩确实是共用同一个了,但是呢,你现在要对S1进行修改,你不能影响我S2啊,所以此时呢,我们才说就是你要对它进行任何修改啊,你去心造,因为你因为呢,你要是在原有基础上改了,你会导致别的片上也改了的,这是不合适的,所以我们才设置为它叫不可变的这种特性啊。就是这种特性的话呢,会导致我们这个操作起来呢,效率会差一些,呃,每次呢,只要修改你的新建啊好下一个问题说string呢,是否可以被继承。
10:02
能吗?不能,为什么呢?对它声明成final了,这个咱们前面讲面向对象呢,说过,那这个呢,咱们刚才已经讲过了,过了下边一个呢,他们三者的对比,不光是这个执行效率的对比,就是你得输出他们三者呢在开发中使用的一些区别。这个呢也是一道面试当中的高频问题,这个需要大家呢掌握,哎,找个同学说一说。嗯,方间差,嗯,你说说这个题,String型可变,String不可变的自由序列和string是可变字符序列,嗯,他们二者呢,都是可变的字符序列,那个效率一些,相对于,嗯,它相较于string builder来讲效率低一些啊,但是它同时又比这个string要还高一些,对它为什么说它的效率比它低啊,对,他是县城安全的,嗯,县城不安全,别县城危险啊,啥时候说过县城危险啊,县城不安全啊,嗯,发使。
11:22
开发中优先使用哪个,这个得看情况,就是你看你是一个什么样的问题了,是吧,什么问题的时候用string builder啊涉及那个程,对不涉及到多线程,或者说呢,你涉及到多线程了,但是呢,它不是共享数据对吧?嗯,那什么时候用它呢,涉及到多线程,然后操作共享数据,对,就涉及到多线程了,而且呢,就是还会出现这种共享数据了,这个呢,会有安全问题,咱们诶要考虑用4g buffer,嗯。还有吗?占用内存多一些,这是什么意思?
12:11
改变,当发生多次改变的时候,他就是。之前那个。嗯,这个意思其实是说出来了,但是没有表达很清楚啊,就是你说这个占用内存多呢,平常到呃不爱这样说,但是你说的话呢,你要能解释清楚也也OK,你想说的是咱们当时写那个比较他们三者效率的时候呢,我们在这个S,呃,String的基础上,这个不断的让他去加,等于一个新的,一个拼接一个新的内容,这些时候你会每次拼接一个新的,之后呢,你会把旧的这个呢给它,这个相当于是就回收掉了,是吧,你会看到这个内存呢,大量的新建和回收啊对内存的消耗呢比较大,嗯,而我们这两个呢,要稍微好一些,嗯,原因在于它是可变的子物序列,它不会说呢,你每次呢说我去新加一个这个这个小A,呃新加一个小B,他每次呢新加一个都给你新建它俩呢不一定,那因为呢,咱们看源码的时候呢,看过他呢,会提前空出来一些啊,当你要不够的时候呢,它又扩容为二倍加了个二又会空出来好多,所以呢,你每次去添加的时候呢,其实很多时候呢,都没有最。
13:29
去给你新建一个新的内容,而是呢,在原有的这个数组基础上呢,就给你直接把数据放进去了,对内存的消耗呢要小一些,所以起下面呢,就这两个效率呢,比它要高一些,嗯,它们三者呢,其实还有一个相同点,就是咱们提到了他们底层存储的结构呢,对,都是咱们这个叫差型的一个数组,哎,叉型的数组啊,那这个xin数组呢,对于这个string来讲呢,区别呢,就是它加了一个final,就是这个叉型数组呢,一旦复制以后不能改了,嗯,就是你这个索引地址不能再修改,而对于我们后边两个来讲呢,它可没有final。
14:07
而且也不能有final啊,你想你要有final以后呢,我们这个value是不是就意味着不能重新复值了,对,那那那成了不可变了是吧,那咱们这块呢,需要你后边扩容,扩容呢,不就新建了一个嘛,新建一个又重新给它赋的值,所以呢,这个后边两个的查询数组一定不会有翻动,哎像这些内容呢,都是用咱们前面面向对象能解释大家呢,回头我们比如看到一个类,我们没有讲过的,你这个你打开这一类以后呢,你想用哪个方法,或者用它的属性,你一看它的这些声明,你就知道它大概是什么意思,因为呢,我们面向对象呢,基本上把这些关键字啊,这些结构啊,先后执行顺序啊讲的都比较清楚了啊下面一个呢,我出了一个这个问题啊,说string的常用方法有哪些?咱们在讲一个API的时候呢,其实很少说把这个结构当中的所有方法呢都涉及到,或者说呢,讲这里边儿很多方法那类呢,作为其中一个特别情况就是呢,咱们用的比较多,各种各样的这个开发中的操作呢,很多时候都是针对于string的,所以大家呢对它呢要比较熟悉,那当然呢,也涉及到它当中的这个方法的执行。
15:11
啊,其实很多时候面试呢也比较灵活,像这道题呢,这个有的同学回来反映说这个面试的时候也问过他,当然一问呢也可以问懵了啊当然呢,给他家出了一个关于词论的一个小的一个算法题,让他呢就是现考虑他呢,就是可能这个想了想后来没说特别清楚,那因为你这个算法题当中呢,会涉及到string的一些方法的调用,顺便呢,人家就问了一句,哎,那开发当中词类呢,这个你都常用哪些方法呀?哎,顺便就问了一嘴,啊一问呢,诶还真给问懵了,哎像这个问题呢,按说都不用你说去我提前准备啥的了,因为咱们写代码写项目当中,你翻来覆去都会用,那常用方法有哪些呀?这个异常常见的异常都有哪些呀?能不能举举例子呀,像这些呢,平时大家还是得多写代码啊,翻来覆去的去讲啊,啊你写代码写的多以后呢,问到这些问题,你自然而然的就会有反馈啊,你不用提前的去死记硬背说啊,有这些方法那没有意义了啊,那随便写写七个吧。
16:08
嗯,哪些啊,但嗯,Char at这些还有什么啊,Pen的这个注意没有啊,这个是buffer build的,咱这没有,还有啥啊,这个呢,其实很多的啊,你像这个S这个是吧,这是比较是不是相等的,然后还可以比较大小啊compare to啊还有这个咱们讲叫starts with吧,从什么什么开始的,然后还有叫ends with,哎,这都可以,还有呢,就是呃,是不是包含contents是包含,然后呢,还有这个叫index of吧,还有这个叫last index of吧,对吧?啊还有呢,我们string呢,跟其他一些结构的转换,比如叫get bys转换成字节数组呗。
17:08
转换成字符数组呢,那get chars不对是吧,叫to char array,哎等等,然后还涉及到这个string呢,跟基本数据类型包装类之间的转换,哎,是不是好多的VALUE6OF啊,哎等等等等啊,就是这里边方法很多的,大家呢就是呃,随便呢举几个就可以了啊,当然要开发中顺便问到的时候呢,用不着非得说找着七个来啊,就这块呢,你就能想到几个给他说出来就可以了啊这个呢不用提前准备像这种题好这呢就是我们说的这几道问题。
我来说两句