stringbuffer和stringbuilder是什么_Java编程

stringbuffer和stringbuilder是什么_Java编程字符串常量池什么是字符串常量池?JVM为了减少字符串对象的重复创建,其维护了一块特殊的内存,这段内存被称为字符串常量池(存储在方法区中)。具体实现当代码中出现字符串时,JVM首先会对其进行检查。如果字符串常量池中存在相同内容的字符串对象,则将这个对象的地址返回。如果字符串常量池中不存在相同内容的字符串对象,则创建一个新的字符串对象并放入常量池。newString(“str…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

字符串常量池

  1. 什么是字符串常量池?
    JVM为了减少字符串对象的重复创建,其维护了一块特殊的内存,这段内存被称为字符串常量池(存储在方法区中)。

  2. 具体实现
    当代码中出现字符串时,JVM首先会对其进行检查。

    • 如果字符串常量池中存在相同内容的字符串对象,如果有,则不再创建,直接返回这个对象的地址返回
    • 如果字符串常量池中不存在相同内容的字符串对象,则创建一个新的字符串对象并放入常量池,并返回新创建的字符串的引用地址。
    • new String(“str”)时,首先也会去检查常量池是否存在“str”(存在则不创建、不存在则在常量池先创建一个),然后在堆空间再开辟一块内存区域创建字符串对象 。

String 与 CharSequence区别

在这里插入图片描述

  • String 继承于CharSequence,也就是说String也是CharSequence类型。
  • CharSequence是一个接口,它只包括length(), charAt(int index), subSequence(int start, int end)这几个API接口。
  • 除了String实现了CharSequence之外,StringBuffer和StringBuilder也实现了CharSequence接口。
  • CharSequence就是字符串,String, StringBuilder和StringBuffer本质上都是通过字符数组实现的!
  • CharSequence与String都能用于定义字符串,但CharSequence的值是可读可写字符串,而String的值是只读字符串

概述String,StringBuffer与StringBuilder的区别

String在java编程中广泛应用,首先从源码进行分析
在这里插入图片描述
String底层是一个final类型的字符数组,所以String的值是不可变的每次对String的操作都会生成新的String对象,造成内存浪费 而StringBuffer和StringBuilder就不一样了,他们两都继承了AbstractStringBuilder抽象类,从AbstractStringBuilder抽象类中我们可以看到
在这里插入图片描述
他们的底层都是可变的字符数组,所以在进行频繁的字符串操作时,建议使用StringBuffer和StringBuilder来进行操作。

接着看一下他们的继承结构以及部分源码实现
在这里插入图片描述
StringBuffer.apped()方法是线程安全的
在这里插入图片描述

StringBuilder.apped()方法线程不安全
在这里插入图片描述

从这三张图我们不难得知:

  1. StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)
  2. 由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。

细说一下String

  1. String并不是基本数据类型,而是一个对象。
  2. 字符串为对象,那么在初始化之前,它的值为null,到这里就有必要提下””、null、new String()三者的区别。
  3. null 表示string还没有new ,也就是说对象的引用还没有创建,也没有分配内存空间给他
  4. 而””、new String()则说明了已经new了,只不过内部为空,但是它创建了对象的引用,是需要分配内存空间的。

java的虚拟机在内存中开辟出一块单独的区域,用来存储字符串对象,这块内存区域被称为字符串常量池(字符串缓冲池)。那个java的字符串缓冲池是如何工作的呢?

String a = "abc";
String b = "abc";
String c = new String("xyz");

在这里插入图片描述

String a = "abc";
  • 创建字符串的时候先查找字符串常量池有没有相同的对象,如果相同的对象就直接返回该对象的引用,如果没有相同的对象就在字符串常量池中创建该对象,然后将该对象的引用返回。对于这一步而言,缓冲池中没有abc这个字符串对象,所以首先创建一个字符串对象,然后将对象引用返回给a。
String b = "abc";
  • 这一句也是想要创建一个对象引用变量b使其指向abc这一对象。这时,首先查找字符串常量池,发现abc这个对象已经有了,这是就直接将这个对象的引用返回给b,此时a和b就共用了一个对象abc,不过不用担心,a改变了字符串不会影响b,因为字符串都是常量,一旦创建就没办法修改了,除非创建一个新的对象。
String c = new String("xyz");

String c = new String(“xyz”); JVM首先是在字符串常量池中找”xyz” 字符串,如果没有创建字符串常量,然后放到常量池中,若已存在,则不需要创建;当遇到 new 时,还会在内存(不是字符串常量池中)上创建一个新的String对象,存储”Hello”,并将内存上的String对象引用地址返回。

从上边的分析可以看出,当new一个字符串时并不一定是创建了一个新的对象,有可能是与别的引用变量共同使用了同一个对象。下面看几个常见的有关字符串常量池的问题。

创建了几个对象

String a = "abc";
String b = "abc";
String c = new String("xyz");
String d = new String("xyz");
String e = "ab" + "cd";

这个程序与上边的程序比较相似,我们分比来看一下:

  1. String a = “abc”;这一句由于常量池中没有abc这个字符串对象,所以会创建一个对象;

  2. String b = “abc”;由于常量池中已经有了abc这个对象,所以不会再创建新的对象;

  3. String c = new String(“xyz”);由于常量池没有xyz这个字符串对象,所以会首先创建一个xyz的对象,然后放到常量池中,然后new的时候,在内存中(不是常量池中)又创建了一个新的字符串对象,所以一共创建了两个对象;

4、String d = new String(“xyz”);常量池中已有该字符串对象,则常量池中不再创建该对象,然后会在new的时候内存中创建一个新的字符串对象,所以只创建了一个对象;

5、String e = ”ab” + ”cd”;由于常量的值在编译的时候就被确定了。所以这一句等价于String e = ”abcd”;所以创建了一个对象;

所以创建的对象的个数分别是:1,0,2,1,1

了解了String类的工作原理,回归问题本身。
在String的工作原理中,已经提到了,new 一个String对象,是需要先在字符串常量中查找相同值或创建一个字符串常量,然后再在内存中创建一个String对象,所以 String str = new String(“xyz”); 会创建两个对象。

到底相等不相等

我们知道两个字符串对象相等的判断要用equal而不能使用==,但是学习了字符串常量池以后,应该知道为什么不能用==, 什么情况下==equal等价的>

首先,必须知道的是

  1. equal比较的是两个字符串的是否相等
  2. ==比较的是两个对象的内存地址是否相等

实例一:

public static void main(String[] args) { 
    
    String s1 = "money"; 
    String s2 = "money"; 
    if (s1 == s2) { 
   
    	System.out.println("s1 == s2"); 
    } else { 
   
    	System.out.println("s1 != s2"); 
    }
}

执行结果: s1 == s2
分析: 通过对字符串常量池的了解,我们知道s1和s2都是指向字符串常量池中的同一个对象,所以内存地址是一样的,所以用==可以判断两个字符串是否相等。

实例二:

public static void main(String[] args) { 
    
  	String s1 = "money"; 
    String s2 = new String("money"); 
    if (s1 == s2) { 
   
    	System.out.println("s1 == s2"); 
    } else { 
   
		 System.out.println("s1 != s2"); 
	}
    if (s1.equals(s2)) { 
   
    	System.out.println("s1 equals s2"); 
    } else { 
   
    	System.out.println("s1 not equals s2"); 
    }
} 

执行结果:
s1 != s2
s1 equals s2

分析: String s2 = new String(“money”);这一句话没有字符串常量池中创建新的对象,但是会在内存的其他位置创建一个新的对象,所以s1是指向字符串常量池的s2是指向内存的其他位置两者的内存地址不同的

实例三:

public static void main(String[] args) { 
    
    String s1 = "money"; 
    String s2 = new String("money"); 
    s2 = s2.intern(); 
    if (s1 == s2) { 
   
        System.out.println("s1 == s2"); 
    }else { 
   
        System.out.println("s1 != s2"); 
    }
    if (s1.equals(s2)) { 
   
        System.out.println("s1 equals s2"); 
    }else { 
   
        System.out.println("s1 not equals s2"); 
    }
}

输出结果:
s1 == s2
s1 equals s2
分析: 先来说说intern()这个方法的作用吧,这个方法的作用是返回在字符串常量池中的对象的引用,所以s2指向的也是字符串常量池中的地址,和s1是相等的。

intern()方法:返回在字符串常量池中的对象的引用

实例四:

public static void main(String[] args) { 
    
    String Monday = "Monday";  
    String Mon = "Mon";  
    String  day = "day";  
    System.out.println(Monday == "Mon" + "day");  
    System.out.println(Monday == "Mon" + day);  

}

输出结果:
true
false
分析: 第一个为什么等于true我们已经说过了,因为两者都是常量所以在编译阶段就已经能确定了,在第二个中,day是一个变量,所以不能提前确定他的值,所以两者不相等,从这个例子我们可以看出,只有+连接的两边都是字符串常量时,引用才会指向字符串常量池否则都是指向内存中的其他地址。

实例五:

public static void main(String[] args) { 
    
    String Monday = "Monday";  
    String Mon = "Mon";  
    final String  day = "day";  
    System.out.println(Monday == "Mon" + "day");  
    System.out.println(Monday == "Mon" + day);  
}

输出结果:
true
true
分析: 加上final后day也变成了常量,所以第二句的引用也是指向的字符串常量池。

String有没有线程安全问题

String类是一个不可变对象,其它有两层意思:

  1. 一是String类是一个final类,不能产生一个String的子类;
  2. 二是在String类中提供的所有方法中,如果有String返回值就会创建一个String对象,不对原对象进行修改,这就保证了原对象不可改变。

