String

\color{#00FA9A}{what:}

1. 字符串广泛应用在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。

\color{Orchid}{property:}

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

\color{DarkRed}{why:}

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中的键往往都使用字符串。

\color{#000080}{ example:}

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

\color{#7B68EE}{ extend:}

  1. String、StringBuilder、StringBuffer

可变性


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是同步的,因此您具有线程安全性。

\color{#D02090}{resources:}


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)


从零开始系列---学习、复习、记录、思考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容