【java基础】String、StringBuffer和StringBuild 那些事

String

基本特性

  • String是一个final类,代表不可变的字符序列。
  • 字符串是常量,用双引号引起来表示。它们的值在创建之后不能更改。
  • String对象的字符内容是存储在一个字符数组value[]中的。

String的继承图

Serializable

在 Java 中,Serializable 是一个标记接口,没有任何方法需要实现。它的主要用途是用于序列化对象,使得对象可以被转换为字节流,从而可以轻松地保存到文件或通过网络发送到另一个进程或机器。

序列化是将对象的状态信息转换为可以存储或传输的形式的过程。通过实现 Serializable 接口,一个类的对象可以被序列化,这意味着它们可以被转换为字节流。然后,这些字节流可以存储到文件、数据库或通过网络发送到任何地方。之后,这些字节流可以被反序列化回其原始对象状态。

以下是一些使用 Serializable 接口的常见场景:

  1. 持久化存储:对象可以被序列化并保存到文件中,以便在程序重新启动或需要时恢复它们的状态。
  2. 网络传输:对象可以被序列化并通过网络发送到另一个进程或机器。例如,通过网络传输对象状态信息到客户端。
  3. 单元测试:在单元测试中,对象的状态信息经常需要被捕获和比较。通过序列化对象,可以将它们的状态信息保存到文件或数据库中,以便稍后进行比较。
  4. 分布式系统:在分布式系统中,对象的状态信息经常需要在不同的节点之间共享。通过序列化对象,可以将它们的状态信息发送到其他节点。

要使一个类可序列化,只需实现 Serializable 接口即可。但是,如果一个类中有不可序列化的字段(例如,字段的类型不是可序列化的),则需要在该字段上使用 transient 关键字进行标记。这样,该字段的值将不会被序列化,而是会使用默认值进行反序列化。

Comparable

在Java中,Comparable接口是一个泛型接口,用于定义对象之间的自然顺序。通过实现Comparable接口,一个类可以指定其元素的排序方式。

当一个类实现了Comparable接口,就意味着它支持某种形式的比较语义,使得可以对类的实例进行排序和比较。要实现Comparable接口,类需要实现compareTo方法,该方法用于比较当前对象与另一个对象的大小关系。

实现Comparable接口的好处是:

  1. 类型安全:使用泛型,可以在编译时捕获类型错误。
  2. 灵活性:可以在运行时改变对象的排序规则。
  3. 自动排序:可以使用Java集合框架中的排序算法(如Collections.sort())对实现了Comparable接口的对象进行排序。

下面是一个简单的示例,演示如何使用Comparable接口对一个简单的Person类进行排序:


