文章目录
- 1.String结构剖析
- 2.String创建剖析
- 3.String特性分析
- 4.String方法总结
- 5.StringBuffer和StringBuilder总结
- 5.1stringbuffer对象的创建方法
- 5.2string--stringbuffer之间的相互转换:
这个是我第二次学习string,听的是hsp老师的课程,我认为有些东西可以速成,但是对于这个知识的学习,语法的学习,我们还是要听hsp这样的老师的课程,因为这个才是真正的学习,其他的一些机构都是浅尝辄止,我认为达不到学习的效果,所以听韩老师的课学第二遍;
1.String结构剖析
下面的这个常见对象的方式我们再熟悉不过了,但是我们真的未必理解他:
- 我们通过这个底层的源码可以看到:String这个类实际上是实现了三个接口的,分别是这个Serializable,Comparable,CharSequence接口,这个第一个表示这个string是可以进行串行化(这个表示String可以进行网络传输),我之前学习这个Mysql事务的时候听过这个名词。不了解也不要紧,我们后面还会学到的;
- Comparable这个接口表示我们的这个string对象之间其实是可以进行相互的比较的,这个是我们的string里面的一个进行比较的方法,我们后面也会遇到的;
- 在这个源码里面,我们可以看到这个string类的前面加上了这个final进行修饰,这个表示我们的string是不可以被继承,不可以修改的,这个不可以修改指的是我们的这个对象的引用指向的位置是不可以修改的,就是只能指向一块内存空间,不可以修改这个引用的指向,而不是不可以修改这个指向的内容,这个指向的内容是可以进行修改的;
- String里面实现了很多的构造器的重载,就是我们可以根据这个实际的需求去进行这个构造器的调用;
- 其实这个本质上是我们的这个string存放到了一个value数组里面,是这个数组的地址是确定的,不可以进行修改,这个才是科学的解释;
2.String创建剖析
String s1=“hspedu”;
String s2=new String(“hello”);
我们可以通过内存分布图,看一下两个创建对象方式的区别:
我们使用第一个方式进行对象的创建的时候:是直接到我们的这个常量池里面去查看是否存在hspedu这个对象,如果有就会直接指向这个对象,如果没有,我们就会创建这个对象,然后再指向,这个时候栈区里面的这个s1指向的就是常量区里面的字符串,这个地址就是0x11;
第二个创建方式,我们的这个new是在堆区里面开辟空间,然后是到这个常量池里面去查看,这个字符串是不是存在,存在就直接指向这个字符串,否则就创建这个字符串,然后指向,因为我们的这个里面使用到的这个s1和s2是一样的,因此这个里面使用到的这个对象都是0x11这个内容,我们的这个堆区里面的这个value数组地址就是0x11,但是我们的这个堆区地址是0x22,我们的这个栈区上面的s2存放的是堆区的地址,因此,虽然两者指向的内容是一样的,但是这个时候两个引用的地址是有所差别的;
上面的这个题目就是画出内存分布图,其实就很好的解决了:
这个里面直接使用这个equals比较a,b的内容,这个肯定是一样的;
a==b比较的是两个引用的地址,这个时候一个存的是堆区的地址,一个是存放的这个常量区的地址,这个肯定是不一样的啦;
接下来用到了这个intern方法:可以发现,这个intern返回值就是我们的这个堆区指针指向的这个常量区内容的地址,因此这个a=b.intern()表示的就是我们的这个a引用的地址和我们的b在常量区的这个内容的地址进行比较,这个是一样的,也就是说,虽然我们的这个a=b不成立,但是我们的这个b.intern得到这个b常量池地址,这个就可以是一样的了;
最后一个是这个b.intern这个常量池的地址,b存的是我们的这个堆区上面的地址,因此这两个是不一样的,我们一定要理解这个每一个表示的方法,放的是哪个区域的地址,是堆区,还是常量池,我们的引用在哪里。我们的内容在哪里,这样我们就不会混乱了;
下面的这个就是这道题目的内存分布的情况:
首先这个p1.name和p2.name的内容进行比较,肯定是一样的;
p1.name和p2.name进行比较,这个比较的就是p1对象的这个name属性hspedu这个内容的地址,因此这个是没有问题的,两个的这个指向的是常量池上面的相同的区域,因此这个p1.name和p2.name就是一样的;
p1.name和我们的这个字符串进行比较,两个的地址也是一样的,因为这个p1.name就是我们的这个字符串,这个引用指向的是我们的这个常量池里面的这个内容的地址,我们的这个hspedu可以理解为一个字符数组,我们的这个就是打印字符数组的地址,这个字符数组的地址就是这个里面的第一个元素字符的地址,还是在这个常量池区域的这个字符串的地址,因此上面的这三个都是一样的;
下面的这个new出来的两个对象,不用看都是不一样,因为开辟的是堆区上面的不同的空间,我们的这个比较的就是堆区的地址,因此这个就是不一样的,如果是上面的s1.intern==s2.intern得到这个指向的常量池的地址,这个就是一样的;
我们上面的这个代码很简单,下面的这个就是一个简单的内存分布情况,因为这个里面没有涉及到我们的new的情况,这个没有显示出来堆区,我们的这两个对象都是在这个栈区上面的,字符串内容是在我们的常量区,因为两个内容是完全一样的,因此这个时候栈区的引用指向的空间是一样的,无论是equals进行内容比较,还是这个==进行这个内存地址的比较,两个都是一样的;
3.String特性分析
这个其实进行的就是我们的编译器的优化,实际上就创建了一个对象,而不是三个对象;
这个a+b并不是和上面的内容相加一样,这个时候是进行了多于操作的:
首先我们的这个a+b的时候会创建一个stringBuilder的对象,然后使用这个append方法,把我们的这个字符串的内容添加到这个对象里面去,最后使用这个toString方法,把这个对象转换为我们的string类型的作为返回值
我们的这个c就会指向这个堆区上面的对象,这个对象的内容是在我们的常量池上面的,因此这个a+b操作涉及到了new操作,这个就和我们的堆区有关,这个时候的c指向的就是我们的堆区上面的地址;
上面的这个也是韩老师上课用到的一个案例,我之前的一个博客里面已经介绍了这个,就是我们额str=java是指向了新的对象,这个指向的是新的空间,ch[0]表示的就是对于我们原来的内容进行修改;
这个里面调用到了这个change方法,这个调用方法就会创建新的栈,就是在这个栈上面开辟新的空间,这个时候我们的原来的栈和新的栈上面都有这个str,这个时候指向的是相同的空间,在这个change方法里面,我们让这个形参栈的str指向新的str,而且修改这个ch数组里面的内容,然后这个新参的栈就小销毁了;
我们打印的时候,ex.str是这个实参的str内容,因此这个还是打印的hsp,但是这个ch被我们的形参修改了,虽然形参的栈销毁了,但是这个修改保留了下来,因此这个数组的打印结果就是hava;
想要更加深入了解的小伙伴可以去看我的主页的文章—函数栈帧的创建和销毁,明白这个过程,这个题目的栈帧的销毁创建就不难理解了;我们的形参的这个栈开始的时候指向的内容和我们的实参是一样的,但是我们的str是指向了新的内容,ch是修改元素的内容;
4.String方法总结
这个里面其实是有很多的方法的,但是之前已经总结过一些:这个里面就写一个之前没有遇到过的方法
compareto方法进行字符串的比较:
String str1="jcck";
String str2="jack";
System.out.println(a.compareTo(str2));
上面的这个情形下,这个str1和str2两个对象的这个长度是一样的,我们就会从前面开始比较,这个第一个字符一样,开始比较第二个字符,c的这个码值比a大二,这个时候的返回值就是我们的2;
如果两个字符串的长度不一样:
String str1="jcc";
String str2="jack";
System.out.println(a.compareTo(str2));
其实这个情况下,我们就会使用第一个字符串的长度减去第二个字符串的长度,因此这个时候的返回值就是我们的3-4=-1;
即长度相等的时候返回值是我们的码值的差值,长度不相等的时候返回的是我们的字符串的长度的差值;
方法了解就可以了,我们用到的时候到这个文档里面查找即可;
5.StringBuffer和StringBuilder总结
5.1stringbuffer对象的创建方法
string每一次变化都需要在这个常量池里面创建新的对象,但是我们的这个stringbuilder是在我们的堆区的,因为这个stringbuilder内容是可以修改的,不是在常量区,而是在这个堆区上面的;—stringbuilder效率会更高
下面的是stringbuffer对象三个不同的创建方式:
第一个没有任何的参数,这个时候就是开辟出来大小为16的数组空间;
制定了一个数字参数,就会开辟出来该容量大小的空间;
指定了一个字符串,我们开辟空间的大小就是这个字符串的长度加上16;
5.2string–stringbuffer之间的相互转换:
string转换为stringbuffer:
下面的两个方式:一个是直接把我们的这个str作为参数,第二个就是调用这个append方法,把我们的这个字符串添加到这个对象里面去,这个时候相当于是创建了一个内容一样的stringbuffer对象;
stringbuffer转换为string对象:
一种就是使用我们的这个tostring方法进行这个转换;
第二个就是使用new一个string对象,这个里面的参数就是我们的这个stringbuffer对象;
stringbuiler主要是单线程的情况,因为这个本身是不安全的,但是对于这个单线程的情况,速度是比这个stringbuffer更快的;
buffer对象;
[外链图片转存中…(img-KheAQLSp-1728912498496)]
stringbuiler主要是单线程的情况,因为这个本身是不安全的,但是对于这个单线程的情况,速度是比这个stringbuffer更快的;
[外链图片转存中…(img-iiA9ihRj-1728912498497)]