String详解(string的一些常用方法)

  本篇文章为你整理了String详解(string的一些常用方法)的详细内容,包含有string类详解 string的一些常用方法 stringl string.h String详解,希望能帮助你了解 String详解。

   String对象的不可变原因,String对象的内存布局,及String对象之间的执行`==`,`equals`,`+`运算时的分析。

  
String对象的不可变原因,String对象的内存布局,及String对象之间的执行==,equals,+运算时的分析。

  Author: Msuenb

  Date: 2023-02-16

  
java.lang.String类代表字符串。String 对象用于保存字符串,也就是一组字符序列。字符串是常量,它们的值在创建后不能更改。

  String 的特点

  
String 类的 char[] value 数组是 final 修饰的,说明 value 数组地址不可变,不是数组元素不可变

  由于 value 数组是 private 的,所以在 String 类外无法直接修改 value 数组的元素值(除非用反射)

  String 类中的方法涉及到 value 数组长度变化,或修改元素值,都是用用新对象来表示修改后内容

  


final char[] value = {h, e, l, l, o};

 

  // value = new char[5]; // value 的地址不可修改

  value[0] = H; // 可以修改 value 元素值

  String str = "hello";

  str = "world";

  // 不会修改 "hello", 会在池中创建一个新的String对象接受 "world" str指向这个新对象 共会创建两个对象

  

 

  
String 对象不可变,可以共享(节省内存)。Java 中把需要共享的字符串常量对象存放在常量池中。

  

String s1 = "hello";

 

  String s2 = "hello";

  System.out.println(s1 == s2); // true

  // 内存中只有一个 "hello" 对象被创建,同时被s1和s2共享

  

 

  
创建String对象

  总的来说,String 对象的创建方式有两种:直接赋值 和 使用构造器,这两种方式的机制是不一样的

  直接赋值:String str1 = "hello";

  先从常量池中查看是否有 "hello" 数据空间,如果有,直接指向;如果没有,重新创建,然后指向。

  str1 最终指向的是常量池的空间地址

  
先在堆中创建空间,里面维护了 char[] value属性。

  如果常量池中有 "hello" 数据空间,value 直接指向 "hello" 空间;如果没有,重新创建,然后指向。

  str2 最终指向的是堆中的空间地址;value 才是指向常量池的空间地址

  
String str2 = new String("hello");
 

  3. 先在堆中开辟空间,里面维护了 value 属性,str2 指向这片空间
 

  4. 检查常量池中是否有 "hello" 数据空间,有,value 直接指向 "hello" 数据空间

  
哪些字符串对象地址放入字符串常量池:

  

需要共享的字符串地址记录到字符串常量池的table表中,不需要共享的字符串对象其地址值不需要记录到字符串常量池的table表中。

 

  除了以下2种,其他的都不放入字符串常量池:

  1. "..." 字符串字面量

  2. 字符串对象.intern()的结果

  1. 直接new

  2. valueOf,copyValueOf等

  3. 字符串对象拼接:concat拼接 以及 +左右两边出现 非字符串字面量拼接

  4. toUpperCase,toLowerCase,substring,repalce等各种String方法得到的字符串

  其实下面这些方式,本质都是新new的,其地址都是指向堆空间。

  

 

  String对象的创建形式

  String对象的创建方式有两种,使用其它形式生成String对象,像valueOf()或toString(),它们在底层也是调用String的构造方法

  
public String() :其表示创建空字符序列。

   String(String original):其表示创建一个与参数相同的字符序列;也即新创建的字符串是该参数字符串的副本。

  public String(char[] value) :通过当前参数中的字符数组来构造新的String。

  public String(byte[] bytes) :通过使用默认字符集解码当前参数中的字节数组来构造新的String。也可以指定字符集

  

String str1 = new String(); // 创建一个空字符串

 

  String str2 = new String("hello");

  System.out.println(str1.equals("")); // true

  char[] value = {h, e, l, l, o}; // 字符数组

  String str3 = new String(value);

  byte[] bytes = {104, 101, 108, 108, 111}; // 字节数组

  String str5 = new String(bytes); // hello

  

 

  
