杰瑞科技汇

Java中String属于基本数据类型吗?

String 是什么?—— 核心概念

String(字符串)是 Java 中用于表示文本数据的类型,它是一个对象,而不是像 int, double 这样的基本数据类型,这意味着你可以调用 String 对象的方法来操作字符串内容。

Java中String属于基本数据类型吗?-图1
(图片来源网络,侵删)
String greeting = "Hello, World!";
int length = greeting.length(); // 调用 length() 方法获取字符串长度
System.out.println(length); // 输出 13

String 的两大核心特性

理解 String 的关键在于掌握以下两个特性:

a) 不可变性

这是 String 最重要、最核心的特性,一旦一个 String 对象被创建,它的内容就不能被改变

如何实现不可变性?

  1. private final char[] valueString 类内部使用一个 finalchar 数组来存储字符。final 关键字意味着这个数组引用一旦指向一个数组,就不能再指向其他数组。
  2. 所有修改操作(如 substring, replace, concat 等)都不会修改原始的 String 对象,它们会创建并返回一个新的 String 对象,原始对象保持不变。

示例:

Java中String属于基本数据类型吗?-图2
(图片来源网络,侵删)
String original = "hello";
// toUpperCase() 不会改变 original,而是返回一个新的字符串
String modified = original.toUpperCase();
System.out.println("Original: " + original); // 输出: Original: hello
System.out.println("Modified: " + modified); // 输出: Modified: HELLO

过程解析:

  1. String original = "hello"; 在内存中创建了一个 String 对象,内容是 "hello"。
  2. original.toUpperCase(); 方法被调用,但这个方法不会去修改 "hello" 这个对象。
  3. 相反,它会创建一个新的 String 对象,内容是 "HELLO",并将这个新对象的引用赋给变量 modified
  4. 变量 original 仍然指向最初的 "hello" 对象。

不可变性的优点:

  • 线程安全不能被改变,所以多个线程可以同时访问一个 String 对象而无需担心数据被意外修改,无需额外的同步措施。
  • 安全性:在处理文件路径、数据库URL、网络地址等敏感信息时,不可变性可以防止这些信息在程序中被意外篡改。
  • 性能优化不变,String 对象可以被共享和缓存,Java 有一个叫做 字符串常量池 的机制来优化内存使用。

b) 字符串常量池

这是一个特殊的内存区域,专门用于存储字符串字面量。

工作原理: 当你使用双引号 创建一个字符串时,JVM 首先会检查字符串常量池中是否已经存在一个完全相同的字符串。

  • 如果存在:JVM 不会创建新对象,而是直接返回池中已有对象的引用。
  • 如果不存在:JVM 会在池中创建这个字符串对象,然后返回它的引用。

示例:

String s1 = "hello";
String s2 = "hello";
// s1 和 s2 指向的是同一个对象
System.out.println(s1 == s2); // 输出 true (== 比较的是内存地址)
System.out.println(s1.equals(s2)); // 输出 true (.equals() 比较的是内容)

使用 new 关键字创建 String 如果你使用 new 关键字,则会强制在堆内存中创建一个新的 String 对象,不会检查也不使用字符串常量池。

示例:

String s3 = new String("hello");
String s4 = new String("hello");
// s3 和 s4 都是在堆上创建的新对象,它们彼此不同,也与池中的 "hello" 不同
System.out.println(s3 == s4); // 输出 false
System.out.println(s3.equals(s4)); // 输出 true

String 的常用方法

String 类提供了非常丰富的方法来处理字符串。

