Java 學習筆記 09 – 常用類別套件


Posted by vickyh1315 on 2024-05-14

Java SE 提供官方 API,而 API 由許多類別組成,包在套件給其他檔案引用,常見套件像是 java.utiljava.mathjava.lang,分別有 ScannerBigDecimalString

java.util.Scanner 標準輸入

public class GuessNumber {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int answer = (int)(Math.random() * 10);
        int guessNumber = -1;
        do {
            System.out.print("Choose a number: ");
            guessNumber = sc.nextInt();
        } while (answer != guessNumber);

        System.out.print("Bingo");
    }

}

java.math.BigDecimal 高精度浮點數計算

public class SumOfNumbers() {

    public static void main(String[] args) {
        BigDecimal num1 = new BigDecimal("34.56789876");
        BigDecimal num2 = new BigDecimal("34.2234");
        BigDecimal num3 = number1.add(number2);
        System.out.println(num3);
    }

}

java.lang.String 字串類別

字串是預設引入類別,宣告字串時不用輸入 import java.lang.String 就能引入套件。
不同於 BigDecimal、Scanner 的參考物件,String 不用構造函數就能將值指定給變數,因此這邊的值稱作 String literal(字串常值)。

// "text" 稱作 String literal
String str = "text";

"text" v.s new String("text")
既然有 String str = "text"; 的簡便寫法,為什麼還要 new String("text")?

字串有不可改變的特性(Immutable)(字串是字元陣列組成),一旦變數給定字串值,值就不能改變。

// 編輯字串這動作,看起來像編輯同物件,編輯後字串其實記憶體指向的是另個物件
  // str3 ≠ str2 ≠ str1
  public class StrHandler {

      public static void main(String[] args) {
          String str1 = "It's ";
          String str2 = "A";
          String str3 = str1 + str2;
      }
  }

上面程式執行時,Java 記憶體配置先後產生字串物件「It's 」、「A」,再透過運算產生新字串「It's A」。整個過程產生了三個字串,但最後面只留「It's A」,剩下的「It's 」、「A」會透過 JVM 的垃圾回收機制(Garbage Collection,縮寫 GC)處理掉。

字串使用次數極高,為減少重複耗用的資源,也有所謂的字串池(String Pool)機制。

上面例子 StrHandler 類別適用沒有字串池的記憶體配置情況,有了字串池機制,JVM 會在 Heap 中開設一塊區域給字串池,並允許這些字串(準確來說是字串常值)被重複存取。
image

public class StrHandler {

      public static void main(String[] args) {
          String str1 = "A";
          String str2 = new String("A");
          String str3 = "A";
      }
  }

改寫後的 StrHandler 記憶體配置是這樣:
image
字串池的機制是,Java 會先判斷是否字串值,是的話先去字串池找有無相同字串,有就指向該字串位址,沒有則將它增加進去。而 new String() 建立的字串在底層構建時就直接視為另個物件,並放到字串池外的 Heap 區域。因此 str1 與 str3 的位址相同,與 str2 的位址不同。

public class StrHandler2 {

      public static void main(String[] args) {
          String str1 = "A"+"A";
          String str2 = "AA";
          String str3 = new String("AA");

          System.out.println("A: " + (str1 == str2));
          System.out.println("B: " + (str1.equals(str2)));
          System.out.println("C: " + (str1 == str3));
          System.out.println("D: " + (str1.equals(str3)));
      }
  }

以 StrHandler2 類別來小結一下,A~D 分別是:
A: true(==:比較參考位址)
B: true(.equal():比較物件儲存在 Heap 上的值)
C: false
D: true

A: str1 "A"+"A" 加總以後等於 "AA",執行後先去字串池找 "AA",得到 "AA" 的位址,str2 同理,只差在 str1 多了個加總的動作,str1 跟 str2 同樣指向字串池的位址 → 相同 reference
B: 對應同個字串池的字串 → 相同 value
C: str1 對應字串池裡的 "AA",str3 對應存在 Heap 裡面但在字串池外的 "AA" → 不同 reference
D: 一樣都叫做 "AA" → 相同 value

StringBuffer, StringBuilder

String 不可改變的特性,儘管有字串池的機制,將 str1 重新賦值 "AB" 的話,仍然會建立一個新物件,重複耗用資源的情況還是在,StringBuffer / StringBuilder 即是改善耗用資源的情形。

StringBuffer
StringBuffer 屬於同步類別(同步概念到線程再討論),保障線程的安全性,適合多線程場景執行。具備可改變的特性(mutable),只要建立一個 StringBuffer 就能做到建立多個 String 連接字串的工作,會是大量連接字串時,選用的類別之一(這裡以 .append() 方法實作),但要留意因有同步特性,如果在不需要多線程複雜性的單線程場景使用,會有資源耗用且比較慢的情況出現。

public class StringBufferHandler {

    public static void main(String[] args) {
        StringBuffer sb = new StringBuffer("TEXT");
        sb.append(" 123");
        System.out.println(sb); // TEXT 123
    }

}

StringBuilder
比 StringBuffer 晚一點,從 JDK 1.5 版本開始出現,功能類似於 StringBuffer,為的是解決同步類別的缺點,裡面定義的方法跟 StringBuffer 一樣,但適合單線程場景使用,不保障線程安全;實作上如果不擔心多線程的問題,直接用 StringBuilder 速度比較快。

public class StringBuilderHandler {
      // 輸入一個句子,回傳句子倒著念的版本
      public String reverseWordsInSentence(String sentence) {

          if(sentence == null) return "INVALID";
          if(sentence.equals("")) return "";
          String[] words = sentence.split(" ");
          StringBuilder reversedSentence = new StringBuilder();
          for(int i=0; i<words.length; i++) {
              StringBuilder sb = new StringBuilder(words[i]);
              String reversedWord = sb.reverse().toString();
              reversedSentence.append(reversedWord);

              if(i<words.length-1) {
                  reversedSentence.append(" ");
              }
          }

        return reversedSentence.toString();
    }
}

常用靜態函數

假設 str 為字串變數:
回傳 int
取得指定位置字元:str.charAt([index])
取得指定字元順序(首個找到):str.indexof(char)
取得指定字元順序(最後):str.lastIndexof(char)

回傳 boolean
是否包含指定字串:str.contains(String)
是否以指定字串開頭:str.startsWith(String)
是否以指定字串結尾:str.endsWith(String)
是否為空:str.isEmpty()

回傳 String
擷取字串:str.subString([startIndex])
      subString([startIndex],[endIndex]) # 擷取不含 endIndex
轉換大寫:str.toUpperCase()
轉換小寫:str.toLowerCase()
去除首尾空白:str.trim()
合併文字:"str1".concat("str2")
     String.join("To ", "concatenate ", "the ", "Strings.")
取代文字:"str1".replace('s', '1')


#java







Related Posts

JS-[promises語法糖]-用async await來實現多個promises

JS-[promises語法糖]-用async await來實現多個promises

[ 筆記 ] 後端基礎 - PHP

[ 筆記 ] 後端基礎 - PHP

![MTR04] W2 D6 函式、參數與引數

![MTR04] W2 D6 函式、參數與引數


Comments