import java.util.ArrayList;  
import java.util.Collections;  
import java.util.List;  public class Person implements Comparable<Person> {  private String name;  private int age;  public Person(String name, int age) {  this.name = name;  this.age = age;  }  @Override  public int compareTo(Person other) {  return this.age - other.age; // 按年龄升序排序  }  public static void main(String[] args) {  List<Person> people = new ArrayList<>();  people.add(new Person("Alice", 25));  people.add(new Person("Bob", 30));  people.add(new Person("Charlie", 20));  Collections.sort(people); // 对people列表进行排序  for (Person person : people) {  System.out.println(person.getName() + " " + person.getAge());  }  }  
}

在上述示例中,Person类实现了Comparable<Person>接口,并重写了compareTo方法来指定按照年龄升序排序。然后,可以使用Collections.sort()方法对Person对象列表进行排序。

CharSequence

在Java中,CharSequence 是一个标记接口,主要用于表示任何可以产生字符序列的对象,如字符串(String)、缓冲区(StringBuilder 或 StringBuffer)等。

CharSequence 接口定义了一些用于操作字符序列的方法,例如 length(), charAt(int index), subSequence(int start, int end) 等。这些方法允许你获取字符序列的长度、访问特定位置的字符以及获取子序列。

虽然 CharSequence 是一个标记接口,但它并没有强制实现类必须实现特定的方法。它的主要作用是作为一个通用的字符序列接口,以便在不同的字符序列类型之间进行转换或操作。

例如,当你需要将一个字符串转换为缓冲区时,可以使用 StringBuffer 类的构造方法,该构造方法接受一个 CharSequence 参数。同样地,当需要将缓冲区转换为字符串时,可以使用 String 类的构造方法,该构造方法接受一个 CharSequence 参数。

以下是一个简单的示例,演示了如何使用 CharSequence 接口:


String str = "Hello, World!";  
CharSequence cs = str;  // 使用CharSequence接口的方法  
int length = cs.length();  
char ch = cs.charAt(0); // 'H'  
CharSequence subSeq = cs.subSequence(7, 12); // "World"

通过实现 CharSequence 接口,不同的字符序列类型可以更加灵活地进行操作和转换。

创建String 对象的两种方式

  1. 直接赋值:String s = "涛涛之海";

这种方式它首先会先从常量池查看是否有"涛涛之海" 这个数据空间,如果有就直接指向,如果没有就创建一个”涛涛之海“这个数据空间然后指向它。注意s最终指向的是常量池的空间地址。

  1. 调用构造器 String s1= new String("涛涛之海");

这种方式则是先在堆中创建空间,里面维护了value属性,指向常量池的"涛涛之海"空间。如果常量池中没有''涛涛之海'',则重新创建,如果有就直接通过value指向。注意这里最终指向的是堆中的空间地址

StringBuffer

基本特性

  1. 线程安全,可变的字符序列。 字符串缓冲区就像一个String ,但可以修改。
  2. 字符缓冲可以安全的被多个线程使用。前提是这些方法必须进行同步
  3. 每个字符串缓冲区都有一个容量。 只要字符串缓冲区中包含的字符序列的长度不超过容量,就不必分配新的内部缓冲区数组。 如果内部缓冲区溢出,则会自动变大

StringBuffer的继承图

Appendable

在Java中,Appendable 接口是一个标记接口,用于表示一个对象可以被追加内容。它没有定义任何方法,只是作为一个类型标记来指示一个对象支持追加操作。

Appendable 接口通常与 String 构造器一起使用,以便将可变的内容追加到字符串中。通过实现 Appendable 接口,对象可以表明它支持追加操作,并提供一个用于追加内容的 append 方法。

以下是一个使用 Appendable 接口的示例:


import java.io.Appendable;  
import java.io.IOException;  public class Example implements Appendable {  private StringBuilder sb;  public Example() {  sb = new StringBuilder();  }  @Override  public Appendable append(CharSequence csq) throws IOException {  sb.append(csq);  return this;  }  @Override  public Appendable append(CharSequence csq, int start, int end) throws IOException {  sb.append(csq, start, end);  return this;  }  @Override  public Appendable append(char c) throws IOException {  sb.append(c);  return this;  }  @Override  public String toString() {  return sb.toString();  }  
}

在上面的示例中,我们创建了一个自定义的 Example 类,该类实现了 Appendable 接口。然后,我们重写了 append 方法来将内容追加到 StringBuilder 中。最后,我们提供了 toString 方法来返回追加的内容。

通过实现 Appendable 接口,我们可以使用 append 方法将内容追加到对象中,并最终将其转换为字符串。这种方式比直接使用字符串连接操作更加灵活和高效。

StringBuilder

基本特性

1)一个可变的的字符序列。提供了和SteingBuffer兼容的API。

2)StringBuilder是线程不安全的,此类设计用作简易替换为StringBuffer在正在使用由单个线程字符串缓冲区的地方。

3)StringBuilder的主要StringBuilder是append和insert方法,它们是重载的,以便接受任何类型的数据。 每个都有效地将给定的数据转换为字符串,然后将该字符串的字符附加或插入字符串构建器。

4)它的速度比StringBuffer快毕竟线程不安全换来的。

StringBuilder的继承图

实战


