首页 > 编程语言 >Java基础类String学习分析

Java基础类String学习分析

时间:2022-12-26 23:33:35浏览次数:42  
标签:Java String StringBuilder value 学习 win 字符串 public

目录

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,StringBuilder,value,学习,win,字符串,public
From: https://www.cnblogs.com/knowledgeispower/p/17007142.html

相关文章

  • 《学习OpenCV3》第14章课后习题
    1、在一条含有N个点的封闭轮廓中,我们可以通过比较每个点与其它点的距离,找出最外层的点。(这个翻译有问题,而且这个问题是实际问题)a、这样一个算法的复杂度是多少?b......
  • Camera 画质调试,学习资料分享
    “  最近有很多小伙伴私信我有没有 Camera 相关的学习资料,所以这里总结了一下,我目前看到及学习过的觉得比较优质的资料,大家可以收藏学习起来。注意下文章中的链接无法直......
  • 数值分析·学习 | 解线性方程组的直接方法(高斯消去法以及LU求解)matlab实现
    ​ 目录一、前言:二、算法描述:三、实现代码:1、高斯消去法:2、高斯消去法-列主元消去法:3、LU分解:4、求逆矩阵:四、总结:一、前言:个人学习内容分享二、算法描述:1......
  • 数值分析·学习 | 平方根法和追赶法matlab实现
    ​ 目录一、前言:二、算法描述:三、实现代码:1、平方根法:2、改进的平方根法:3、追赶法:四、总结:一、前言:个人学习内容分享二、算法描述:平方根法:      ......
  • 前端·学习 | 登录界面特效(一)
    ​ 目录前言:特效展示实现代码:html部分:css部分:js部分:总结:前言: 个人学习内容分享特效展示前端界面特效(一) 实现代码:html部分:<!DOCTYPEhtml><ht......
  • Matlab·学习 | (一)太阳系模型
    ​目录前言:效果图如下:Matlab代码:1.主文件:2.相对公转绘制:3.移动轨迹绘制:4.个体主体绘制总结:前言:个人学习内容分享:太阳系模型:初学matlab的绘图学习案例。想要......
  • Java编程思想22
    3.关于volatile  volatile关键字确保了应用中的可视性。如果你将一个域声明为volatile的,那么只要对这个域产生了写操作,那么所有的读操作就都可以看到这个修改。即便使用......
  • 数值分析·学习 | 拉格朗日插值法matlab实现
    ​目录前言一、拉格朗日(Lagrange)插值是什么?二、matlab实现代码1.线性插值:2.抛物线插值:3.拉格朗日(Lagrange)插值:总结:前言本篇内容为个人所学知识分享一、拉格朗......
  • 数值分析·学习 | 牛顿插值法matlab实现
     目录前言一、牛顿插值法是什么?1.均差下的牛顿插值2.为了给出​编辑的表达式,引入均差的概念3.差分形式的牛顿插值公式(牛顿前插公式)三、matlab实现代码1.生成牛顿均......
  • MATLAB·学习 | music:祝你生日快乐
    ​一、前言本篇文章内容为个人学习分享,读代码须知以下乐理的基本知识,以及十二平均律(波的频率与音高的标准),个人推荐在维基百科中搜索十二平均律表。二、整体思想 在......