《JAVA SE》认识String类

简介: 《JAVA SE》认识String类

前言

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


一、JDK中String类的声明

在这里插入图片描述
==为何Sring类被final修饰?==

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

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

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

二、创建字符串

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

  1. 方式一: 直接赋值(常用)

String str = "Hello World";

  1. 方式二:通过构造方法产生对象

String str2 = new String(“Hello World”);

  1. 方式三:通过字符数组产生对象

char[] data = new char[]{'a', 'b', 'c'};
String str = new string(data);

  1. 方式四:通过String的静态方法valueOf(任意数据类型) => 转化为字符串(常用)

String str = String.valueOf(10);

在这里插入图片描述

三、字符串比较相等

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

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

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

equals 使用注意事项:

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

String str = new String("Hello");
// 方式一
System.out.println(str.equals("Hello"));
// 方式二
System.out.println("Hello".equals(str));

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

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) 直接赋值

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) 采用构造方法
类对象使用构造方法实例化是标准做法。分析如下程序:

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数组。

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

在这里插入图片描述

六、如何修改字符串内容

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

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

String str = "Hello";
str = "h" + str.substring(1);
System.out.println(str);
// 执行结果
hello

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

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 中。

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

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 ❤❤❤

相关文章
|
7天前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
6天前
|
存储 安全 Java
Java——String类详解
String 是 Java 中的一个类,用于表示字符串,属于引用数据类型。字符串可以通过多种方式定义,如直接赋值、创建对象、传入 char 或 byte 类型数组。直接赋值会将字符串存储在串池中,复用相同的字符串以节省内存。String 类提供了丰富的方法,如比较(equals() 和 compareTo())、查找(charAt() 和 indexOf())、转换(valueOf() 和 format())、拆分(split())和截取(substring())。此外,还介绍了 StringBuilder 和 StringJoiner 类,前者用于高效拼接字符串,后者用于按指定格式拼接字符串
11 1
Java——String类详解
|
2天前
|
安全 Java
Java StringBuffer 和 StringBuilder 类详解
在 Java 中,`StringBuffer` 和 `StringBuilder` 用于操作可变字符串,支持拼接、插入、删除等功能。两者的主要区别在于线程安全性和性能:`StringBuffer` 线程安全但较慢,适用于多线程环境;`StringBuilder` 非线程安全但更快,适合单线程环境。选择合适的类取决于具体的应用场景和性能需求。通常,在不需要线程安全的情况下,推荐使用 `StringBuilder` 以获得更好的性能。
|
2天前
|
Java 开发者
Java Character 类详解
Java中的`Character`类是`java.lang`包的一部分,用于将基本类型`char`封装为对象,并提供了丰富的静态方法来处理字符,如类型判断、大小写转换等。
|
2天前
|
Java 索引
Java String 类详解
Java 中的 `String` 类用于表示不可变的字符序列,是 Java 标准库 `java.lang` 包的一部分。字符串对象一旦创建,其内容不可更改,修改会生成新对象。
|
3天前
|
Oracle Java 关系型数据库
Java(TM) Platform SE binary 已停止工作”的解决方法
Java(TM) Platform SE binary 已停止工作”的解决方法
|
1月前
|
Java 开发者
奇迹时刻!探索 Java 多线程的奇幻之旅:Thread 类和 Runnable 接口的惊人对决
【8月更文挑战第13天】Java的多线程特性能显著提升程序性能与响应性。本文通过示例代码详细解析了两种核心实现方式:Thread类与Runnable接口。Thread类适用于简单场景,直接定义线程行为;Runnable接口则更适合复杂的项目结构,尤其在需要继承其他类时,能保持代码的清晰与模块化。理解两者差异有助于开发者在实际应用中做出合理选择,构建高效稳定的多线程程序。
45 7
|
1月前
|
Oracle 安全 Java
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
|
1月前
|
安全 Java
【Java集合类面试三】、Map接口有哪些实现类?
这篇文章介绍了Java中Map接口的几种常用实现类:HashMap、LinkedHashMap、TreeMap和ConcurrentHashMap,以及它们适用的不同场景和线程安全性。
|
3月前
|
Java
Java中,有两种主要的方式来创建和管理线程:`Thread`类和`Runnable`接口。
【6月更文挑战第24天】Java创建线程有两种方式:`Thread`类和`Runnable`接口。`Thread`直接继承受限于单继承,适合简单情况;`Runnable`实现接口可多继承,利于资源共享和任务复用。推荐使用`Runnable`以提高灵活性。启动线程需调用`start()`,`Thread`直接启动,`Runnable`需通过`Thread`实例启动。根据项目需求选择适当方式。
47 2