---
# System prepended metadata

title: '[Java] 字串'
tags: [Java, 轉職]

---



# [Java] 字串

## 目錄

- [目錄](#%E7%9B%AE%E9%8C%84)
- [前言](#%E5%89%8D%E8%A8%80)
- [字串基礎](#%E5%AD%97%E4%B8%B2%E5%9F%BA%E7%A4%8E)
- [字串的特性](#%E5%AD%97%E4%B8%B2%E7%9A%84%E7%89%B9%E6%80%A7)
    - [字串常量與字串池](#%E5%AD%97%E4%B8%B2%E5%B8%B8%E9%87%8F%E8%88%87%E5%AD%97%E4%B8%B2%E6%B1%A0)
    - [不可變動Immutable字串](#%E4%B8%8D%E5%8F%AF%E8%AE%8A%E5%8B%95immutable%E5%AD%97%E4%B8%B2)


## 前言

在 Java 中，字串代表一組字元，是 java.lang.String 類別的實例，Java 中的物件特性他都具備，不過字串有一些特殊的特性，是為了效能考量而設計的。

## 字串基礎

所謂字串，就是由多個得字元組成的序列，例如 "Hello" 就是一個字串，由 H、e、l、l、o 這幾個字元組成。
在 Java 中，我們使用`"`來表示字串，例如：

```java
String str = "Hello";
```

也因為他是字元組成的序列，所以我們可以查看它的長度，或是取得其中的字元。

```java
String str = "Hello";

System.out.println(str.length()); // 5
System.out.println(str.charAt(0)); // H
```

## 字串的特性

### 字串常量與字串池

開始前我們先來回達兩個問題

1. 以下的程式片段結果是 `true` 還是 `false`？
    ```java
    char[] a = {'H', 'e', 'l', 'l', 'o'};
    String str1 = new String(a);
    String str2 = new String(a);
    System.out.println(str1 == str2);
    ```
    答案是 `false`，因為 `new` 關鍵字會在記憶體中建立一個新的物件，所以 `str1` 和 `str2` 是不同的物件。
2. 以下的程式片段結果是 `true` 還是 `false`？
    ```java
    String str1 = "Hello";
    String str2 = "Hello";
    System.out.println(str1 == str2);
    ```
    答案是 `true`，因為 Java 會將所有的字串常量放在字串池中，當我們宣告一個字串時，只要他的`序列`相同、`大小寫`相同，無倫他們出現過多少次，JVM 都只會建立一個 String 物件放在字串池中，如果你又建立了一個相同的字串，JVM 會先檢查字串池中是否已經有這個字串，如果有就直接返回這個字串的參考，不會再建立一個新的物件。如果沒有才會建立一個新的物件。

使用 `""` 來宣告字串，我們就稱他為字串常量(String Literal)，字串常量是不可變動(Immutable)的，也就是說一旦建立了字串常量，他的值就不能再改變了。

而這邊我們就要特別注意，如果你想要比較兩個字串是否相等，因為前面提到的字串池及使用`new`來建立字串物件，所以我們不能使用 `==` 來比較兩個字串是否相等，因為 `==` 是比較兩個物件的參考是否相等，而不是比較兩個物件的值是否相等，所以我們應該使用 `equals()` 方法來比較兩個字串是否相等。

```java
String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");
String str4 = new String("Hello");

System.out.println(str1 == str2); // true
System.out.println(str1.equals(str2)); // true
System.out.println(str1 == str3); // false
System.out.println(str1.equals(str3)); // true
System.out.println(str3 == str4); // false
```

### 不可變動(Immutable)字串

字串常量是不可變動的，這是因為 Java 的效能考量，當我們對字串做修改時，實際上是建立了一個新的字串物件，而原本的字串物件並沒有被改變，這樣會造成記憶體的浪費，所以 Java 設計成字串是不可變動的，這樣就不用擔心字串物件被修改。

```java
String str = "Hello"; // 建立一個字串物件
str = str + " World"; // 在字串池中建立一個新的字串物件，也就是說現在有兩個字串物件，一個是 "Hello"，一個是 "Hello World"
System.out.println(str); // Hello World
```

我們來寫個程式來看看。從耗費記憶體的方式到效能考量的方式。輸出1+2+3...+100的字串

```java
public class Main {
    public static void main(String[] args) {
        String str = "";
        for (int i = 1; i <= 100; i++) {
            str += i;
        }
        System.out.println(str);
    }
}
```

但以上的程式碼是不好的，因為每次迴圈都會建立一個新的字串物件，這樣會造成記憶體的浪費，所以我們應該使用 StringBuilder 或 StringBuffer 來處理字串。

```java
public class Main {
    public static void main(String[] args) {
        StringBuilder str = new StringBuilder();
        for (int i = 1; i <= 100; i++) {
            str.append(i);
        }
        System.out.println(str.toString());
    }
}
```

在 Java 中，StringBuilder 和 StringBuffer 都是用來創建和操作可變的字串。它們的 API 都非常相似，但主要的區別在於 StringBuffer 是線程安全的，而 StringBuilder 不是。

- StringBuilder：
    - 線程不安全
    - 非同步、效能較好
    - 如果單線程，建議使用 StringBuilder
- StringBuffer：
    - 線程安全
    - 同步、效能較差
    - 如果多線程，建議使用 StringBuffer


## 字串常用方法

Java 中的字串有很多常用的方法，我們直接使用程式碼來看看。

```java
String str = "Hello";

System.out.println(str.length()); // 5, 取得字串長度
System.out.println(str.charAt(0)); // H, 取得指定位置的字元
System.out.println(str.substring(1)); // ello, 取得指定位置之後的子字串
System.out.println(str.substring(1, 3)); // el, 取得指定位置之間的子字串
System.out.println(str.indexOf("l")); // 2, 取得指定字元第一次出現的位置
System.out.println(str.lastIndexOf("l")); // 3, 取得指定字元最後一次出現的位置
System.out.println(str.startsWith("H")); // true, 判斷字串是否以指定字元開頭
System.out.println(str.endsWith("o")); // true, 判斷字串是否以指定字元結尾
System.out.println(str.equals("Hello")); // true, 判斷字串是否與指定字串相等
System.out.println(str.equalsIgnoreCase("hello")); // true, 忽略大小寫判斷字串是否相等
System.out.println(str.toLowerCase()); // hello, 將字串轉換為小寫
System.out.println(str.toUpperCase()); // HELLO, 將字串轉換為大寫
System.out.println(str.trim()); // Hello, 移除字串前後的空白
System.out.println(str.replace("H", "h")); // hello, 取代字串中的字元
System.out.println(str.contains("l")); // true, 判斷字串是否包含指定字元
System.out.println(str.concat(" World")); // Hello World, 連接字串
System.out.println(str + " World"); // Hello World, 連接字串
```