从JVM理解String、StringBuffer

简介: 每一次字符串的创建在JVM中都发生了什么呢?

@TOC

前言

String类是Java类中最重要也是最常用的一个大类,它包含了大量的成员方法功能十分强大,类的存在往往伴随着对象的创建,类的加载,引用类型的数据就存在地址的指向问题,而这在JVM中就有生动的体现

一.String类特性

**🟢String的对象用于保存字符串,简言之就是一组字符序列(采用Unicode编码一个字符占两个字节)
String类有很多的构造器,这决定了它在Java中的重要地位**

例如:
==🟢String(StringBuffer)String(byte, int, int,Charset)
String(StringBuilder)
String(String)String(byte, int)
String(char[])String(byte,String)
String(byte, int, int,String)String(byte, int, int)
String(char[]int, int)String(byte, Charset)
String(byte, int, int, int)String(byte)
String(char, boolean)String()String(int[, int, int)==

🟢String类实现了接口serializable这保证了String类可以串行化,实现了接口Comparable使String对象可以比较大小在这里插入图片描述🟢String是一个final类它==不能被其他类继承==(查看源码可知)
在这里插入图片描述
🟢String类存放字符串内容是因为它具有==char类型的value[]属性==
例如:在这里插入图片描述

**🟢可以注意到 这里的value[]属性是由 final来修饰的 而他是一个引用类型,这就说明字符串的地址是不可修改的
由此也派生出了两种不同的赋值方式**

二.String内存布局图

通过两种关于String类型的赋值方式来展示他的JVM内存布局图,以及字符串常量的创建过程

1.赋值方式一(🚩)

String str="Hello";
在这里插入图片描述
:先从常量池查看是否有"Hello"数据空间,如果有则直接指向;如果没有则重新创建,然后指向。==str最终指向的是常量池的空间地址==

2.赋值方式二(🚩)

String str2=new String("Hello");
在这里插入图片描述

🟢先在堆中创建空间,里面具有value属性,指向常量池的"Hello"空间。如果常量池没有"Hello",重新创建,如果有则直接通过value指向。最终指向的是堆中的空间地址。
根据以上的创建过程,不难看出方式一是==直接指向常量池中的字符串常量==,方式二则是==先指向堆中,然后根据堆中的地址指向常量池中的字符串常量==

于是就有:

String a="abc";
String b="abc";
System.out.println(a==b);
运行结果:
true

🏁上述代码的运行结果之所以为true,本质是两个引用类型比较的是地址,而a,b都指向的是常量池中的“abc”,所以他们相等

三.字符串特性

1.当常量相加时:

==编译器的优化==:字符串相加只会创建一个对象

String str="Hello"+"Java";
结合上面的内存图,我们很轻易想到,str指向了池中的“Hello”,“Java”两个对象的地址。但事实却与之相反

🏁编译器会判断常量池的对象是否有相同的指向,而在这里很显然符合,所以直接执行了如下的操作

String str="HelloJava";

事实在池中只创建了一个对象并被str指向

2.当变量相加时:

==StringBuilder的应用==

String a ="Hello";
String b ="Java";
String c = a + b;

上述代码共创建了三个对象,对象c的实现是通过堆中的对象指向池中的字符串常量
内存布局如图:
在这里插入图片描述
底层是由
`StringBuilder c=new StringBuilder();
c.append(a);
c.append(b);`
创建了StringBuilder对象,并使用append()方法实现了"c=a+b"
综上所述:
==当常量相加时——瞄准常量池==
==当变量相加时——瞄准堆==

四.String的一些常用方法

1.equals()判断字符串内容是否相等(注意有大小写之分)

       String a = "hello";
       String b = "Hello";
       System.out.println(a.equals(b));

2.equalsIgnoreCase() 将字符串与指定的对象比较(不考虑大小写)

 String name = "mike";
 System.out.println("mike".equalsIgnoreCase(name))  //name可以是别的对象的引用

3.indexOf()返回指定字母索引值

 String c = "hellojava";
 int index = c.indexOf('j');
 System.out.println(index); 

4.subString()
==①截取索引值后的字符==

   String names = "hello world";
   System.out.println(names.substring(5));

==②截取索引起指定大小的字符==

   String names = "hello world";
   System.out.println(names.substring(0, 5));//表示从索引0开始截取,截取到索引5-1=4的位置

5.toUpperCase(),toLowerCase()大小写输出字符串

  String d = "hELlo";
     System.out.println(d.toUpperCase());
     System.out.println(d.toLowerCase());

6.contact()字符串拼接

    String e = "彭于晏";
    e = e.concat("彭于晏").concat("好帅").concat("我好爱");
    System.out.println(e); 

7.toCharArray()将字符串转换成字符数组

String f = "hello";
  char[] chs = f.toCharArray();   //转成字符数组
    for (int i = 0; i < chs.length; i++) {
    System.out.println(chs[i]);
}

8.compareTo()字符串比较

 String h = "jeva";  //前大正,后大负 等反0
 String i = "java";  //前部分始终相等 长度不等就返回长度差值(看源码)
 System.out.println(h.compareTo(i));// 字符串比较 返回的是'e'-'a'=4的值

9.format()格式化字符串

 String id = "蔡徐坤";
 int age = 25;
 double score = 90;
 char sexy = '男';
 String tip = "我会唱跳rap";
 String formatStr = "我的姓名是%s年龄是%d,成绩是%.2f 性别是%c.我的特长是%s";
 String info2 = String.format(formatStr, id, age, score, sexy, tip);
 System.out.println(info2);

10.split()将一个字符串分割为子字符串,然后将结果作==为字符串数组返回==。

   String str1 = "Hello Java Good Morning";
   String[] str2 = str1.split(" "); //以空格分割字符串
   System.out.println(Arrays.toString(str2))
运行结果:[Hello, Java, Good, Morning]

五.StringBuffer

1.String VS StringBuffer

🏁顾名思义就是给String加了一个buff 相当于他的一个加强类,他的char属性没有final修饰 这是他们最大的不同,可以理解成StringBuffer的对象是一个可变的容器

String保存的是字符串常量,常量的值是不可以更改的,当我们要对String对象的内容进行修改时都是更改地址,原因是用于保存常量的char属性被final修饰

在这里插入图片描述

StringBuffer保存的是字符串里面的值可以动态更改,每次字符串常量的修改对应的是==堆中空间的扩容==,效率更高

在这里插入图片描述
通过查看源码可以发现造成这种差异的原因是因为char类型的属性没有final修饰,这也就保证了堆中的对象可以动态扩容修改,不需要再指向常量池中

2.StringBuffer常用方法

1.append()增

StringBuffer s = new StringBuffer("hello");
s.append("!");//增
s.append("Java");
System.out.println(s); 
运行结果:
hello!Java

2.delete()删

 StringBuffer s = new StringBuffer("helloJava");
 s.delete(0, 2); //删除0-2的字符
 System.out.println(s);
 运行结果:
 loJava

3.replace()改

StringBuffer s = new StringBuffer("helloJava");
s.replace(5, 8, "python"); //使用python替换5-8的字符
System.out.println(s);
运行结果:
hellopython

4.insert()插

StringBuffer s = new StringBuffer("helloJava");
s.insert(5, "!");// 在索引5之前插入!,索引值为5以及以后的字符后移
System.out.println(s);
运行结果:
hello!Java
相关文章
|
3月前
|
存储 安全 Java
【JAVA基础】String、StringBuilder和StringBuffer的区别——巨详细
String是不可变的,StringBuilder和StringBuffer是可变的。而StringBuffer是线程安全的,而StringBuilder是非线程安全的。
|
2月前
|
存储 XML 缓存
Java字符串内幕:String、StringBuffer和StringBuilder的奥秘
Java字符串内幕:String、StringBuffer和StringBuilder的奥秘
26 0
|
2天前
|
存储 编解码 算法
Java 的 String StringBuilder StringBuffer(上)
Java 的 String StringBuilder StringBuffer
22 0
|
17天前
|
移动开发 安全 Java
String、StringBuffer 、StringBuilder、StringJoiner
String、StringBuffer 、StringBuilder、StringJoiner
|
1月前
|
存储 安全 Java
String、StringBuilder、StringBuffer的区别
String、StringBuilder、StringBuffer的区别
13 0
|
1月前
|
安全 Java
针对String、StringBuffer、Stringbuilder区别及使用场景
针对String、StringBuffer、Stringbuilder区别及使用场景
|
1月前
|
安全 Java API
String和StringBuffer的区别
String和StringBuffer的区别
|
1月前
|
安全 Java API
Java String类(3):StringBulider和StringBuffer详解
Java String类(3):StringBulider和StringBuffer详解
|
1月前
|
存储 安全 Java
36、Java 中的 String、StringBuilder、StringBuffer、字符串常量池和 intern 方法
36、Java 中的 String、StringBuilder、StringBuffer、字符串常量池和 intern 方法
31 0
|
3月前
|
存储 安全 前端开发
Java中的String类与StringBuilder、StringBuffer的比较:缺点与解决办法
Java中的String类与StringBuilder、StringBuffer的比较:缺点与解决办法
54 0