一.String类
String是字符串常量,如果有去看它底层的人都知道,它的底层是由final修饰,所以其值明面上是不可变的序列,可能会有很多人开始疑惑了,没事,等我接下来一一细说

1.1String类创建有俩种方式:
(1)通过构造对象创建
String 变量名 = new String(有参/无参);
(2)通过直接赋值创建
String s1="abc";
这俩者的区别就在于内容相同时,地址不相同,我们先说通过构造对象创建

首先,这个图的结果是false 即俩个s1和s2地址不相同。首先String s1 = new String("abc"); 是创建了新的对象,即会在内存中申请一个空间,接着先在常量池中存放了"abc"这个对象,然后在去堆内存中存放一个"abc"对象。所以有参构造方法在jvm中就创建了俩个对象相比直接赋值会多出一个对象。而String s2 = new String("abc");也会在内存中申请一个新的空间,用于存放"abc"。
所以通过new创建的字符串对象,每一次new都会申请一个内存空间,虽然内容相同,但是地址值不同
接着我来说一下通过直接赋值创建

老样子,我们先说结果为true,即s3和s4的地址相同。在直接将"abc"赋值给到了s3,内存中的堆内存就存放了"abc"这个字符串 而指向了地址(001),栈内存中放着 String s3 (001),当想要通过"abc"字符串赋值给s4时,在堆内存中找到了"abc"这个字符串,并将地址值传回到栈内存中存放的String s4。
所以通过直接赋值创建的方法,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象。
1.2String类 字符串的比较
上面我用到了地址比较是"==",有人说,这不是比较内容的嘛,怎么变成比较值了?
其实"=="对于基本数据类型时,比较的确实是对应值即内容,而对于引用数据类型时,它用来比较地址值,而引用数据类型用什么来比较值呢?
equals
当要比较俩个字符串是否相同时就可以用equals 如s1.equals(s2)
1.3String类 值的改变
我想这是很多人都迫切想知道的。String类被final修饰,那它如何改变其值?
1.字符串拼接
String a="a”;
String b="b";
a=a+b;
System.out.println(a);
答案是:ab
String可以做字符串拼接,从成改变a的值。但明面上我们看到了它改变的自己的值,但是暗地里它连自己都改变了,我们来看一段代码
String a="a";
String b="b";
System.out.println(a.hashCode());
a=a+b;
System.out.println(a);
System.out.println(a.hashCode());
答案是
97
ab
3105
可以发现,俩次a的值都不一样。原理就是String a="a";在栈内存中存放了 String a 及地址值001,在堆内存中存放了"a",而在a=a+b;时,堆内存中的"a"和旧地址就会被清除出堆内存,进来了 a+b的结果,即"ab",然后有了新的地址,在赋予String类的a的新地址
2.反射改变String的值
String s = "abc";
Class<? extends String> aClass = s.getClass();
Field value = aClass.getDeclaredField("value");
value.setAccessible(true);
char[] b=(char[]) value.get(s);
System.out.println(s);//输出原有的值
System.out.println(s+"的hashcode= "+s.hashCode());//原有值的地址
b[0]='A';//原有字符串第一位改为A
System.out.println();
System.out.println(s+"的hashcode= "+s.hashCode());//改变值后的地址
System.out.println(s);//改变后的值
答案
abc
abc的hashcode= 96354
Abc的hashcode= 96354
Abc
通过反射改变的原有字符串的值,但在哈希表的数值仍不变,则地址不变。
二.StringBuilder类
他是一种非线程安全的,StringBuilder的类的对象能够被多次的修改、并且不产生新的对象。
且运行速度快
2.1格式
StringBuilder ss = new StringBuilder(有参或者无参);
当直接传字符串变量,则是将String类转化为StringBuilder类
2.2append、resever
StringBuilder类有自己的字符串添加方法,和字符串反转方法
当单线程操作字符串缓冲区下操作大量数据 StringBuilder。
三.StringBuffer类
StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量 ,且是一种线程安全的。
2.1StringBuffer格式
StringBuilder ss = new StringBuilder(有参或者无参);
有参即直接传字符串变量,将String类转化为StringBuffer
2.2sppend、insert
与String最大的区别就在于StringBuilder类值改变的可行,它有自己的添加方法append()、insert()
可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
2.3deleteCharAt (int index)
public StringBuffer deleteCharAt (int index)
删除指定位置的字符,并返回本身
四.String、StringBuilder、StringBuffer
4.1区别
String类的值,在一般意义上,是不可变的,大部分使用的是字符串拼接,这就使得每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间
所以一般字符串是不可变
StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量 ,且线程是安全的。
StringBuilder是可变类,速度是最快的,但线程不安全。
4.2互相转化
String a="a";
StringBuffer sb1= new StringBuffer(a);//将String ---> StringBuffer
sb1.append("bc");
a=sb1.toString();//将StringBuffer ---> String
System.out.println(a);
String a1="a";
StringBuilder sb2=new StringBuilder(a1);//将String ---> StringBuilder
sb2.append("bcd");
a1=sb2.toString();//将StringBuilder ---> String
System.out.println(a1);
StringBuffer和StringBuilder转换为String类时都是通过toString方法。
而StringBuffer、StringBuilder都可以进行相应操作后在转换为String类
String类转换为StringBuffer和StringBuilder,则在创建对象时加入即可。