认识string类

创建字符串

常见的构造 String 的方式

//方式一
String str = "Hello csdn";
// 方式二 
String str2 = new String("Hello csdn");
// 方式三 
char[] array = {'a', 'b', 'c'}; 
String str3 = new String(array); 

对于"hello" 这样的字符串字面值常量, 类型也是 String.
String 是引用类型.
对于String str = “Hello”; 这样的代码其内存布局如下:

在这里插入图片描述

java中的引用类型是在栈上开辟了一小块内存空间保存一个地址. 类似于c语言中的指针,但不能像指针那样能进行各种数字运算(指针+1), 就好像一个标签, “贴” 到一个对象上. 一个对象可以贴一个标签, 也可以贴多个.
如果一个对象上面一个标签都没有, 那么这个对象就会被 JVM 当做垃圾对象回收掉.
Java 中数组, String, 以及自定义的类都是引用类

双引号引起的字符串内容均放在堆上的常量池内,若该字符串内容已存在则不会再放在常量池(常量池jdk1.7之前是放在方法区

对于以下代码,其在内存中布局图

String str1 = "Hello"; 
String str2 = str1; 

在这里插入图片描述

既然两个str都指向同一个数据,如果我们修改str1的内容,str2会不会发生改变呢?

String str1 = "Hello"; 
String str2 = str1; 

str1 = "world";
System.out.println(str2);
// 执行结果 Hello

结果并没有发生改变。
事实上, str1 = “world” 这样的代码并不算 “修改” 字符串,
而是让 str1 这个引用指向了一个新的 String 对象

在这里插入图片描述

字符串比较

int x = 10 ;
int y = 10 ; 
System.out.println(x == y); 
// 执行结果 true 

//代码1
String str1 = "Hello"; 
String str2 = "Hello"; 
System.out.println(str1 == str2); 
// 执行结果 true    

//代码2
String str1 = new String("Hello"); 
String str2 = new String("Hello"); 
System.out.println(str1 == str2);
// 执行结果 false

我们知道"=="返回的是一个boolean值,上面的代码1和代码2看起来几乎是一模一样,为什么比较出来一个是true,一个是false呢?

我们可以通过他们的内存布局来看:
代码1
在这里插入图片描述

代码2在这里插入图片描述
实际上String 使用 “==” 比较并不是在比较字符串内容, 而是比较两个引用是否是指向同一个对象.
Java 中要想比较字符串的内容, 必须采用String类提供的equals方法.

String str1 = new String("Hello"); 
String str2 = new String("Hello"); 
System.out.println(str1.equals(str2)); 

// System.out.println(str2.equals(str1)); 或者这样写也行

// 执行结果 true 

字符串常量池

在上面的例子中, String类的两种实例化操作, 直接赋值和 new 一个新的 String.
1.直接赋值时

String str1 = "hello" ; 
String str2 = "hello" ;  
String str3 = "hello" ;  

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

其内部的存储空间如下
在这里插入图片描述
不难发现,经对象增加,但都指向同一个值,并没有开辟新的堆内存空间。
String类的设计使用了共享设计模式

在JVM底层实际上会自动维护一个对象池(字符串常量池) 如果现在采用了直接赋值的模式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存到这个对象池之中. 如果下次继续使用直接赋值的模式声明String类对象,此时对象池之中如若有指定内容,将直接进行引用如若没有,则开辟新的字符串对象而后将其保存在对象池之中以供下次使用

关于池的概念,其实就是一个同类事物集合。举个例子:
现实生活中有一种女神, 称为 “绿茶”, 在和高富帅谈着对象的同时, 还可能和别的屌丝搞暧昧. 这时候这个屌丝被 称为 “备胎”.
那么为啥要有备胎? 因为一旦和高富帅分手了, 就可以立刻找备胎接盘, 这样效率比较高. 如果这个女神, 同时在和很多个屌丝搞暧昧, 那么这些备胎就称为备胎池.

2.创建对象赋值时

String str = new String("hello") ; 

在这里插入图片描述

这样的做法有两个缺点:

  1. 如果使用String构造方法就会开辟两块堆内存空间,并且其中一块堆内存将成为垃圾空间(字符串常量 “hello” 也 是一个匿名对象, 用了一次之后就不再使用了, 就成为垃圾空间, 会被 JVM 自动回收掉).
  2. 字符串共享问题. 同一个字符串可能会被存储多次, 比较浪费空间.

用第二种方法时 我们可以使用intern()方法手工入池

String str1 = new String("hello").intern() ;  
String str2 = "hello" ;  

System.out.println(str1 == str2);  
 // 执行结果 true 

在这里插入图片描述
到这里我们就可以总结两种方法的差别了

  1. 直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用。
  2. 构造方法:会开辟两块堆内存空间,其中一块成为垃圾空间,不会自动保存在对象池中,可以使用 intern()方法手工入池。

理解字符串不可变

字符串是一种不可变对象. 它的内容不可改变. String 类的内部实现也是基于 char[] 来实现的, 但是 String 类并
没有提供 set 方法之类的来修改内部的字符数组.

String str = "hello" ;  
str = str + " world" ;  
str += "!!!" ;  

System.out.println(str);  
 
// 执行结果 hello world!!! 

形如 += 这样的操作, 表面上好像是修改了字符串, 其实不是. 内存变化如下:
在这里插入图片描述
所有并不是 String 对象本身发生改变, 而是 str 引用到了其他的对象!

如果实在需要修改字符串, 例如, 现有字符串 str = “Hello” , 想改成 str = “hello” , 该怎么办?
a) 常见办法: 借助原字符串, 创建新的字符串

String str = "Hello"; 
str = "h" + str.substring(1); 

System.out.println(str); 
 
// 执行结果 hello 

还可以使用反射来破坏封装,访问类内部的私有变量。

所以类似如下代码, 会产生大量的临时对象, 效率比较低,不应在开发中出现

String str = "hello" ;  
for(int x = 0; x < 1000; x++) {
    str += x ;  
    } 

System.out.println(str);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值