首页 > 编程语言 >Java String

Java String

时间:2024-01-24 23:13:44浏览次数:34  
标签:Java String 对象 StringBuffer 字符串 new Pool

String

概览

String 被声明为 final,因此它不可被继承。

内部使用 char 数组存储数据,该数组被声明为 final
这意味着 value 数组初始化之后就不能再引用其它数组。
并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

不可变的好处

1. 可以缓存 hash 值

因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。
不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。

2. String Pool 的需要

如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。
只有 String 是不可变的,才可能使用 String Pool。

3. 安全性

String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 对象的那一方以为现在连接的是其它主机,而实际情况却不一定是。

4. 线程安全

String 不可变性天生具备线程安全,可以在多个线程中安全地使用。

Program Creek : Why String is immutable in Java?

String, StringBuffer and StringBuilder

1. 可变性

  • String 不可变
  • StringBuffer 和 StringBuilder 可变

2. 线程安全

  • String 不可变,因此是线程安全的
  • StringBuilder 不是线程安全的
  • StringBuffer 是线程安全的,内部使用 synchronized 进行同步

3. 性能

  • Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢

  • StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用

  • StringBuilder每次都会对StringBuilder对象本身进行操作,而不是生成新的对象并改变对象引用。
    相同情况下使用StirngBuilder相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却冒多线程不安全的风险。

三者使用的总结

  • 操作少量的数据,使用String
  • 单线程操作字符串缓冲区下操作大量数据,使用StringBuilder
  • 多线程操作字符串缓冲区下操作大量数据,使用StringBuffer

了解: String和StringBuffer的相互转换

(1)String --> StringBuffer
方式一:构造方法 StringBuffer sb=new StringBuffer(s);

方式二:通过append()方法 StringBuffer sb=new StringBuffer(); sb.append(s);

(2)StringBuffer --> String

方式一:构造方法 String s=new String(buffer);

方式二:通过toString()方法 String s=buffer.toString();

String Pool

字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确定。

不仅如此,还可以使用 String 的 intern() 方法在运行过程中将字符串添加到 String Pool 中

当一个字符串调用 intern() 方法时,如果 String Pool 中已经存在一个字符串和该字符串值相等(使用 equals() 方法进行确定),
那么就会返回 String Pool 中字符串的引用;
否则,就会在 String Pool 中添加一个新的字符串,并返回这个新字符串的引用。

下面示例中,s1 和 s2 采用 new String() 的方式新建了两个不同字符串,
而 s3 和 s4 是通过 s1.intern() 方法取得一个字符串引用。
intern() 首先把 s1 引用的字符串放到 String Pool 中,然后返回这个字符串引用。
因此 s3 和 s4 引用的是同一个字符串。

String s1 = new String("aaa");
String s2 = new String("aaa");
System.out.println(s1 == s2);           // false
String s3 = s1.intern();
String s4 = s1.intern();
System.out.println(s3 == s4);           // true

如果是采用 "bbb" 这种字面量的形式创建字符串,会自动地将字符串放入 String Pool 中。

String s5 = "bbb";
String s6 = "bbb";
System.out.println(s5 == s6);  // true

在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。
而在 Java 7,String Pool 被移到堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。

new String("abc")

使用这种方式一共会创建两个字符串对象(前提是 String Pool 中还没有 "abc" 字符串对象)。

  • "abc" 属于字符串字面量,因此编译时期会在 String Pool 中创建一个字符串对象,指向这个 "abc" 字符串字面量;
  • 而使用 new 的方式会在堆中创建一个字符串对象。

创建一个测试类,其 main 方法中使用这种方式来创建字符串对象。

public class NewStringTest {
    public static void main(String[] args) {
        String s = new String("abc");
    }
}

使用 javap -verbose 进行反编译,得到以下内容:

// ...
Constant pool:
// ...
   #2 = Class              #18            // java/lang/String
   #3 = String             #19            // abc
// ...
  #18 = Utf8               java/lang/String
  #19 = Utf8               abc
// ...

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=2, args_size=1
         0: new           #2                  // class java/lang/String
         3: dup
         4: ldc           #3                  // String abc
         6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
         9: astore_1
// ...

