String的深入理解——java学习总结(2)

1.String是不可变的字符串,它的底层是一个用final修饰的字符数组;[1]
在这里插入图片描述
在这里插入图片描述
至于这里为什么用byte[]而不用char[]

因为开发人员发现人们使用的字符串值是拉丁字符居多而之前使用的char数组每一个char占用两个字节而拉丁字符只需要一个字节就可以存储,剩下的一个字节就浪费了,造成内存的浪费,gc的更加频繁。因此在jdk9中将String底层的实现改为了byte数组。

2.String 的两种创建方式
(1)创建方式一:String a = "hello";

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

输出为true,原因如下:
String维护了一个字符串的常量池[2],当用String a = "hello"这种方式时,首先创建一个String类型的引用a,然后到常量池中需找是否有等于"hello"的字符串,有的话另a指向它,没有则在常量池中新建,然后a指向它。
所以上例中,b直接指向这个常量,a和b在内存中是同一个对象,所以a==b为true。
在这里插入图片描述

(2)创建方式2:String A = new String("hello");

String A = new String("hello");
String B = new String("hello");
System.out.println(A==B);

输出为false,原因如下:

String A = new String("hello");可以分解为两步(实际上在内部实现也是这么做的)
1、String object = "hello";
2、String A = new String(object);
第一步和创建方式一相同;
第二步是在堆内存中以object为参数传入,从而创建一个堆内存对象。
如图:
在这里插入图片描述

这就是为什么会输出false。

3.为什么会有StringBuffer和StringBuilder
当我们做拼接字符串操作的时候:

String str = "you";
str = str+"win";
底层是这样的:
在这里插入图片描述

str刚开始指向常量池中的“you”,拼接字符串“win”的时候又开辟了两块块内存空间一块保存“win”,一块保存拼接以后生成的字符串“ youwin”并且str指向拼接以后的字符串,在这个过程中一共占用了三块内存空间,所以效率是非常低下的。

所以需要引入StringBuilder和StringBuffer

4.StringBuilder和StringBuffer
StringBuilder 和 StringBuffer都继承于:AbstractStringBuilder
他们的底层使用的是没有用final修饰的字符数组:char[]

abstract class AbstractStringBuilder implements Appendable, CharSequence {
 /**
 * The value is used for character storage.
 */
 char[] value;

所以在做字符串拼接的时候就在原来的内存上进行拼接,不会浪费内存空间。

StringBuilder和StringBuffer的区别是:
StringBuilder是线程不安全的,它的执行效率比StriingBuffer要高StringBuffer是线程安全的,它的执行效率比StringBuilder要低

5、“+”拼接字符串

String s2 = "hello world"; 
String s1 = "hello "+"world";
System.out.println(s1==s2);

输出为

false

这里为什么是false呢?
因为"+"是底层通过StringBuilder.append实现的,append后new了一个新的String来存放,所以不是同一个字符串对象了

6、“+”拼接final字符串

final String s1 = "hello"; 
final String s2 = "world";
String s3 = "hello world"
String s4 = s1 + s2;
System.out.println(s3==s4);

输出为

true

s1和s2都是常量字符串。
s3和s4都是直接从字符串常量池取,所以是相同的。

[1]不可变类:例如String s =“111”,此时在内存中会先创建“111”,然后将s指向“111”,不可变是指“111”不可变。当执行s=“222”时,会先在内存中生成“222”,再将s指向“222”.
[2]常量池也位于堆内存中,通过引入字符串常量池的概念,让字符串处理的效率得到了提高,这是jvm对字符串的一种优化手段。

参考引用:https://blog.csdn.net/qq_44507430/article/details/106733368

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
String a="hello world"; //在java中有一个常量池,当创建String 类型的引用变量给它赋值时,java会到它的常量池中找"hello world"是不是在常量池中已存在。如果已经存在则返回这个常量池中的"hello world"的地址(在java中叫引用)给变量a 。注意a并不是一个对象,而是一个引用类型的变量。它里面存的实际上是一个地址值,而这个值是指向一个字符串对象的。在程序中凡是以"hello world"这种常量似的形式给出的都被放在常量池中。 String b=new String("hello world"); //这种用new关键字定义的字符串,是在堆中分配空间的。而分配空间就是由new去完成的,由new去决定分配多大空间,并对空间初始化为字符串"hello world" 返回其在堆上的地址。 通过上面的原理,可以做如下实验: String a="hello world"; String b="hello world"; String c=new String("hello world"); String d=new String("hello world"); if(a==b) System.out.println("a==b"); else System.out.println("a!=b"); if(c==d) System.out.println("c==d"); else System.out.println("c!=d"); //输出结果: a==b c!=d 为什么会出现上面的情况呢? String a="hello world"; String b="hello world"; 通过上面的讲解可以知道,a和b都是指向常量池的同一个常量字符串"hello world"的,因此它们返回的地址是相同的。a和b都是引用类型,相当于c语言里面的指针。java里面没有指针的概念,但是实际上引用变量里面放的确实是地址值,只是java为了安全不允许我们对想c语言中的那样对指针进行操作(如++ 、--)等。这样就有效的防止了指针在内存中的游离。 而对于 String c=new String("hello world"); String d=new String("hello world"); 来说是不相等的,他们是有new在堆中开辟了两块内存空间,返回的地址当然是不相等的了。如果我们要比较这两个字符串的内容怎么办呢?可以用下面的语句: if(c.equals(d)) System.out.println("c==d"); else System.out.println("c!=d"); //输出 c==d

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值