static String valueOf(xx value):xx支持各种数据类型,返回各种数据类型的value参数的字符串表示形式。

  


char[] data = {h,e,l,l,o,j,a,v,a};

 

  String s1 = String.copyValueOf(data);

  String s2 = String.copyValueOf(data, 0, 5); // hello

  String s3 = String.valueOf(237); // 237

  

 

  
Student stu = new Student();

  String s2 = stu + ""; // 自动调用对象的toString(),然后与 "" 进行拼接

  

 

 

  
toString方法

  Object 类中声明了 toString() 方法,因此任意对象都可以调用 toString 方法,转为字符串类型。

  

LocalDate today = LocalDate.now();

 

  String str = today.toString();

  System.out.println(str);

  

 

  
==:比较的是两个字符串对象的地址

  boolean equals(Object obj)方法:比较是两个字符串对象的内容(String 类重写了 equals 方法)

  

String str1 = "hello";

 

  String str2 = "hello";

  String str3 = new String("hello");

  String str4 = new String("hello");1

  System.out.println(str1 == str2); // true str1 和 str2 都指向池中 "hello" 数据空间

  System.out.println(str1 == str3); // false str3 指向堆中

  System.out.println(str1.equals(str3)); // true str1 与 str3 内容都是 "hello"

  System.out.println(str3 == str4); // false str3 与 str3 指向堆中不同的内存空间

  System.out.println(str3.equals(str4)); // true

  System.out.println(str1 == str3.intern()); // true

  System.out.println(str3 == str3.intern()); // false

  // 常量池中存在 "hello" 数据空间 str2.intern() 返回 "hello" 的引用 与str1相同

  

 

  intern() 方法说明:执行str2.intern()时,会返回常量池中与 str2 内容相同的字符串常量的地址;若池中没有,则将 str2 添加到池中,并返回 str2 的引用

  内存布局分析:

   str1 和 str2 中的 value 都指向常量池中的 "hello" 数据空间,str1.value == str2.value。

  compareTo方法

  int compareTo(String str)方法:String 类型实现了 Comparable 接口, 重写了 compareTo 方法,即 String 对象支持自然排序。

  int compareTo(String str) 方法按照字符的 Unicode 编码值进行比较大小的

  

