【Java SE】带你在String类世界中遨游!!!

10 阅读11分钟

引言

在本章中,我们将对Java中的字符串(String)进行初步了解,探讨它在Java程序设计中的核心地位。

Java字符串概述

字符串是编程中经常使用的数据结构,用于表示文本数据。在Java中,字符串是一个不可变的序列的字符。

示例:字符串的创建

String greeting = "Hello, World!";
System.out.println(greeting);

在这个示例中,我们创建了一个字符串并打印它。

字符串在Java中的重要性

字符串是Java编程中不可或缺的一部分,用于各种场景,包括但不限于用户输入、配置信息、文件路径等。

字符串与基本数据类型

Java的字符串与基本数据类型(如intdouble等)不同,它属于对象类型。

示例:基本类型与字符串的比较
int num = 10;
String str = "10";

// 基本类型之间的比较
if (num == 10) {
    System.out.println("The number is ten.");
}

// 字符串之间的比较
if (str.equals("10")) {
    System.out.println("The string represents ten.");
}

在这个示例中,我们展示了基本类型和字符串之间的比较。

字符串的字面量

在Java中,用双引号括起来的字符序列被称为字符串字面量。

示例:字符串字面量
String literal = "This is a string literal.";
System.out.println(literal);

这个示例展示了字符串字面量的创建。

字符串的不可变性

在Java中,字符串被设计为不可变的(Immutable),这意味着一旦创建,就不能更改字符串的内容。

不可变对象的概念

不可变对象是指其状态(内部状态)不能被修改的对象。对于字符串而言,这意味着字符串的字符序列在创建后不能被改变。

示例:尝试修改字符串

String str = "hello";
str = str + " world"; // 实际上创建了一个新的字符串对象
System.out.println(str); // 输出: hello world

在这个示例中,尽管看起来修改了str,但实际上是创建了一个新的字符串对象。

不可变字符串的优势

  1. 安全性:字符串通常用于标识,不可变性确保了它们作为标识的安全性。
  2. 线程安全:不可变对象天然线程安全,无需额外同步。
  3. 易于管理:字符串常量池的实现基于不可变性。

示例:字符串作为标识

String key1 = "username";
String key2 = "username";
System.out.println(key1 == key2); // 输出: true,因为指向同一个对象

在这个示例中,key1key2指向同一个对象。

不可变字符串的劣势

  1. 性能问题:在某些情况下,如字符串拼接,可能需要创建多个临时字符串对象,影响性能。
  2. 内存使用:不可变性可能导致内存使用增加,尤其是在大量修改字符串内容的场景。

示例:字符串拼接的性能问题

String result = "";
for (int i = 0; i < 100; i++) {
    result += i; // 每次循环都可能创建新的字符串对象
}
System.out.println(result);

在这个示例中,每次循环都可能创建一个新的字符串对象。

字符串的创建和赋值

在Java中,字符串的创建和赋值是常见的操作。理解其背后的机制对于编写高效的代码至关重要。

使用双引号和new String()的区别

在Java中,可以使用双引号直接创建字符串字面量,或者使用new String()来创建字符串对象。

示例:使用双引号创建字符串

String str1 = "Hello, World!";

这种方式会使用字符串常量池中的对象,如果常量池中已存在相同的字符串,则会直接引用。

示例:使用new String()创建字符串

String str2 = new String("Hello, World!");

这种方式会创建一个新的字符串对象,即使常量池中已存在相同的字符串。

字符串常量池的工作原理

字符串常量池是Java堆内存中的一个特殊区域,用于存储字符串字面量和等价的字符串表达式。

示例:字符串常量池的效果

String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2); // 输出: true,表明两者引用相同对象

在这个示例中,str1str2引用的是相同的对象。

字符串的国际化

字符串常量池也支持字符串的国际化,允许根据用户的地区设置来显示不同的文本。

示例:使用ResourceBundle进行国际化

ResourceBundle bundle = ResourceBundle.getBundle("messages", locale);
String message = bundle.getString("greeting");
System.out.println(message);

在这个示例中,messages是一个属性文件,包含了不同语言的字符串。

字符串的连接

字符串连接是常见的操作,但需要理解其性能影响。

示例:字符串连接

String result = "";
for (int i = 0; i < 100; i++) {
    result += i; // 每次循环都可能创建新的字符串对象
}

在这个示例中,每次循环都可能创建一个新的字符串对象,影响性能。

更好的做法:使用StringBuilder

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
    sb.append(i);
}
String result = sb.toString();

使用StringBuilder可以提高字符串连接的性能。

字符串的操作

