前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《JAVA SE》认识String类

《JAVA SE》认识String类

作者头像
VIBE
发布2022-12-02 16:14:30
2060
发布2022-12-02 16:14:30
举报
文章被收录于专栏:算法与开发

前言

Sting类是JAVA中十分重要的一种引用数据类型,本章将深入String类内部,了解其基本用法以及常见操作,认识字符串常量池以及StringBuffer 和 StringBuilder。


一、JDK中String类的声明

在这里插入图片描述
在这里插入图片描述

为何Sring类被final修饰?

被final修饰的类无法被继承,String类不存在子类。

这样的话就可以保证所有使用JDK的人,大家用的String类都仅此一次,大家都相同。

继承的方法覆写在带来灵活性的同时,也会带来很多子类行为不一致导致的问题。

二、创建字符串

常见创建字符串的四种方式:

  1. 方式一: 直接赋值(常用) String str = “Hello World”;
  2. 方式二:通过构造方法产生对象 String str2 = new String(“Hello World”);
  3. 方式三:通过字符数组产生对象 char[] data = new char[]{‘a’, ‘b’, ‘c’}; String str = new string(data);
  4. 方式四:通过String的静态方法valueOf(任意数据类型) => 转化为字符串(常用) String str = String.valueOf(10);
在这里插入图片描述
在这里插入图片描述

三、字符串比较相等

所有引用数据类型比较是否相等时,使用equals方法比较,JDK常用类,都已经覆写了equals方法,直接使用即可。(如String 、 Integer)

引用数据类型使用 “==” 比较的仍然是数值(地址是否相等)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

equals 使用注意事项:

现在需要比较 str 和 “Hello” 两个字符串是否相等, 我们该如何来写呢?

代码语言:javascript
复制
String str = new String("Hello");
// 方式一
System.out.println(str.equals("Hello"));
// 方式二
System.out.println("Hello".equals(str));

在上面的代码中, 哪种方式更好呢? 我们更推荐使用 “方式二”. 一旦 str 是 null, 方式一的代码会抛出异常, 而方式二不会.

代码语言:javascript
复制
String str = null;
// 方式一
System.out.println(str.equals("Hello"));  // 执行结果 抛出 java.lang.NullPointerException 异
常
// 方式二
System.out.println("Hello".equals(str));  // 执行结果 false

注意事项: “Hello” 这样的字面值常量, 本质上也是一个 String 对象, 完全可以使用 equals 等 String 对象的方法。

四、字符串常量池

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

a) 直接赋值

代码语言:javascript
复制
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底层实际上会自动维护一个对象池(字符串常量池)

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

理解 “池” (pool) “池” 是编程中的一种常见的, 重要的提升效率的方式, 我们会在未来的学习中遇到各种 “内存池”, “线程池”, “数据库连接池” … 然而池这样的概念不是计算机独有, 也是来自于生活中. 举个栗子: 现实生活中有一种女神, 称为 “绿茶”, 在和高富帅谈着对象的同时, 还可能和别的屌丝搞暧昧. 这时候这个屌丝被称为 “备胎”. 那么为啥要有备胎? 因为一旦和高富帅分手了, 就可以立刻找备胎接盘, 这样 效率比较高. 如果这个女神, 同时在和很多个屌丝搞暧昧, 那么这些备胎就称为 备胎池.

b) 采用构造方法 类对象使用构造方法实例化是标准做法。分析如下程序:

代码语言:javascript
复制
String str = new String("hello");
在这里插入图片描述
在这里插入图片描述

这样的做法有两个缺点:

  1. 如果使用String构造方法就会开辟两块堆内存空间,若常量池不存在该对象,则入池,否则将销毁。
  2. 字符串共享问题. 同一个字符串可能会被存储多次, 比较浪费空间.
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们可以使用 String 的 intern 方法(见下注解)来手动把 String 对象加入到字符串常量池中:

手工入池:String类提供的intern方法

在这里插入图片描述
在这里插入图片描述

