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

相关文章
|
14天前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
37 17
|
6天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
10天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
50 4
|
11天前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
26 2
|
15天前
|
存储 安全 Java
如何保证 Java 类文件的安全性?
Java类文件的安全性可以通过多种方式保障,如使用数字签名验证类文件的完整性和来源,利用安全管理器和安全策略限制类文件的权限,以及通过加密技术保护类文件在传输过程中的安全。
|
19天前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
|
19天前
|
Java API Maven
如何使用 Java 字节码工具检查类文件的完整性
本文介绍如何利用Java字节码工具来检测类文件的完整性和有效性,确保类文件未被篡改或损坏,适用于开发和维护阶段的代码质量控制。
|
19天前
|
存储 Java 编译器
java wrapper是什么类
【10月更文挑战第16天】
22 3
|
22天前
|
Java 程序员 测试技术
Java|让 JUnit4 测试类自动注入 logger 和被测 Service
本文介绍如何通过自定义 IDEA 的 JUnit4 Test Class 模板,实现生成测试类时自动注入 logger 和被测 Service。
21 5
|
22天前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
30 2

热门文章

最新文章