方法 描述 示例
length() 返回字符串的长度。 "abc".length() 返回 3
charAt(int index) 返回指定索引处的字符。 "abc".charAt(1) 返回 'b'
substring(int beginIndex) 返回从 beginIndex 开始到结尾的子字符串。 "hello".substring(1) 返回 "ello"
substring(int beginIndex, int endIndex) 返回从 beginIndex 开始到 endIndex (不包含) 的子字符串。 "hello".substring(1, 3) 返回 "el"
indexOf(String str) 返回子字符串 str 第一次出现的索引,如果不存在则返回 -1 "hello world".indexOf("world") 返回 6
lastIndexOf(String str) 返回子字符串 str 最后一次出现的索引。 "aabbcc".lastIndexOf("b") 返回 3
toUpperCase() / toLowerCase() 将字符串转换为大写/小写。 "Java".toUpperCase() 返回 "JAVA"
trim() 去除字符串首尾的空白字符(空格, tab, 换行等)。 " hello ".trim() 返回 "hello"
replace(char old, char new) / replace(CharSequence old, CharSequence new) 替换所有匹配的字符或子串。 "hello".replace('l', 'p') 返回 "heppo"
split(String regex) 根据给定的正则表达式分割字符串,返回一个字符串数组。 "a,b,c".split(",") 返回 ["a", "b", "c"]
startsWith(String prefix) / endsWith(String suffix) 判断字符串是否以指定前缀/后缀开头/ "filename.txt".endsWith(".txt") 返回 true
equals(Object anObject) 比较两个字符串的是否相等。 "abc".equals("abc") 返回 true
equalsIgnoreCase(String anotherString) 比较两个字符串的内容是否相等,忽略大小写。 "ABC".equalsIgnoreCase("abc") 返回 true
compareTo(String anotherString) 按字典顺序比较两个字符串,返回负数、零或正数。 "apple".compareTo("banana") 返回负数

vs .equals() 的区别 这是一个非常经典的面试题。

  • 比较的是两个对象的内存地址(引用是否相同)。
  • .equals()String 类重写了 Objectequals() 方法,它比较的是两个字符串的是否相同。
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
System.out.println(s1 == s2);      // true (都指向池中的同一个对象)
System.out.println(s1.equals(s2));  // true (内容相同)
System.out.println(s1 == s3);      // false (s3 在堆中是新对象)
System.out.println(s1.equals(s3));  // true (内容仍然相同)

StringStringBuilderStringBuffer

当你在循环中频繁拼接字符串时,直接使用 号会非常低效,因为每次拼接都会创建一个新的 String 对象。

低效示例:

String result = "";
for (int i = 0; i < 1000; i++) {
    result = result + i; // 每次循环都创建一个新的 String 对象
}

这会导致大量的内存分配和垃圾回收,性能很差。

这时,你应该使用 StringBuilderStringBuffer

StringBuilder

  • 特点:可变的、非线程安全的。
  • 性能:因为它没有同步开销,所以它的拼接操作(append())速度最快。
  • 适用场景单线程环境下进行大量的字符串修改操作,例如在循环中拼接字符串,这是最常用的选择。

高效示例:

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i); // 在原有的对象上进行修改,不创建新对象
}
String result = sb.toString(); // 最后一次性转换为 String

StringBuffer

  • 特点:可变的、线程安全的。
  • 性能:它的方法(如 append())都使用了 synchronized 关键字进行同步,以保证线程安全,它的性能比 StringBuilder 稍慢。
  • 适用场景多线程环境下,如果多个线程需要同时修改同一个字符串缓冲区。

如何选择?

  • 默认使用 StringBuilder:在绝大多数情况下,我们都在单线程中工作,StringBuilder 的性能优势是明显的。
  • 只有在明确需要线程安全时才使用 StringBuffer:在 Web 应用中处理共享的请求或响应数据时。

特性 描述
本质 String 是一个 final 类,代表一个字符序列。
不可变性 String 对象创建后其内容不可改变,所有修改操作都返回新对象。
字符串常量池 优化内存机制,存储字符串字面量,避免重复创建相同内容的字符串。
创建方式 String s = "hello"; (可能使用池) vs String s = new String("hello"); (强制在堆上创建)
常用方法 length(), charAt(), substring(), indexOf(), replace(), split(), equals() 等。
vs equals() 比较地址,equals() 比较内容。
性能优化 频繁修改字符串时,使用 StringBuilder (单线程) 或 StringBuffer (多线程) 代替 拼接。

掌握 String 的这些核心概念,对于编写高效、健壮的 Java 代码至关重要。

分享:
扫描分享到社交APP
上一篇
下一篇