public class Test {public static void main(String[] args) {long startTime = 0L;long endTime = 0L;StringBuffer buffer = new StringBuffer("");startTime = System.currentTimeMillis();for (int i = 0; i < 80000; i++) {buffer.append(String.valueOf(i));}endTime = System.currentTimeMillis();System.out.println("StringBuffer的执行时间:" + (endTime - startTime));StringBuilder builder = new StringBuilder("");startTime = System.currentTimeMillis();for (int i = 0; i < 80000; i++) {builder.append(String.valueOf(i));}endTime = System.currentTimeMillis();System.out.println("StringBuilder的执行时间:" + (endTime - startTime));String text = "";startTime = System.currentTimeMillis();for (int i = 0; i < 80000; i++) {text = text + i;}endTime = System.currentTimeMillis();System.out.println("String的执行时间:" + (endTime - startTime));long startTime1 = System.nanoTime();  for (int i = 0; i < 1000000; i++) {  str1 = "123" + "456" + "789";  }  long endTime1 = System.nanoTime();  System.out.println("String Concatenation Time: " + (endTime1 - startTime1) + " nanoseconds");  long startTime2 = System.nanoTime();  for (int i = 0; i < 1000000; i++) {  str2 = new StringBuilder("123").append("456").append("789").toString();  }  long endTime2 = System.nanoTime();  System.out.println("StringBuilder Concatenation Time: " + (endTime2 - startTime2) + " nanoseconds");}}

结论

在循环中,每执行一次 “+”,都会创建一个 String 对象,因此会有大量对象创建和回收的消耗。

简单来说,在循环中对同一个字符串对象做字符串拼接,优先选择 StringBuilder。

在单次拼接中,String 更快。

我们都知道 String str1 = "123" + "456" + "789"; 其实是等同于 String str1 = "123456789";的,而 StringBuilder 反而需要多次调用 append 方法。

总结

在Java中,String、StringBuffer和StringBuilder都是用于处理字符串的类,但它们在性能和功能上有所不同。以下是它们之间的主要区别:

  1. 不可变性
    • String:字符串是不可变的,这意味着一旦创建了一个字符串,就不能更改它。如果你需要修改字符串,实际上是创建了一个新的字符串。
    • StringBuffer和StringBuilder:这两个类允许你修改字符串。
  1. 线程安全
    • String:字符串是不可变的,所以它是线程安全的。
    • StringBuffer:此类的所有公共方法都是同步的,这意味着它们是线程安全的。但是,每次调用方法都需要进行同步,这可能会降低性能。
    • StringBuilder:此类的所有公共方法都是非同步的,这意味着它是非线程安全的。如果你在多线程环境中工作,可能需要额外的同步机制。
  1. 性能
    • 对于大量字符串的修改操作(如拼接),StringBuffer和StringBuilder通常比String更高效,因为它们避免了创建大量新的字符串对象。
  1. API
    • String:提供了很多用于操作字符串的静态方法,如toUpperCase()、toLowerCase()等。
    • StringBuffer和StringBuilder:提供了更多的方法来修改字符串,如append()、insert()、delete()等。
  1. 使用场景
    • 当你需要一个不可变的字符串时,可以使用String。
    • 当你需要一个线程安全的可变字符串时,字符串存在大量的修改操作时,可以使用StringBuffer。
    • 当你需要一个非线程安全的可变字符串时,字符串存在大量的修改操作时,可以使用StringBuilder。

总之,选择使用哪个类取决于你的具体需求,例如是否需要修改字符串、是否需要考虑线程安全以及性能要求等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/416600.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Electron中苹果支付 Apple Pay inAppPurchase 内购支付

正在开发中&#xff0c;开发好了&#xff0c;写一个完整详细的过程&#xff0c;保证无脑集成即可 一、先创建一个App 一般情况下&#xff0c;在你看这篇文章的时候&#xff0c;说明你已经开发的app差不多了。 但是要上架app到Mac App Store&#xff0c;则要在appstoreconnect…

中央空调安装冷媒配管基本要求一

冷媒配管施工三原则:清洁、干燥、气密 施工流程&#xff1a; 安装机组-按施工图配管-氮气置换&钎焊-管道吹洗-气密试验-真空干燥 配管的安装 R410A冷媒配管要求 材料:磷酸脱氧无缝铜管&#xff0c;设计压力:4.0Mpa以上(运行压力比R22高约1.6倍) 洁净度要求: 杂质含量<3…