字符串是Java中使用最频繁的数据类型之一。本章将介绍字符串的一些基本操作,包括长度获取、子串、拼接、比较等。

字符串长度

可以使用length()方法获取字符串的长度。

示例:获取字符串长度

String str = "Hello, World!";
int length = str.length();
System.out.println("Length of the string: " + length);

子串操作

使用substring(int beginIndex)substring(int beginIndex, int endIndex)获取字符串的子串。

示例:获取子串

String str = "Hello, World!";
String sub = str.substring(7, 12); // 从索引7开始到11结束
System.out.println(sub); // 输出: World

字符串拼接

使用concat(String str)方法或+操作符来拼接字符串。

示例:字符串拼接

String part1 = "Hello";
String part2 = ", World!";
String result = part1.concat(part2);
System.out.println(result); // 输出: Hello, World!

// 或者使用+
String result2 = part1 + part2;
System.out.println(result2); // 输出: Hello, World!

字符串比较

使用equals(Object another)equalsIgnoreCase(String another)方法来比较两个字符串的内容。

示例:字符串比较

String str1 = "hello";
String str2 = "Hello";

boolean areEqual = str1.equalsIgnoreCase(str2);
System.out.println("Are the strings equal (ignoring case)? " + areEqual); // 输出: true

查找和替换

使用indexOf(int ch)indexOf(String str)replace(char oldChar, char newChar)等方法进行查找和替换操作。

示例:查找和替换

String str = "Hello, World!";
int index = str.indexOf("World");
System.out.println("Index of 'World': " + index); // 输出: 7

String replaced = str.replace("World", "Java");
System.out.println(replaced); // 输出: Hello, Java!

字符串分割

使用split(Pattern)split(String regex)方法将字符串分割为数组或列表。

示例:字符串分割

String str = "item1,item2,item3";
String[] items = str.split(",");
for (String item : items) {
    System.out.println(item);
}
// 输出:
// item1
// item2
// item3

字符串的格式化

在Java中,格式化字符串是一种常见的操作,用于创建具有特定格式的字符串,例如设置日期格式、数值格式等。

使用String.format()

String.format()方法是一个灵活的字符串格式化工具,它使用格式化字符串和参数来生成结果。

示例:使用String.format()

String name = "Alice";
int age = 30;
String formatted = String.format("Name: %s, Age: %d", name, age);
System.out.println(formatted); // 输出: Name: Alice, Age: 30

格式化占位符

格式化占位符指定了如何将值插入到字符串中。常见的占位符包括:

  • %s:字符串
  • %d%i:整数
  • %f:浮点数
  • %.xf:小数点后x位的浮点数

示例:不同的格式化占位符

double pi = 3.14159;
String circleArea = String.format("Circle area: %.2f", Math.PI * 10 * 10);
System.out.println(circleArea); // 输出: Circle area: 314.16

使用printf()sprintf()

除了String.format()System.out.printf()System.out.sprintf()也可以用于格式化字符串。

示例:使用printf()

System.out.printf("Current date: %tB %te, %tY\n", Calendar.getInstance().getTime());

格式化日期和时间

Java 8引入了新的日期和时间API,可以使用java.time.format.DateTimeFormatter进行更现代的日期格式化。

示例:使用DateTimeFormatter

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

LocalDate date = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String formattedDate = date.format(formatter);
System.out.println("Formatted date: " + formattedDate); // 输出: Formatted date: 2024-01-01

格式化数值

数值格式化涉及到千位分隔符、小数点、货币符号等。

示例:使用NumberFormat

import java.text.NumberFormat;

double amount = 12345.67;
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
String formattedAmount = currencyFormatter.format(amount);
System.out.println(formattedAmount); // 输出: $12,345.67

正则表达式与字符串

正则表达式是处理字符串数据的强大工具,它在Java中通过java.util.regex包提供支持。

正则表达式基础

正则表达式使用单个字符串来描述、匹配或者变换一系列符合某个句法规则的字符串。

示例:简单的正则表达式匹配

String input = "Version 2.0.1 is available";
boolean isVersion = input.matches(".*\\d+\\.\\d+\\.\\d+.*");
System.out.println("Is version format: " + isVersion); // 输出: Is version format: true

Java中的正则表达式API

java.util.regex包中的PatternMatcher类是使用正则表达式的核心。

示例:使用PatternMatcher

String data = "John Doe <john.doe@example.com>";
Pattern pattern = Pattern.compile("\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z]{2,6}\\b");
Matcher matcher = pattern.matcher(data);

if (matcher.find()) {
    System.out.println("Found email: " + matcher.group());
}

