1. 字符串广泛应用在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。
1.String被final修饰,说明String无法被继承extends
2.内部使用char数组,同样被final修饰
3.注意final修饰的char[]代表了被存储的数据不可更改性, 但是:虽然final代表了不可变,但仅仅是引用不可变,并不代表了数组本身不会变(图一)
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
*该值用于字符存储 *
private final char value[];
/** Cache the hash code for the string */
*缓存字符串的哈希码 *
private int hash; // Default to 0

图一.png
1.String为什么要被final修饰呢
a.为了实现字符串池---结合(图二)可得: 只有当字符串是不可变的,字符串池才有可能实现--- 因为不同的字符串变量(s1,s2,s3)都指向池中的同一个字符串,如果字符串是可变的,那么String中的public native String intern();也将无法实现,同理可得s1,s2,s3也将会随之改变,同时字符串池的实现也会在运行时节约很多的heap空间。
b.为了线程安全---根据源码中private final char value[]; ,分析可得在String对象生成的那一刻它在内存中就是不可变(这里不可变指的是内存地址,并非值)了,其原因是String内部存储字符串的char数组以及和char数组相关的信息都是final的。参考(example.2)
c.为了实现String可以创建HashCode不可变性---字符串在创建时HashCode就被缓存起来了,且不需要再重新计算了,这样的特性字符串就很合适作为Map中的key了, 字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。
1. String 创建的字符串存储在公共池中,而 new 创建的字符串对象在堆上
String s1 = "Runoob"; // String 直接创建
String s2 = "Runoob"; // String 直接创建
String s3 = s1; // 相同引用
String s4 = new String("Runoob"); // String 对象创建
String s5 = new String("Runoob"); // String 对象创建
System.out.println(s1.equals(s2));//true
System.out.println(s1.equals(s3));//true
System.out.println(s1==s2);//true
System.out.println(s1==s3);//true
System.out.println(s4.equals(s5));//true
System.out.println(s4==s5);//false 这里比较的就是内存地址了

图二.png
2.String内存地址的多线程
package com.box.crm.levy;
public class LevyThread implements Runnable{
String t1 = "Levy-Thread-Test";
@Override
public void run() {
System.out.println("LevyThread的内存地址:"+System.identityHashCode(t1));
}
}
package com.box.crm.levy;
public class LevyThread2 implements Runnable{
String t2 = "Levy-Thread-Test";
@Override
public void run() {
System.out.println("LevyThread2的内存地址:"+System.identityHashCode(t2));
}
}
package com.box.crm.levy;
public class LevyThread3 implements Runnable{
String t3 = "Levy-Thread-Test";
String t3_concat = t3.concat("-concat");
String t3_add = t3+"-add";
@Override
public void run() {
System.out.println("LevyThread3的内存地址:"+System.identityHashCode(t3));
System.out.println("t3_concat的内存地址:"+System.identityHashCode(t3_concat));
System.out.println("t3_concat的内存地址:"+System.identityHashCode(t3_add));
}
}
public class LevyTest {
public static void main(String[] args) {
String str_levy = "Levy-Thread-Test";
System.out.println("main中的内存地址:"+System.identityHashCode(str_levy));
LevyThread demo1 = new LevyThread();
LevyThread2 demo2 = new LevyThread2();
LevyThread3 demo3 = new LevyThread3();
Thread thread = new Thread(demo1);
Thread thread2 = new Thread(demo2);
Thread thread3 = new Thread(demo3);
thread.start();
thread2.start();
thread3.start();
//result
main中的内存地址:392292416
LevyThread的内存地址:392292416
LevyThread2的内存地址:392292416
LevyThread3的内存地址:392292416
t3_concat的内存地址:1644271340
t3_concat的内存地址:1824945372
可变性
1.String不可变---由(example.1&图一)可得:当你修改其值的时候,则会直接创建一个新的对象
2.StringBuffer&StringBuilder可变:参考下面例子
这里再举一个常见的案例: 数据库的用户名、密码都是以字符串的形式传入来获得数据库连接得,思考一下
StringBuilder builder = new StringBuilder();
builder.append("builder");
System.out.println("builder.identityHashCode():"+System.identityHashCode(builder));
builder.append(666);
builder.append("8888");
System.out.println("builder.identityHashCode():"+System.identityHashCode(builder));
//result
builder.identityHashCode():392292416
builder.identityHashCode():392292416
StringBuffer buffer =new StringBuffer();
buffer.append("buffer");
System.out.println("buffer.identityHashCode():"+System.identityHashCode(buffer));
buffer.append(666);
buffer.append("8888");
System.out.println("buffer.identityHashCode():"+System.identityHashCode(buffer));
//result
buffer.identityHashCode():1818402158
buffer.identityHashCode():1818402158
线程安全
1.String 不可变,因此是线程安全的
2.StringBuilder 不是线程安全的
3.StringBuffer 是线程安全的,内部使用 synchronized 进行同步
最终结论
1.如果您的字符串不会更改,请使用 String 类,因为String对象是不可变的。
2.如果您的字符串可以更改(例如:字符串构造中的许多逻辑和操作)并且只能从单个线程访问,则使用StringBuilder就足够了。
3.如果您的字符串可以更改,并且可以从多个线程访问,请使用 StringBuffer 因为 StringBuffer是同步的,因此您具有线程安全性。
1.[String的线程安全](https://developer.aliyun.com/article/608537)
2.[String为什么是final](https://www.jianshu.com/p/9c7f5daac283)
3.[String](https://www.pdai.tech/md/java/basic/java-basic-lan-basic.html#string-stringbuffer-and-stringbuilder)
从零开始系列---学习、复习、记录、思考