@Test

 

  public void test07() {

   String[] arr = {"java","linux","hadoop","hive","shell","flink","spark"};

   Arrays.sort(arr);

   System.out.println(Arrays.toString(arr));

  

 

  + 与 concat 的区别

  拼接两个字符串的方式有两种:连接运算符+ 和concat()方法

  

String str1 = "hello,";

 

  String str2 = "world";

  String str3 = str1 + str2;

  String str4 = str1.concat(str2);

  System.out.println(str3); // helloworld

  System.out.println(str4); // helloworld

  

 

  
"..."字符串字面量拼接,编译器直接优化为拼接后的字符串常量值。如,"abc" + "def" 优化等价于 "abcdef"

  非"..."字符串字面量拼接,编译器优化为 StringBuilder 的 append 方法,然后再把结果 toString。

  

String str1 = "hello";

 

  String str2 = "world";

  String str3 = "helloworld";

  String str4 = str1 + str2; // 非 "..." 字符串字面量拼接

  String str5 = str1 + "world"; // 非 "..." 字符串字面量拼接

  String str6 = "hello" + "world"; // "..." 字符串字面量拼接 等价于: String str6 = "helloworld";

  System.out.println(str3 == str4); // false

  System.out.println(str3 == str5); // false

  System.out.println(str3 == str6); // true

  

 

  现在使用debug方式追一下String str5 = str1 + "world";的执行过程,如下:

  
最后 str5 会指向堆中的 String 对象,value[] 指向常量池中的 "helloworld" 数据空间。

  注意:final String str1 = "hello"; // 此时 str1 完全等价于"hello"

  

final String s1 = "hello";//此时s1完全等价于"hello"

 

  final String s2 = "world";//此时s2完全等价于"world"

  String s3 = "helloworld";

  String s4 = s1 + s2;

  String s5 = s1 + "world";

  String s6 = "hello" + "world";

  System.out.println(s3 == s4);//true

  System.out.println(s3 == s5);//true

  System.out.println(s3 == s6);//true

  

 

  
str1.concat(str2):只要拼接的不是空字符串,每次都 new 一个 String

  

String str1 = "hello";

 

  String str2 = "world";

  String str3 = "helloworld";

  String str4 = str1.concat(str2); // str2 不是空串 创建一个新对象接受连接后的内容

  String str5 = str1.concat("world");

  String str6 = "hello".concat("world");

  System.out.println(str3 == str4); // false

  System.out.println(str3 == str5); // false

  System.out.println(str3 == str6); // false

  

 

  concat(String)方法源码:

   因为 String 对象是不可变对象,虽然可以共享常量对象,但是对于频繁字符串的修改和拼接操作,效率极低。因此 Java 提供了可变字符序列 StringBuilder 和 StringBuffer 类型。

  
StringBuffer和StringBuilder

  StringBuffer 与 StringBuilder 最大区别就是在于 StringBuffer 是线程安全的,支持多线程访问;StringBuilder是线程不安全的,在单线程下使用。

  StringBuilder 的效率要高于 StringBuffer,通常情况先建议使用 StringBuilder,但在要求线程安全的情况下需要使用String Buffer。

  其他方面 StringBuffer 与 StringBuilder 相似:

  
StringBuffer 和 StringBuilder 都继承自 AbstractStringBuilder,有属性 char[] value,用于存放字符串内容,

  由于 value 不是 final 类型,因此 value 数组里的内容存放在堆空间,而不是常量池。

  
StringBuffer 和 StringBuilder 有相同扩容机制:初始容量默认capacity = str.length + 16,每次扩容为capacity = 2 * capacity + 2

  
StringBuilder s = new StringBuilder();

   s.append("hello").append(true).append(a).append(12).append("world");

   System.out.println(s);

   System.out.println(s.length());

  @Test

  public void test2(){

   StringBuilder s = new StringBuilder("helloworld");

   s.insert(5, "java");

   System.out.println(s);

  @Test

  public void test3(){

   StringBuilder s = new StringBuilder("helloworld");

   s.deleteCharAt(4);

   System.out.println(s);

  @Test

  public void test4(){

   StringBuilder s = new StringBuilder("helloworld");

   s.reverse();

   System.out.println(s);

  @Test

  public void test5(){

   StringBuilder s = new StringBuilder("helloworld");

   s.setCharAt(2, a);

   System.out.println(s);

  @Test

  public void test6(){

   StringBuilder s = new StringBuilder("helloworld");

   int index = s.indexOf("owo");

   System.out.println(index);

  

 

 

  String,StringBuffer,StringBuilder效率测试

  

import org.junit.jupiter.api.Test;

 

  public class EfficiencyTest {

   @Test

   public void test() {

   long start = System.currentTimeMillis();

   stringTest();

   // stringBufferTest();

   // stringBuilderTest();

   long end = System.currentTimeMillis();

   long memory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

   System.out.println("String拼接+用时:" + (end - start));

   System.out.println("String拼接+占用内存: " + memory);

   // String StringBuffer StringBuilder

   // time 5986 5 3

   // memory 57263088 43127632 43127912

   public void stringTest() {

   String str = new String("0");

   for (int i = 0; i 50000; i++) {

   str += i;

   public void stringBufferTest() {

   StringBuffer sb = new StringBuffer("0");

   for (int i = 0; i 50000; i++) {

   sb.append(i);

   public void stringBuilderTest() {

   StringBuilder sb = new StringBuilder("0");

   for (int i = 0; i 50000; i++) {

   sb.append(i);

  

 

  以上就是String详解(string的一些常用方法)的详细内容,想要了解更多 String详解的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: