String,StringBuffer,StringBuilder
String是final修饰的,不可变得,每次操作都会产生新的对象。
String这个类是被final修饰的,里面存的东西不能变,变了的话就会产生新的对对象。
jdk9之前是char数组,jdk9之后就是byte数组,这里写的都是关于jdk8的
StringBuffer,和StringBuilder都是在原对象上操作。
StringBuffer是线程安全的,StringBuilder线程是不安全的。
StringBuffer的方法都是synchronized修饰的。
这俩扩容都是当存的容量大于原来的容量时,才会扩容为原来的2倍加二,是通过Arrays.copyOf
进行复制扩容的;他们俩都是继承AbstractStringBuilder这个类,一char数组存储
传进来的minCapacity是原字符串长度加现需要追加的字符串长度,即(当前追加需要改变后的容量)。
为啥每次扩容是2倍,而且还要加2?
传入参数为int,这里万一传入的参数为零,<<相当于乘以2,结果还是零,此时如果初始化数组的时候必然报错,所以为了安全考虑,此处加2,加2避免左移一位还是0这样的问题,还有就是当开始传入的字符串长度较小时,j加2可以提高扩容效率,减少扩容次数。如果扩容后大小还不计划改变后的容量小,那么直接将要改变后的大小的值给给现在需要扩容的。
扩容左移一位的(*2),底层都是2进制的,左移一位是因为位运算比较快。
append方法会去判断是否需要扩容,如果字符串的长度大于扩容后的,那么这个新扩容的字符串大小为传进来的字符串大小。
为啥默认容量是16?
Stringbuilder的有参构造:
父类的有参构造:
次时容量为我们传入的字符串长度+16
但是输出此时存有字符串长度时,我们输出的是已存数据的长度
子类中
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
public StringBuilder append(String str) {
super.append(str);
return this;
}
父类中
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
此时若输出字符长度的话,输出的是count(原来数组长度)+len(传进来的数组长度),及所存字符串的长度,
StringBuilder 和StringBuffer 一样的只是StringBuffer 里面的方法加锁了
字符串的拼接采用的都是append方法
性能:**StringBuilder > StringBuffer > String
使用场景:经常需要改变字符串内容时使用StringBuilder,StringBuffer
优先使用 StringBuilder,多线程使用共享变量时使用StringBuffer
==和equals的区别
==对比的是栈的的值,基本数据类型是变量值,引用类型是堆中内存对象的地址。
equals:object中默认也是采用==比较,通常会重写(String中已经帮我们重写了quals方法,将字符串中的每一个字符取出来取对比,看是否相等)
堆中有特殊的区域叫常量池,String s1="hello" hello会在常量池中去分配内存。通过new 来创建的对象是会分配在堆中 s1在栈中存的是内存地址。String s2=s1;赋值赋的是引用地址。 字符串==比较比的就是栈的的值,
都是在java内存(ram)中存储数据的地方
堆区(heap):存储的全部是对象,每一个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令):jvm只有一个heap区,被所有线程共享,不存放基本数据类型(byte,short,int,long,folat,double,char,bolean)和对象引用(String s1=new String()此时对象引用为s1),只存放对象本身。
堆得优劣势:
优势:堆可以动态的分配内存大小,生存期也不必事先告诉编译器,java中的垃圾收集器会自动收取这些不在使用的数据。
缺点: 由于运行时动态分配内存,读取速度慢。
栈区(stack):每一个线程包含一个stack区,只保存基本数据类型的对象和自定义对象的引用(不是对象),对象都存放在共享heap中;每个栈中的数据(基本数据类型和对象引用)都是私有的,其他栈不能访问; 栈分为3部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)
栈的优势劣势:存取速度比堆要快,仅次于直接位于CPU的寄存器,但必须确定的是存在stack中的数据大小与生存期必须是确定的,缺乏灵活性。单个stack的数据可以共享。
stack:是一个先进后出的数据结构,通常保存方法中的参数,局部变量。在java中,所有基本类型和引用类型都在stack中储存,栈中数据的生存空间一般在当前scopes内
栈的用法:
栈本身没有add,peek这些都是继承父类vector的。
栈自己有push,和pop,
栈的pop和peek,都是弹出,但是pop弹出后会删除栈顶的元素,而peek不会改变栈的值(不会删除栈顶元素)
栈的push在压入成功后会返回当前压入的对象,而add成功后会返回ture。
方法区:
1.又叫静态区,根堆一样,被所有的线程共享。方法去包含多有的class和static变量;
2.方法去中包含的都是在程序中唯一的元素。
java中流中不同存储数据的地方
1.寄存器
2.栈
3.堆
4.静态存储
5.常量存储
6.非RAM存储。