总结

  • Java中String对象是不可变的

  • Java支持通过构造方法或字面常量创建字符串

  • 字符串对象存放的位置可能在堆内存,也可能在字符串常量池(和创建方法以及JDK的版本有关)。使用构造方法构建的字符串对象一定在堆内存,如果堆该字符串对象调用String.intern()方法,则可以将该字符串移入字符串常量池。

  • 字符串常量池在JVM底层本质上是一个Hashtable

  • 字符串上支持很多操作API,例如字符串连接、截取字符串、trim、替换字符等等,这些操作看似是写操作,实际上都会返回一个新的字符串

  • 字符串的连接方式:“+”运算符重载,底层是依靠StringBuilder实现的;String.contact()方法,底层是依赖Array.copy实现的;ringBuilder,通过预先分配一个字符缓冲区来进行字符串的连接,适合大批量字符串连接的情况

  • StringBuilder是JDK1.5提供的,目的是补充StringBuffer用在单线程环境下——不必要且性能低的不足。

  • String、StringBuilder和StringBuffer的底层数据结构都是char[]数组,不同的是String将该char数组设置成了不可变的(final)

  • String的intern()方法调用该方法时,如果字符串常量池中包括了一个等于此String对象的字符串(由equals方法确定),则返回池中的字符串的引用。否则,将此String对象添加到池中,并且返回此池中对象的引用

    • 在JDK6中,不推荐大量使用intern方法,因为这个版本字符串缓存在永久代中,这个空间是有限了,除了FullGC之外不会被清楚,所以大量的缓存在这容易OutOfMemoryError。

在这里插入图片描述

  1. 如果要操作少量的数据用 String;

  2. 多线程操作字符串常量池下操作大量数据 StringBuffer;

  3. 单线程操作字符串常量池下操作大量数据 StringBuilder。

字符串在JDK1.6—JDK1.8的区别

  • JDK1.6及以前,常量池在方法区,这时的方法区也叫做永久代,其中存放的是字符串的实例(字符串存在永久代中,容易出现性能问题和内存溢出。)
  • JDK1.7(含)方法区合并到了堆内存中,这时的常量池也可以说是在堆内存中,存储的是字符串对象的引用,字符串实例是在堆中

    1.6之后的版本把字符串放入了堆中,避免了永久代被挤满。

  • JDK1.8 已移除永久代,方法区又从堆内存中剥离出来了字符串常量池是在本地内存当中,存储的也只是引用。但实现方式与之前的永久代不同,这时的方法区被叫做元空间,常量池就存储在元空间
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/193656.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 独立成分分析(ICA)

    独立成分分析(ICA)1.概念独立成分分析是从多元(多维)统计数据中寻找潜在因子或成分的一种方法.ICA与其它的方法重要的区别在于,它寻找满足统计独立和非高斯的成分。这里我们简要介绍ICA的基本概念、应用和估计原理。1.1 多元数据的线性表示统计数据处理及相关领域中的一个重要和需要长期研究的问题就…

    2022年5月17日
    48
  • CODEIF_变量取名「建议收藏」

    CODEIF_变量取名「建议收藏」网址https://unbug.github.io/codelf/大部分开发者都或多或少遇到过变量命名的烦恼,如果命名不规范,不仅会影响开发的效率,而且对后面维护的同学来说也是一个不小的挑战,因为他要去揣摩你这个变量的含义。“计算机科学里两件最难的事:缓存失效和命名。”Codelf通过搜索在线开源平台Github,Bitbucket,GoogleCode,Codeplex,Sou…

    2022年4月19日
    438
  • 花里胡哨之Typora主题

    花里胡哨之Typora主题工具介绍 Typora 是一款支持实时预览的 Markdown 编辑器 目前 Typora 支持 OSX Windows Linux 三个平台的使用 Typora 支持个性化的主题定制和简便的 Markdown 操作 是一款很实用的编辑器 Markdown 原本就是一个 HTML 语言方式 所以 Typora 能够进行相应的编辑准备下载 要想使用一个工具的前提 毫无疑问那肯定是先下载或安装 以下是 Typora 的官网地址 可以直接在官网上下载最新版本进行安装 https www typora i

    2025年8月19日
    3
  • github开源可视化_可视化拖拽项目管理 github

    github开源可视化_可视化拖拽项目管理 githubGIT下载:国内太慢甚至下载不了的解决方法。在淘宝的镜像网站上下载: https://npm.taobao.org/mirrors/git-for-windows/gitconfig

    2022年8月2日
    8
  • IDEA教程之Activiti插件[通俗易懂]

    IDEA教程之Activiti插件[通俗易懂]本文作者:Spring_ZYL意见反馈:15065421873@163.com文章来源:https://blog.csdn.net/gozhuyinglong版权声明:本文版权归作者所有,转载请注明出处一、安装Activiti插件1.搜索插件点击菜单【File】–>【Settings…】打开【Settings】窗口。点击左侧【Plugins】…

    2022年6月10日
    500
  • HTML5新控件 – 日期和时间选择输入

    HTML5新控件 – 日期和时间选择输入转载自:https://blog.csdn.net/u014063717/article/details/50914466HTML5定义了几个与日期有关的新控件。支持日期控件的浏览器会提供一个方便的下拉式日历,供用户选择。注意:我测试了Chrome和Opera,火狐提供下拉式日历支持,其它浏览器可能仍是一个普通文本框。1,日期控件-date<inputtype="date"valu…

    2022年5月24日
    265

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号