常用正则表达式操作

  • 查找:确定一个模式是否在字符串中出现。
  • 替换:将字符串中符合某个模式的子串替换成另一个子串。
  • 分割:使用模式作为分隔符来分割字符串。

示例:正则表达式的替换操作

String text = "Hello 100, World 200";
String replaced = text.replaceAll("\\d+", "#");
System.out.println(replaced); // 输出: Hello #, World #

正则表达式的复杂性

正则表达式可以非常简洁地表达复杂的匹配规则,但也可能变得难以理解和维护。

示例:复杂的正则表达式

String ipPattern = 
  "((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}" +
  "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
Pattern ipPatternCompiled = Pattern.compile(ipPattern);

性能考虑

正则表达式的使用应考虑性能,特别是对于大量数据的处理。

示例:性能考虑

String longText = generateLongText(); // 假设这是一个很长的文本
long startTime = System.currentTimeMillis();
Pattern.compile("\\w+").matcher(longText).find();
long endTime = System.currentTimeMillis();
System.out.println("Time taken: " + (endTime - startTime) + "ms");

字符串的编码

字符串编码是程序设计中一个重要的概念,尤其是在处理国际化应用程序时。

ASCII编码

ASCII(美国标准信息交换码)是最基本的编码系统,用于表示现代英语的字符。

示例:ASCII编码范围

char asciiChar = 'A';
System.out.println((int)asciiChar); // 输出: 65,ASCII码表中的A

Unicode编码

Unicode是一个更广泛的字符集,它包含了世界上大多数语言的字符。

示例:Unicode字符

char unicodeChar = '你'; // 中文“你”
System.out.println((int)unicodeChar); // 输出: Unicode码点

UTF-8编码

UTF-8是一种Unicode的实现方式,它是一种变长编码,可以用于网络传输。

示例:UTF-8编码字符串

String utf8String = "Hello, 世界";
byte[] utf8Bytes = utf8String.getBytes(StandardCharsets.UTF_8);
System.out.println(java.util.Arrays.toString(utf8Bytes));

字符串编码转换

Java允许将字符串以不同的编码方式转换为字节序列。

示例:编码转换

String str = "Hello, World!";
byte[] bytesInUTF8 = str.getBytes(StandardCharsets.UTF_8);
byte[] bytesInASCII = str.getBytes(StandardCharsets.US_ASCII);

// 将字节序列转换回字符串
String fromUTF8 = new String(bytesInUTF8, StandardCharsets.UTF_8);
String fromASCII = new String(bytesInASCII, StandardCharsets.US_ASCII);

编码与平台依赖性

字符串的默认编码方式可能依赖于平台,因此跨平台的编码操作应该明确指定编码方式。

示例:避免平台依赖性

String content = "Some text that may contain special characters";
byte[] bytes = content.getBytes(StandardCharsets.UTF_8); // 明确使用UTF-8编码

国际化和本地化

在国际化和本地化(i18n和l10n)的应用中,字符串编码的正确处理尤为重要。

示例:国际化资源文件

# messages_en.properties
greeting=Hello, World!

# messages_zh.properties
greeting=你好,世界!

字符串的性能优化

字符串操作是Java程序中常见的活动,但不当的字符串使用会影响程序的性能。本章将探讨一些性能优化技巧。

字符串连接的性能

在循环中使用+操作符连接字符串会导致性能问题,因为每次连接都会创建一个新的字符串对象。

示例:低效的字符串连接

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

更好的做法:使用StringBuilder

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);
}
String result = sb.toString();

StringBuilderStringBuffer

StringBuilderStringBuffer都可以用来构建和修改字符串,但它们在线程安全性方面有所不同。

示例:使用StringBuilder

StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(", World");
String str = sb.toString();

示例:使用StringBuffer

StringBuffer sb = new StringBuffer();
sb.append("Hello");
sb.append(", World");
String str = sb.toString();

字符串池的使用

字符串池是Java中用于存储字符串字面量和相同内容的字符串的地方。使用字符串池可以减少内存消耗。

示例:字符串池

String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2); // 输出: true,表明两者引用相同对象

避免不必要的字符串创建

在某些情况下,可以通过重用变量或使用其他数据结构来避免创建不必要的字符串。

示例:避免不必要的字符串创建

String str = "example";
char[] charArray = new char[str.length()];
str.getChars(charArray, 0);
// 使用charArray而不是创建新的字符串

字符串的不可变性

字符串的不可变性意味着它们是安全的,可以被共享,但同时也意味着每次修改都需要创建新的对象。

示例:字符串的不可变性

String base = "example";
String modified = base + "_string"; // 创建了新字符串