1 String不可变性

  1. String类被声明为 final,因此它不可被继承。
  2. 内部使用char数组存储数据,该数组被声明为final,这意味着value数组初始化之后就不能再指向其它数组。
  3. String内部没有改变value数组的方法
  4. String类中所有修改String值的方法,如果内容没有改变,则返回原来的String对象引用,如果改变了,创建了一个全新的String对象,包含修改后的字符串内容,最初的String对象没有任何改变。(目的:节约存储空间、避免额外的开销)
//String的类声明以及value字段代码:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[]; //字符数组存储String的内容
/** Cache the hash code for the string */
private int hash; // Default to 0
}

不可变的验证分析:

public class Immutable {
public static String upcase(String s) {
return s.toUpperCase();
}
public static void main(String[] args) {
String q = "howdy";
System.out.println(q); // howdy
String qq = upcase(q);
System.out.println(qq); // HOWDY
System.out.println(q); // howdy
}
} /* 输出:
howdy
HOWDY
howdy
*///:~
  1. 当把q传给upcase0方法时,实际传递的是引用的一个拷贝。
  2. upcase0方法中,传入引用s,只有upcase0运行的时候,局部引用s才存在。一旦upcase0运行结束,s就消失。upcaseO的返回值是最终结果的引用。
  3. 综上,upcase()返回的引用已经指向了一个新的对象,而原本的q则还在原地。

延伸结论:

String对象作为方法的参数时,都会复制一份引用,参数传递是引用的拷贝

2 不可变的好处

1. 可以缓存 hash 值

String的hash值经常被使用,例如String用做HashMap的key。不可变的特性可以使得hash值也不可变,因此只需要进行一次计算。

2. String Pool 的需要

如果一个String对象已经被创建过了,那么就会从 String Pool 中取得引用。只有String是不可变的,才可能使用 String Pool。

3. 线程安全

String不可变性天生具备线程安全,可以在多个线程中安全地使用。

3 String+和StringBuilder效率差异

String使用“+”表示字符串拼接

先说结论:

  1. “+”操作,javac编译器会自动优化为StringBuilder.append() 调用。
  2. StringBuilder要比“+”操作高效
  3. 涉及循环追加的,手动创建StringBuilder对象操作比“+”操作编译器优化,更高效

验证:

public class StringBuilderTest {
public static void main(String[] args) {
String s1 = "ABC";
String s2 = "123";
String result = s1+s2;
System.out.println(result);
}
}

编译并查看字节码:javap -verbose StringBuilderTest.class

执行过程:

调用了2次append()方法,最后调用StringBuilder.toString()返回最终结果

为什么StringBuilder要比+高效?

  1. +操作,按照:每次追加都创建新的String对象,把字符加入value数组中。这里产生一次对象创建操作,以及对应的垃圾回收
  2. StringBuilder的底层数组value也是用到了char[],但它没有声明为final,故它可变,所以追加内容时不用创建新的数组,而是直接修改value
  3. StringBuilder比+省去String对象创建以及垃圾回收的开销,因此效率更高。

源码追溯:

 //StringBuilder.append()
@Override
public StringBuilder append(char c) {
super.append(c);
return this;
}
// 父类 AbstractStringBuilder.append()
@Override
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
value[count++] = c;
return this;
}
//AbstractStringBuilder value 字段
abstract class AbstractStringBuilder implements Appendable, CharSequence {
//The value is used for character storage.
char[] value; // 没有声明为final,因此value可变
}

手动实现StringBuilder对象操作比编译器自行优化,更高效

  1. 通过字节码分析可知(我这里省去了,可以自己实现验证):循环部分的代码更简短、更简单,而且它只生成了一个StringBuilder对象
  2. 显式地创建StringBuilder还允许你预先为其指定大小。如果你已经知道最终的字符串大概有多长,那预先指定StringBuilder的大小可以避免多次重新分配缓冲
当你为一个类编写toString方法时,如果字符串操作比较简单,那就可以信赖编译
器,它会为你合理地构造最终的字符串结果。但是,如果你要在toString0方法中使用循环,那
么最好自己创建一个StringBuilder对象来实现。

4 String, StringBuffer and StringBuilder

可变性

  1. String 不可变
  2. StringBuffer和StringBuilder可变

线程安全

  1. String不可变,因此是线程安全的
  2. StringBuilder 不是线程安全的
  3. StringBuffer 是线程安全的,内部使用synchronized进行同步

效率

  1. 如果要操作少量的数据用String
  2. 单线程环境且字符串缓冲区涉及大量数据 StringBuilder
  3. 多线程环境且字符串缓冲区涉及大量数据 StringBuffer

5 String与JVM内存管理

一、引入字符串常量池

  1. Javac编译后,字节码文件中有一块区域:常量池,存储了包括类中声明的字符串常量值等字面量
  2. 运行时,JVM开辟实际内存空间:字符串常量值写入了字符串常量池,属于方法区的一部分。

案例1

String s1 = "win";
String s2 = "win";
System.out.println(s1==s2);
//输出结果:true
//引用 s1 s2 的值等于win在字符串常量池的地址值

结论:

引用 s1 s2 的值等于win在字符串常量池的地址值

分析字节码的执行过程:


案例2

public class StringPool {
public static void main(String[] args) {
String s3 = new String("win");
String s4 = new String("win");
System.out.println(s3==s4);//false
}
}

结论:

通过new操作符创建的字符串对象不指向字符串池中的任何对象

字节码分析:

综上:

public class StringPool {
public static void main(String[] args) {
String s1 = "win";
String s2 = "win";
String s3 = new String("win");
String s4 = new String("win"); System.out.println(s1==s2);//true
System.out.println(s1==s3);//false
System.out.println(s3==s4);//false
}
}

6 String api方法

从这个表中可以看出,当需要改变字符串的内容时,String类的方法都会返回一个新的String对象。同时,如果内容没有发生改变,String的方法只是返回指向原对象的引用而已。这可以节约存储空间以及避免额外的开销。



Java基础类String学习分析的更多相关文章

  1. Java的string学习笔记 与char数组和bufferstring的比较

    ---恢复内容开始--- 一直用的C 导致这种类望而生畏 现在终于鼓起勇气学习一下 首先学习string类型 String s1 = "AbCdEf"; String s2 = & ...

  2. java中String创建对象分析(转)

    String str=new String("abc");   紧接着这段代码之后的往往是这个问题,那就是这行代码究竟创建了几个String对象呢? 相信大家对这道题并不陌生,答案 ...

  3. Java—关于String的分析

    一.两种赋值方式的比较 1.直接赋值法:String s1="abc"; 这种赋值方法用的最多,因为它可能不需要创建对象,或者只创建一次. 它首先会判断字符串常量池有没有存在字符串 ...

  4. [Java] 01 String 内存分析

    public class StringTest{ public static void main(String[] args){ String str1 = new String("123& ...

  5. Java源码学习 -- java.lang.String

    java.lang.String是使用频率非常高的类.要想更好的使用java.lang.String类,了解其源代码实现是非常有必要的.由java.lang.String,自然联想到java.lang ...

  6. 从源码分析java.lang.String.isEmpty()

    今天在写代码的时候用到了java.lang.String.isEmpty()的这个方法,之前也用过,今天突发奇想,就看了看源码,了解了解它的实现方法,总结出来,大家可以交流交流. 通常情况下,我们使用 ...

  7. Java中String连接性能的分析【转】

    [转]http://www.blogjava.net/javagrass/archive/2010/01/24/310650.html 总结:如果String的数量小于4(不含4),使用String. ...

  8. SSM框架报错分析(一)——There is no getter for property named 'XXX' in 'class java.lang.String'

    一.发现问题 <select id="queryStudentByNum" resultType="student" parameterType=&quo ...

  9. Java中String连接性能的分析

    总结:如果String的数量小于4(不含4),使用String.concat()来连接String,否则首先计算最终结果的长度,再用该长度来创建一个StringBuilder,最后使用这个String ...

  10. java中String类学习

    java中String类的相关操作如下: (1)初始化:例如,String s = “abc”; (2)length:返回字符串的长度. (3)charAT:字符操作,按照索引值获得字符串中的指定字符 ...

随机推荐

  1. JavaScript基础&实战(5)js中的数组、forEach遍历、Date对象、Math、String对象

    文章目录 1.工厂方法创建对象 1.1 代码块 1.2.测试结果 2.原型对象 2.1 代码 2.2 测试结果 3.toString 3.1 代码 3.2 测试结果 4.数组 4.1 代码 5.字面量 ...

  2. 齐博x1关于小程序个性源代码的说明

    系统默认推荐商家小程序使用通用型的源码,即框架套壳iframe形式的.这个灵活性更高.但如果有特殊需求的话,也可以设置个性源码,比如配合uni-app使用,针对不同的小程序就使用不同的uni-app风 ...

  3. 三十五、kubernetes NameSpace介绍

    Kubernetes NameSpace 介绍 Kubernetes使用命名空间的概念帮助解决集群中在管理对象时的复杂性问题.命名空间允许将对象分组到一起,便于将它们作为一个单元进行筛选和控制.无论是 ...

  4. 创建.NET程序Dump的几种姿势

    当一个应用程序运行的有问题时,生成一个Dump文件来调试它可能会很有用.在Windows.Linux或Azure上有许多方法可以生成转储文件. Windows平台 dotnet-dump (Windo ...

  5. centos7离线安装PHP7

    环境 centos7.9 PHP7.4.30 准备工作 在编译PHP时会提示一些包版本不够或者缺少某些包,一般选择yum来安装缺少的包,但因为是离线安装,所以可以手动配置本地yum源.先看一下系统版本 ...

  6. Scrapy 如何传递 get请求的params

    我们都知道 在requests中可以使用 requests.get(url,params)的方式传值 那么在scrapy中如何传值呢 直接看代码 from urllib.parse import ur ...

  7. windows下 安装docker

    一.Docker 1.什么是docker 对比 特性 容器 虚拟机 启动 秒级 分钟级 磁盘使用 一般为MB 一般为GB 性能 接近原生 弱于 系统支持量 单机支持上千个容器 一般几十个 2. 使用d ...

  8. csharp 基础

    1.C#基础 1.1 C#简介 C#是一个面向对象的.由微软开发的高级编程语言,它专门为公共语言基础结构(CLI)设计的,CLI是由可执行代码和运行时环境组成的. C#语言在编写时有点像JAVA,在运 ...

  9. ThreadLocal的介绍与运用

    ThreadLocal全面解析 学习目标 了解ThreadLocal的介绍 掌握ThreadLocal的运用场景 了解ThreadLocal的内部结构 了解ThreadLocal的核心方法源码 了解T ...

  10. Java安全之CC6

    前言 之前三篇详细分析了CommonsCollections1利用链,两种方法,LazyMap以及TransformedMap,但是在Javaa 8u71以后,这个利⽤链不能再利⽤了,主要原因是 su ...