Qt QCustomPlot 绘制子轴

抄大神杰作&#xff1a;QCustomplot&#xff08;五&#xff09;QCPAxisRect进行子绘图-CSDN博客 需求来源&#xff1a;试验数据需要多轴对比。 实现多Y轴、单X轴、X轴是时间轴、X轴range联动、rect之间的间距是0&#xff0c;每个图上有legend(这里有个疑问&#xff0c;每添加…

tomcat与servlet

目录 一、Http服务器 二、tomcat 1、概念 2、tomcat解压缩文件 &#xff08;1&#xff09;bin文件夹 &#xff08;2&#xff09;conf文件夹 &#xff08;3&#xff09;logs &#xff08;4&#xff09;webapps 3、借助tomcat服务器访问网页 三、servlet 1、概念 2、s…

Bazel使用案例:构建Springboot工程

本文是关于如何使用Bazel搭建Springboot 3.1.0工程&#xff08;基于JDK17&#xff09;。为什么使用Bazel&#xff0c;而不是使用Maven或者Gradle&#xff1f;可以看我之前关于Bazel的介绍文章。 前期准备 在根目录加入.bazelversion文件&#xff0c;并加入6.2.0&#xff0c;指定…

异步编程(JS)

前言 想要学习Promise&#xff0c;我们首先要了解异步编程、回调函数、回调地狱三方面知识&#xff1a; 异步编程 异步编程技术使你的程序可以在执行一个可能长期运行的任务的同时继续对其他事件做出反应而不必等待任务完成。 与此同时&#xff0c;你的程序也将在任务完成后显示…

关于C#中的LINQ的延迟执行

简介 Linq中的绝大多数查询运算符都有延迟执行的特性,查询并不是在查询创建的时候执行,而是在遍历的时候执行 实例&#xff1a; public void Test2(){List<int> items new List<int>() { -1, 1, 3, 5 };IEnumerable<int> items2 items.Where(x > x &g…

模糊数学在处理激光雷达的不确定性和模糊性问题中的应用

模糊数学是一种用于处理不确定性和模糊性问题的数学工具&#xff0c;它可以帮助我们更好地处理激光雷达数据中的不确定性和模糊性。激光雷达是一种常用的传感器&#xff0c;用于测量目标物体的距离、速度和方向等信息。然而&#xff0c;在实际应用中&#xff0c;激光雷达所获取…

【AI的未来 - AI Agent系列】【MetaGPT】5. 更复杂的Agent实战 - 实现技术文档助手

在 【AI的未来 - AI Agent系列】【MetaGPT】2. 实现自己的第一个Agent 中&#xff0c;我们已经实现了一个简单的Agent&#xff0c;实现的功能就是顺序打印数字。 文章目录 0. 本文实现内容1. 实现思路2. 完整代码及细节注释 0. 本文实现内容 今天我们来实现一个有实际意义的Ag…

系统架构设计师

软考系统架构设计师笔记 专用的成电路&#xff08;Application Specific Integrated Circuit&#xff0c;ASIC) PTR记录&#xff1a;Pointer Record&#xff0c;常被用于反向地址解析&#xff0c;即通过IP地址查询服务器域名。 软件工程 软件开发模型 【增量模型的优点】 …

Verilog基础:强度建模(二)

相关阅读 Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm1001.2014.3001.5482 三、拥有单个强度和确定值的net型信号的线与组合&#xff08;线网多驱动&#xff09; 首先来说明一下什么叫信号拥有单个强度和确定值&#xff0c;其实如果一个ne…

Midjourney网页版

引言 基于国外的api开发开发了一款网页版的midjourney&#xff0c;文末有链接 相关资源 Midjourney官方教学资料Midjourney官网discord官网B站学习资源推荐 账号注册 获取网络访问权限 使用Midjourney的前提是计算机有外网访问权限 此处推荐两款软件,lantern的优势是免费&…