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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • Shiro安全框架入门篇(登录验证实例详解与源码)

    Shiro安全框架入门篇(登录验证实例详解与源码)

    2020年11月12日
  • Session.Abandon与Session.Clear之间的区别

    Session.Abandon与Session.Clear之间的区别Session.Clear()就是把Session对象中的所有项目都删除了,Session对象里面什么都没有。但是Session对象还保留。Session.Abandon()就是把当前Session

  • CEGUI0.8.4例子

    CEGUI0.8.4例子#defineGLUT_DISABLE_ATEXIT_HACK#pragmacomment(lib,”glew32.lib”)#include<stdlib.h>#include<gl/glew.h>#include<gl/glut.h>#include<CEGUI/CEGUI.h>#include<CEGUI/RendererM…

  • 软件开发项目管理经验总结

    软件开发项目管理经验总结这是我从事软件外包工作以来的项目管理经验的总结,编写文章的目的是为了回顾和总结自己的一些想法,如果其中有不足的地方大家可以一起讨论交流。项目经理的职责关于项目经理的工作职责有很多种说法,我自己是这样理解的作为一名项目经理第一目标就是合理利用公司资源组织设计、开发、测试等各种资源完成项目的高质量交付,并保证项目的盈利。这是衡量一个项目失败或者成功的唯…

  • IDEA这些既好用又好玩的三十多个宝贝插件你还不知道吗?「建议收藏」

    小编整理的一些好用的有趣的插件如果有什么问题,欢迎大家评论,群文件也有这些IDEA插件QQ交流群:99979568IDEA下载插件教程如果无法在线下载插件,文末有我下载好的安装包,以及安装包安装的教程强烈推荐的插件PresentationAssistant快捷键展示Codota代码智能提示AlibabaJavaCodeGuidelines—阿里巴巴Java代码规范Translation-必备的翻译插件SequenceDiagra.

  • 原码补码反码转换「建议收藏」

    原码补码反码转换「建议收藏」一、机器数和真值在学习原码、反码和补码之前,需要先了解机器数和真值的概念。1、机器数一个数在计算机中的二进制表示形式,叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号,正数为0,负数为1.比如,十进制中的数+2,计算机字长为8位,转换成二进制就是00000010。如果是-2,就是10000010。那么,这里的00000010和10000010就是机器数。2、真值机器数的第一位是符号位,后边才是真正的数值,所以机器数的形式值就不等于真正的数

发表回复

您的电子邮箱地址不会被公开。

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