在 Constant Pool 中,#19 存储这字符串字面量 "abc",#3 是 String Pool 的字符串对象,它指向 #19 这个字符串字面量。在 main 方法中,0: 行使用 new #2 在堆中创建一个字符串对象,并且使用 ldc #3 将 String Pool 中的字符串对象作为 String 构造函数的参数。

以下是 String 构造函数的源码,可以看到,在将一个字符串对象作为另一个字符串对象的构造函数参数时,并不会完全复制 value 数组内容,而是都会指向同一个 value 数组。

public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

了解:说说String s=new String("aaa")和String s="aaa"的区别?

  • 前者会创建一个或者多个对象
  • 后者只会创建一个或者零个对象

参考:

标签:Java,String,对象,StringBuffer,字符串,new,Pool
From: https://www.cnblogs.com/i9code/p/17986068

相关文章

  • String 类和常量池
    1、String对象的两种创建方式Stringstr1="abcd";Stringstr2=newString("abcd");System.out.println(str1==str2);//false这两种不同的创建方法是有差别的:第一种方式是在常量池中获取对象("abcd"属于字符串字面量,因此编译时期会在常量池中创建一个字符串对象);第......
  • Java内存分配与回收策略
    HotSpot虚拟机GC分类针对HotSpot虚拟机的实现,GC可以分为2大类:部分收集(PartialGC)新生代收集(MinorGC/YoungGC):回收新生代,因为新生代对象存活时间很短,因此MinorGC会频繁执行,执行的速度一般也会比较快。老年代收集(MajorGC/OldGC):只对老年代进行垃圾收集。需......
  • java类文件结构
    类文件概述JVM可以理解的代码就叫做字节码(即扩展名为.class的文件,即类文件),它不面向任何特定的处理器,只面向虚拟机。Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。字节码并不针对一种特定的机器,因此Java......
  • java垃圾收集
    垃圾回收的脑图垃圾收集主要是针对堆和方法区进行。程序计数器、虚拟机栈和本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收。判断一个对象是否可被回收1.引用计数算法为对象添加一个引用计数器,当对象增......
  • Java学习日记 Day10
    Spring框给架:AOP:AOP切面编程一般可以帮助我们在不修改现有代码的情况下,对程序的功能进行拓展,往往用于实现日志处理,权限控制,性能检测,事务控制等。JDBCtemplate:我们做好配置文件以及加入依赖后可以通过JDBCtemplate技术来简化对数据库的操作。Spring事务:实现方式主要是注......
  • JAVA XStream简单使用
    XStreamxStream=newXStream(newStaxDriver());xStream.addPermission(AnyTypePermission.ANY);//不加后面转实体变异常xStream.processAnnotations(resultDTO.getClass());resultDTO=(ResultDTO)xStream.fromXML(result); 1、实体类设置别名  @XStreamAlias("Ma......
  • JavaScript ES6中 module , import和export
      假如你想直接在html的script里面使用import,你会遇到以下这两个问题:需要给script标签添加type='module'属性会遇到跨域问题,不单独启用一个服务器无法解决如果不启动一个server,访问js用的协议是file,不在浏览器跨域允许的协议中。因此无法做到拿到js文件,......
  • Java基础(一)
    文章目录第一章、java环境搭建1.1、Java介绍1.1.1、Java开发平台1.1.2、Java开发环境搭建1.1.3、Java专业术语1.1.4、第一个Java程序1.1.5、练习1.2、main方法1.3、变量1.3.1、变量介绍1.3.2、练习1.3.3、变量名规范1.3.4、注释1.4、数据类型......
  • 韩顺平java基础-13-常用类
    韩顺平java基础-13-常用类常用类包装类分类装箱和拆箱//自动装箱intn1=100;Integerinteger=intn1;//自动调用Integer.valueOf(n1)的方法//自动拆箱intn2=integer;//自动调用integer.intValue的方法包装类与String转换包装类型--->String类toString()......
  • linux 安装多版本java,并可切换版本
    我多版本切换,省事操作。配置:/etc/profileexportJAVA_HOME=/usr/java/jdk/jdk1.8.0_201exportJRE_HOME=/usr/java/jdk/jdk1.8.0_201/jreexportCLASSPATH=.:$JAVA_HOME/lib:$JRE_HOME/lib:$CLASSPATHexportPATH=$JAVA_HOME/bin:$JRE_HOME/bin:$JAVA_HOME:$PATH#切换1.8......