调用intern()方法会将当前字符串引用的对象保存到字符串常量池中。

a. 若当前常量池中已经存在了该对象,则不再产生新的对象,返回常量池中的String对象

b.若当前常量池中不存在该对象,则将对象入池,返回入池后的地址

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

面试题:请解释String类中两种对象实例化的区别

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

综上, 我们一般采取直接赋值的方式创建 String 对象.

五、字符串的不可变性

字符串是一种不可变对象. 它的内容不可改变.

String 类的内部实现也是基于 char[] 来实现的, 但是 String 类并没有提供 set 方法之类的来修改内部的字符数组。

在这里插入图片描述
在这里插入图片描述

因此字符串对象的内容无法改变 = 》 String类的外部无法获取这个value数组。

代码语言:javascript
复制
String str = "hello" ; 
str = str + " world" ; 
str += "!!!" ; 
System.out.println(str); 
// 执行结果
hello world!!!
在这里插入图片描述
在这里插入图片描述

六、如何修改字符串内容

那么如果实在需要修改字符串, 例如, 现有字符串 str = “Hello” , 想改成 str = “hello” , 该怎么办?

a) 常见办法: 借助原字符串, 创建新的字符串

代码语言:javascript
复制
String str = "Hello";
str = "h" + str.substring(1);
System.out.println(str);
// 执行结果
hello

b) 特殊办法: 使用 “反射” 这样的操作可以破坏封装, 访问一个类内部的 private 成员.

代码语言:javascript
复制
String str = "Hello";
// 获取 String 类中的 value 字段. 这个 value 和 String 源码中的 value 是匹配的. 
Field valueField = String.class.getDeclaredField("value");
// 将这个字段的访问属性设为 true
valueField.setAccessible(true);
// 把 str 中的 value 属性获取到. 
char[] value = (char[]) valueField.get(str);
// 修改 value 的值
value[0] = 'h';
System.out.println(str);
// 执行结果
hello

关于反射 反射是面向对象编程的一种重要特性, 有些编程语言也称为 “自省”. 指的是程序运行过程中, 获取/修改某个对象的详细信息(类型信息, 属性信息等), 相当于让一个对象更好的 “认清自己” .


为什么 String 要不可变?(不可变对象的好处是什么?)

  1. 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑何时深拷贝字符串的问题了.
  2. 不可变对象是线程安全的.
  3. 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中。

注意事项: 如下代码不应该在你的开发中出, 会产生大量的临时对象, 效率比较低

代码语言:javascript
复制
String str = "hello" ; 
for(int x = 0; x < 1000; x++) {
    str += x ; 
}
System.out.println(str);

c) 特殊办法: 更换使用StringBuilder类或者StringBuffer类- 已经和String类不是一个数据类型了.

由于String的不可更改特性,为了方便字符串的修改,JDK提供StringBuffer和StringBuilder类:

面试题:请解释String、StringBuffer、StringBuilder的区别:

  1. String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
  2. StringBuffer与StringBuilder大部分功能是相似的
  3. StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

a. 字符串反转操作,sb提供的reverse();

在这里插入图片描述
在这里插入图片描述

b.删除指定范围的数据,删除索引从start开始到end之前的内容,[start,end)

在这里插入图片描述
在这里插入图片描述

c. 插入操作,将新元素插入到sb对象中,插入后新数值的起始索引为offset

在这里插入图片描述
在这里插入图片描述

总结

指的注意的点:

  1. 了解字符串常量池, 体会 “池” 的思想.
  2. 理解字符串不可变
  3. StringBuffer 和 StringBuilder 的功能

关于字符串常用的一些操作方法,后面博主会更新的~感谢大家支持O(∩_∩)O ❤❤❤

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-08-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 一、JDK中String类的声明
  • 二、创建字符串
  • 三、字符串比较相等
  • 四、字符串常量池
  • 五、字符串的不可变性
  • 六、如何修改字符串内容
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档