本文主要解决以下几个问题

  • String源码解析?
  • String和new String的区别?
  • String通过“+”或concat累加时的对象创建机制?
  • StringBuilder和StringBuffer?区别和联系?

String源码解析

String类的定义

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence { ...... }

通过上面的代码可以得出以下结论:

  • String类使用final关键字修饰,表示String类是最终类,不可以被继承;
  • String类是CharSequence的一个实现类;
  • String类实现了Serializable接口,表示String类可以被序列化;
  • String类实现了Comparable接口,表示String类对象之间可以直接进行比较

String#value

/** The value is used for character storage. */
private final char value[];
  • String中的value属性用于字符存储;
  • String中的value属性使用final关键字修饰,表示value不可变

由于value不可变,因此,String对象具有以下两个特点:

  • String对象地址不可变(Immutable)
  • String对象是线程同步的(can be shared)

String#concat()

public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}

不管上面对字符串进行了什么处理,只看最后一行,返回了一个new String,因此,concat()方法实际上是返回了一个新的字符串。也就是说,每次调用concat()方法时,concat()方法中的参数会生成一个新的字符串变量,执行后得到的新字符串又是一个新的字符串变量。使用“+”进行字符串累加时也是一样的机制。

String和new String的区别

Java虚拟机中用于存储的区域分为栈、堆和方法区,方法区中包括另一片区域,称为常量池。String的存储主要涉及到的存储区域就是堆、栈和常量池。

栈中主要存储的是简单类型的变量和引用类型变量的存储地址;堆中存储的是引用类型数据本身;常量池中存储的是常量。

String变量的声明和定义方式有两种:

  • String str1 = new String("mystr");
  • String str2 = "mystr";

以上两种创建String对象的方式的区别是:

  • 运行时机的不同:第一种方式中有new关键字,是在程序运行期间才会执行的;而第二种方式是在编译期间就执行了;
  • 创建对象过程的不同:使用第一种方式创建String对象时,首先在堆中创建一个String对象,然后在栈中开辟一片空间,指向堆中的这一片区域,最后去常量池中寻找是否存在这个字符串常量,如果不存在,则在常量池中创建一个;使用第二种方式创建String对象时,会直接去常量池中寻找是否有这个字符串常量,如果有则直接引用到栈中,如果没有则先创建一个再引用到栈中。
  • 通过上一点可以得出结论:通过第一种方式创建String对象之后,在堆中、常量池中分别有一个对象,因此是创建了一个或两个对象;通过第二种方式创建String对象之后,在常量池中有一个对象,因此是创建了零个或一个对象;
  • 使用第一种方式,栈中的指针指向的是堆中的对象;使用第二种方式,栈中的指针指向的是常量池中的对象

StringBuilder和StringBuffer

前面说到,使用“+”和concat()方法对字符串进行拼接的时候,会创建额外的字符串变量,因此,当我们需要进行字符串拼接的时候,比较提倡的方法是使用StringBuilder或StringBuffer。

StringBuilder和StringBuffer中都提供了append()方法,可以实现字符串的拼接。

public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence

StringBuilder和StringBuffer都继承自AbstractStringBuilder,其底层依然是一个char数组,但不是final类型的。

// StringBuffer # append()
public synchronized StringBuffer append(CharSequence s) {
toStringCache = null;
super.append(s);
return this;
}
// StringBuilder # append()
public StringBuilder append(CharSequence s) {
super.append(s);
return this;
}

从上面的StringBuffer和StringBuilder的append()方法的源码比较中可以发现,两个类中append()方法唯一的区别就是,StringBuffer中的append()方法添加了synchronized关键字,表示这个方法是线程同步的。

通过上面的分析得到结论:StringBuffer是线程安全的,而StringBuilder是线程不安全的。

由于StringBuilder是线程不安全的,因此StringBuilder处理字符串的性能要比StringBuffer高。

【JAVA - 基础】之String存储机制浅析的更多相关文章

  1. Java基础之String、StringBuffer、StringBuilder浅析

    Java基础之String.StringBuffer.StringBuilder浅析 一.前言: 位于java.lang包下的String.StringBuilder.StringBuffer一般都是 ...

  2. Java基础之多态和泛型浅析

    Java基础之多态和泛型浅析 一.前言: 楼主看了许多资料后,算是对多态和泛型有了一些浅显的理解,这里做一简单总结 二.什么是多态? 多态(Polymorphism)按字面的意思就是“多种状态”.在面 ...

  3. Java基础之Collection与Collections浅析

    Java基础之Collection与Collections浅析 一.前言: 位于Java.util包下的Collection与Collections都是Java中重要的工具类,它们都是Java集合框架 ...

  4. Java基础篇(JVM)——类加载机制

    这是Java基础篇(JVM)的第二篇文章,紧接着上一篇字节码详解,这篇我们来详解Java的类加载机制,也就是如何把字节码代表的类信息加载进入内存中. 我们知道,不管是根据类新建对象,还是直接使用类变量 ...

  5. 【Java基础】String 相关知识点总结

    String 相关知识点总结 字符串的不可变性 概述 String 被声明为 final,因此它不可继承 在 Java8 中,String 内部使用 char 数组存储数据 public final ...

  6. Java基础之String中equals,声明方式,等大总结

    无论你是一个编程新手还是老手,提到String你肯定感觉特别熟悉,因为String类我们在学习java基础的时候就已经学过,但是String类型有我们想象的那么简单吗?其实不然,String类型的知识 ...

  7. java基础之 垃圾回收机制

    1. 垃圾回收的意义 在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象:而在Java中,当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾.JVM的 ...

  8. Java 基础之 String 类

    String String 被声明为 final,因此不能被继承.(Integer 等包装类也不能被继承) 在 java8 中,String 内部使用 char 数组 来存储数据 public fin ...

  9. Java基础-String 存储机制管理

    JVM运行的时候,将内存分为两个部分,一部分是堆,一部分是栈.堆中存放的是创建对象,而栈中存放的则是方法调用过程中的局部变量或引用.在设计JAVA字符串对象内存实现的时候,在堆中又开辟了一块很小的内存 ...

随机推荐

  1. linux安装redis及外网访问

    1.下载Redis,最新版是redis-3.2.1.tar.gz 2.上传到Linux上,解压到/usr/local/下面  ,命令:tar -zxvf redis-3.2.1.tar.gz 3.我们 ...

  2. li列表循环滚动的简单方法,无需插件,简单方法搞定

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. NOI1995 石子合并 [Luogu P1880]

    一道区间dp的模板题,这里主要记一下dp时环形数据的处理. 简略版:方法一:枚举分开的位置,将圈化为链,因此要做n次. 方法二:将链重复两次,即做一个2n-1长度的链,其中第i(i<=n)堆石子 ...

  4. LNMP+Redis

    如果要让php支持redis需要安装php-redis模块.可以再github上下载哦. https://github.com/phpredis/phpredis 配置lnmp环境,太简单了就不演示了 ...

  5. Oracle“ORA-00979:不是GROUP BY 表达式”解决方式

    今天在工作中碰到一个问题,用group by 语句进行分组时出现ORA-00979错误. 代码如下: select R.ORDER_NO, R.PRODUCT_CODE, R.REGION_NO, R ...

  6. IntelliJ IDEA 中设置左菜单字体, 编辑器字体和控制台的字体

    IntelliJ IDEA 中设置左菜单字体大小 File-Settings,然后选择appearance,下图右侧红色边框中的内容即设置菜单的字体和大小 ​ IntelliJ IDEA 中设置当前编 ...

  7. 「每日五分钟,玩转 JVM」:GC 概览

    前言 GC(Garbage Collection)是我们在学习 JVM 的过程中不可避免的一道坎,接下来,我们就来系统的学习一下 GC. 做一件事情之前,我们一定要去知道我们为什么要去做,这里不仅仅指 ...

  8. linux No module named yum错误的解决办法

    linux No module named yum错误的解决办法 肯定是yum的版本与当前python的版本不一致造成的 <pre>所以修改yum的配置,修改文件: vim /usr/bi ...

  9. 关于Struts.xml的路径配置以及对应的连接路径问题

    主要记住一点!: 1.   namespace的路径配置例如:/x:   2.  name名自由配置(主要用来是做action跳转方法用的) 3.  form表单中提交action时以这个格式提交(此 ...

  10. 重写(OverRide)/重载(Overload)

    方法的重写规则 参数列表必须完全与被重写方法的相同: 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同): ...