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)


相关推荐

  • 精通Dubbo——Dubbo支持的协议的详解[通俗易懂]

    精通Dubbo——Dubbo支持的协议的详解[通俗易懂]Dubbo支持dubbo、rmi、hessian、http、webservice、thrift、redis等多种协议,但是Dubbo官网是推荐我们使用Dubbo协议的。下面我们就针对Dubbo的每种协议详解讲解,以便我们在实际应用中能够正确取舍。dubbo协议缺省协议,使用基于mina1.1.7+hessian3.2.1的tbremoting交互。连接个数:单连接连接方式:长连接传输协议

  • decltype typename[通俗易懂]

    decltype typename[通俗易懂]decltype((variable))总是引用类型,但是decltype(variable)只有当variable是引用类型时才是引用类型。#include<iostream>#include<typeinfo>usingstd::cin;usingstd::cout;usingstd::endl;autof(au…

  • uIP中国的协议文件:Ch01

    uIP中国的协议文件:Ch01

    2021年12月17日
  • Python assert 断言函数「建议收藏」

    使用assert断言是学习python一个非常好的习惯,pythonassert断言句语格式及用法很简单。在没完善一个程序之前,我们不知道程序在哪里会出错,与其让它在运行最崩溃,不如在出现错误条件时就崩溃,这时候就需要assert断言的帮助。本文主要是讲assert断言的基础知识。pythonassert断言的作用pythonassert断言是声明其布尔值必须为真的判定,如果发生异

  • JavaScript两种数组去重方法「建议收藏」

    JavaScript两种数组去重方法「建议收藏」JavaScript两种数组去重方法第一种(利用splice方法去重)splice()方法用于插入、删除或替换数组的元素。//申明一个数组vararr=[1,3,5,4,3,1,5,7,8,4,7];functiontemp(arr){for(i=0;i<arr.length;i++){for(j…

  • bt3使用_手机怎么下载视频到u盘上

    bt3使用_手机怎么下载视频到u盘上BT3U盘版下载软件类型:国产软件授权方式:免费软件界面语言:简体中文软件大小:783M文件类型:.iso运行环境:Win2003,WinXP,Win2000,Win9X软件等级:★★★★★发布时间:2010-12-26官方网址:http://www.backtrack-linux.org演示网址:http://www.backtrack-linux.org下载次数:

发表回复

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

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