JAVA學習筆記 05 - 字符串

本文是Java基礎課程的第五課。主要介紹Java中的字符串,包括字符串基本的聲明、使用,字符串在JVM中的內存分配,字符串常用的API,字符串與基本數據類型的轉換等內容

一、認識字符串

字符(char)類型是Java的基本數據類型之一,用來存儲單個字符。在開發過程中,往往多個字符一起才能表達一個有意義的數據。Java提供了字符串String類型,用來處理一連串的字符。字符串便是由若干字符組成的序列

1、字符串的聲明和初始化

Java中,聲明和初始化一個字符串的語法格式如下:

String 變量名 = "初始值";
// 或
String 變量名 = new String("初始值");

下面是一個示例:

public class Test {
	public static void main(String[] args) {
		String str1 = "Hello world";
		String str2 = new String("Hello World");
	}
}

說明:

  • 一旦創建了String對象,那它的無法改變了。
  • 事實上,String類型用一個char類型數組來存儲,處理字符串的。

2、字符串的連接

多個字符串連接在一起,也可以將字符串與基本類型變量連接。最常用連接字符串的方法是使用+操作符
下面是一個示例:

public class Test {
	public static void main(String[] args) {
		String str1 = "Hello";
		String str2 = "World";
		String str3 = str1 + str2;
		String str4 = "Hello" + "World";
		int num1 = 123;
		String str5 = num1 + str1;
		String str6 = str1 + num1;
		System.out.println("str1 = " + str1);
		System.out.println("str2 = " + str2);
		System.out.println("str3 = " + str3);
		System.out.println("str4 = " + str4);
		System.out.println("str5 = " + str5);
		System.out.println("str6 = " + str6);
	}
}

說明:

  • 字符串基本數據類型變量使用+操作符進行連接時,會基本類型轉換字符串類型,然進行連接操作。

3、字符串的比較

在程序中,比較兩個數據是否相等是一種很常見的操作,基本數據類型的變量之間使用==操作符便可以判斷變量中保存的數據是否相等。而字符串之間的比較有所不同。

下面是一個示例:

public class Test {
	public static void main(String[] args) {
		String str1 = "Hello";
		String str2 = "Hello";
		String str3 = new String("Hello");
		System.out.println("(str1 == str2) = " + (str1 == str2));
		System.out.println("(str1 == str3) = " + (str1 == str3));
		System.out.println("str1.equals(str3) = " + str1.equals(str3));
	}
}

說明:

  • ==操作符比較str1str2,結果爲true,事實上,==操作符比較的是變量中存儲的內容數據內存地址編號)。字符串是引用類型,str1 == str2的結果爲true,意味着str1str2兩個變量中存儲着同樣的內存地址編號,即引用了同一塊內存(這一點在下面會詳細介紹)。
  • ==操作符比較str1str2,結果爲false,意味着str1str3兩個變量分別存儲了不同的內存地址編號。
  • String類型提供了equals(Object anObject)方法,可以比較字符串字面值是否相同,str1.equals(str3)的返回結果爲true。因此,如果要比較字符串的字面值是否相同,需要使用equals(Object anObject)方法

4、內存中的字符串

觀察下面的示例:

public class Test {
	public static void main(String[] args) {
		// 基本數據類型
		int num1 = 1;
		int num2 = 1;
		System.out.println("(num1 == num2) = " + (num1 == num2));

		// 數組,引用數據類型
		char[] char1 = {'H'};
		char[] char2 = {'H'};
		System.out.println("(char1 == char2) = " + (char1 == char2));

		// String,引用數據類型
		String str1 = "Hello";
		String str2 = "Hello";
		System.out.println("(str1 == str2) = " + (str1 == str2));
	}
}

num1num2兩個基本數據類型的變量進行==運算,這兩個變臉中存儲的都是基本數據類型的值,即1,因此返回結果是truechar1char2兩個數組進行==運算,因爲它們是引用類型,分別在堆內存中申請了內存空間,所以char1char2兩個變量中存儲的是不同的內存地址編號,故而返回結果爲false。同爲引用類型的String型的變量,str1str2使用==操作符比較時,返回的結果是true,即str1str2引用的內存地址相同,這是什麼原因呢?

事實上,這是Java有意爲之,由於字符串這一類型的數據是程序中出現頻率的數據類型,並且字符串內容重複概率也非常的,如果全部分開申請內存的話,將會非常耗費內存,Java爲了節省內存考慮,設計了字符串池這一內存區域(字符串池在JDK 7以前設計在方法區中,而在JDK 8以後設計在堆內存中),下面用圖示說明上例中兩個變量str1str2中存儲的內存地址編號爲何相同:
在這裏插入圖片描述
說明:

  1. 執行代碼String str1 = "Hello"時,首先在棧中main方法對應棧幀的局部變量表中,爲變量str1分配內存查找字符串池是否有一塊內存中已經存儲Hello這一字符串字面量沒有的話,字符串池分配內存並存儲字符串字面量"Hello"賦值操作會將字符串池中已經分配好的內存空間的首地址存儲到變量str1局部變量表對應位置
  2. 執行代碼String str2 = "Hello"時,棧中main方法對應棧幀的局部變量表中,爲變量str2分配內存查找字符串池是否有一塊內存中已經存儲Hello這一字符串字面量的話,引用該內存地址
  3. 執行代碼System.out.println("(str1 == str2) = " + (str1 == str2)),由於變量str1str2引用的內存地址相同,故返回結果爲true

進一步觀察下面的另一個例子:

public class Test {
	public static void main(String[] args) {
		String str1 = "Hello";
		String str2 = "Hello";
		String str3 = new String("Hello");
		System.out.println("(str1 == str2) = " + (str1 == str2));
		System.out.println("(str1 == str3) = " + (str1 == str3));
	}
}

前面的例子已經進行了分析,變量str1str2引用了字符串池中相同的內存地址,str1 == str2的結果爲true。運行str1 == str3的結果爲false,這又是什麼原因呢?

原因就在於使用了new運算符,下面用圖示來說明:
在這裏插入圖片描述
說明:

  • 執行代碼new String("Hello")時,會判斷字符串池是否已經存儲了Hello這一字符串字面量沒有的話,字符串池中會分配內存存儲Hello這一字符串字面量,同時,堆內存分配內存存儲Hello這一字符串字面量,最終,返回堆內存分配好的內存空間首地址。因此,變量str1str3中存儲的是不同的內存地址編號,str1 == str3的結果爲false
  • 使用new運算符時,總會堆內存分配內存空間

再來觀察一個例子:

public class Test {
	public static void main(String[] args) {
		String str1 = "Hello";
		String str2 = "HelloWorld";
		String str3 = "Hello" + "World";
		String str4 = str1 + "World";
		System.out.println("(str2 == str3) = " + (str2 == str3));
		System.out.println("(str2 == str4) = " + (str2 == str4));
	}
}

str2 == str3的結果爲true,即變量str2str3引用的內存地址相同;而str2 == str4的結果爲false,即變量str2str4引用的內存地址不同,這又是怎麼回事呢?

str2 == str3的結果爲true,原因在於字符串字面量連接,在編譯階段就已經完成了,String str3 = "Hello" + "World"String str3 = "HelloWorld",對於JVM來說並沒有什麼區別;而str2 == str4的結果爲false,是由於字符串變量參與字符串連接,這一過程發生在程序運行時,會在堆內存申請內存空間。下面用圖示來說明:
在這裏插入圖片描述
說明:

  1. 執行代碼String str2 = "HelloWorld"時,字符串池分配內存存儲HelloWorld這一字符串字面量,變量str2引用該內存地址。
  2. 執行代碼String str3 = "Hello" + "World"時,會在字符串池查找HelloWorld這一字符串字面量直接返回內存地址,故變量str3str2引用相同的內存地址。
  3. 執行代碼String str4 = str1 + "World"時,會將字符串變量str1中的字符串Hello和字符串字面量World進行連接,並在堆內存分配內存空間進行存儲,最終變量str4引用該堆內存中的內存地址。

最後來觀察一個例子:

public class Test {
	public static void main(String[] args) {
		// 引用數據類型的變量相互賦值
		int[] nums1, nums2;
		nums1 = new int[1];
		nums1[0] = 3;
		nums2 = nums1;
		nums2[0] = 4;
		System.out.println("nums1[0] = " + nums1[0]);
		// 字符串類型的變量相互賦值
		String str1, str2;
		str1 = "Hello";
		str2 = str1;
		str2 = "World";
		System.out.println("str1 = " + str1);
	}
}

打印nums1[0]的結果爲4,作爲引用類型變量,將數組nums2賦值給數組nums1時,實際拷貝的是內存地址編號,變量nums1nums2引用同一內存地址改變數組nums2元素,其實就是改變數組nums1元素。打印str1的結果爲Hello,同樣作爲引用類型,在執行str2 = str1後,字符串變量str1str2也引用同一內存地址,可是改變變量str2的值,變量str1卻沒有發生變化,這又是爲什麼呢?

事實上,在程序運行str2 = "World"之後,字符串變量str1str2也並不再引用同一內存地址了,仍然用圖示來說明:
在這裏插入圖片描述

說明:

  1. 執行代碼str1 = "Hello"時,首先查找字符串池是否有一塊內存中已經存儲Hello這一字符串字面量沒有的話,字符串池分配內存並存儲字符串字面量"Hello"賦值操作會將字符串池中已經分配好的內存空間的首地址存儲到變量str1局部變量表對應位置
  2. 執行代碼str2 = str1時,變量str2str1引用字符串池中同一內存地址。
  3. 執行代碼str2 = "World"時,首先查找字符串池是否有一塊內存中已經存儲World這一字符串字面量沒有的話,字符串池分配內存並存儲字符串字面量"World"賦值操作會將字符串池中已經分配好的內存空間的首地址存儲到變量str2局部變量表對應位置。注意,這一過程中變量str1並沒有受到影響。

二、字符串常用API

在開發工程中,經常需要對字符串進行各種操作,熟練掌握字符串的各種操作,對編碼過程很有幫助。

1、什麼是API

API(Application Programming Interface),即應用程序編程接口,對這一概念的定義大多數都晦澀難懂。這裏通俗的理解這一概念:

  • A(Application),即應用,按不同的場合,可以是一個提供特定功能的軟件一個提供某些數據的網絡服務一個作爲程序組件的類庫、甚至是一個硬件設備,等等。
  • P(Programming),即編程
  • I(Interface),即接口,是不同系統實體交接並通過它彼此作用媒介(或者理解爲方式、渠道、手段)。

所以,API就是在編程過程中實體或系統之間進行信息交換的媒介

更直白的理解,當某一應用需要與其他應用進行信息交換時,需要通過某一媒介告知對方應用一些信息,同時獲取本應用所需要信息,同時,該媒介應當隱藏這些應用的其他細節,這一媒介便是API,與這一媒介具有同樣性質事物都可以稱之爲API

開發人員在使用API時,無需關心API後面隱藏的實現細節僅需要關心API的名稱輸入什麼數據獲得什麼數據這三點就足夠了。

2、字符串常用API

Java中的字符串提供給開發人員許多非常有用的API,通過它們,可以很輕鬆的獲得字符串的一些常用信息,或者對字符串進行一些加工處理。
Java中字符串常用API有:

方法 返回值類型 方法說明
equals(Object anObject) boolean 將此字符串與指定的對象比較
equalsIgnoreCase(String anotherString) boolean 忽略大小寫比較兩個字符串的字面值是否相等
length() int 獲取字符串的長度
charAt(int index) char 獲取指定索引處的 char
indexOf(String str) int 獲取指定子字符串在原字符串中第一次出現處的索引
lastIndexOf(String str) int 獲取指定子字符串在原字符串中最右邊出現處的索引
startsWith(String prefix) boolean 測試原字符串是否以指定的前綴開始
endsWith(String suffix) boolean 測試原字符串是否以指定的後綴結束
toLowerCase() String 獲取原字符串對應的小寫字符串
toUpperCase() String 獲取原字符串對應的大寫字符串
substring(int beginIndex) String 截取原字符串,從傳入參數beginIndex爲下標的位置開始截取到末尾
substring(int beginIndex, int endIndex) String 截取原字符串,從參數beginIndex爲下標的位置開始截取到參數endIndex爲下標的位置
trim() String 去掉原字符串首尾的空格
split(String regex) String[] 將原字符串按照傳入參數regex分割爲字符串數組
replace(String regex, String replacement) String 將原字符串中指定的內容替換成另外的內容

3、案例

3.1 案例1

下面是一個示例:

public class Test {
	public static void main(String[] args) {
		// 驗證用戶名長度,長度在6至20之間爲合法,否則爲不合法
		Scanner input = new Scanner(System.in);
		System.out.println("請輸入用戶名");
		String name = input.next();
		if (name.length() >= 6 && name.length() <= 20) {
			System.out.println("用戶名長度合法");
		} else {
			System.out.println("用戶名長度不合法");
		}
	}
}

說明:

  • 該案例演示驗證用戶名的長度,需要從鍵盤上輸入用戶名,對用戶名進行驗證,合法的用戶名長度在6到20之間。如果在這區間,輸出用戶名長度合法,否則輸出用戶名長度不合法。
  • 使用字符串length()方法可以獲取字符串長度

3.2 案例2

下面是一個示例:

public class Test {
	public static void main(String[] args) {
		/*
		  從鍵盤上輸入email,對email進行驗證,合法的email的條件是
		  1. 必須包含“@”和“.”
		  2. “@”必須在“.”的前面且不能連着
		  3. “@”不在開頭和結尾,並且只能出現一次
		 */
		Scanner input = new Scanner(System.in);
		System.out.println("請輸入Email");
		String email = input.next();
		int atIndex = email.indexOf("@");
		int dotIndex = email.indexOf(".");
		if (atIndex == -1 || dotIndex == -1) { //必須包含“@”和“.”
			System.out.println("Email非法,不存在@或.");
		} else if (atIndex >= dotIndex - 1) {  //@必須在.的前面且不能連着
			System.out.println("Email非法,@必須在.的前面且不能連着");
		} else if (email.startsWith("@") || email.endsWith("@")) { //“@”不在開頭和結尾
			System.out.println("Email非法,“@”不能出現在開頭和結尾");
		} else if(email.lastIndexOf("@") != atIndex){ // “@”只能出現一次
			System.out.println("Email非法,要求@只能出現一次");
		}else {
			System.out.println("Email合法");
		}
	}
}

說明:

  • 該案例演示電子郵箱地址校驗,校驗規則爲必須包含“@”和“.”,“@”必須在“.”的前面且不能連着,“@”不在開頭和結尾,並且只能出現一次。
  • 使用字符串indexOf(String str)方法可以獲取指定子字符串原字符串第一次出現處的索引沒有找到返回-1
  • 使用字符串startsWith(String str)方法可以判斷原字符串是否以指定前綴開始
  • 使用字符串endsWith(String str)方法可以判斷原字符串是否以指定後綴結束
  • 使用字符串lastIndexOf(String str)方法可以獲取指定子字符串原字符串最後一次出現處的索引沒有找到返回-1

3.3 案例3

下面是一個示例:

public class Test {
	public static void main(String[] args) {
		// 將新聞標題中的“爪窪”換成“Java”
		String title = "爪窪技術發展這些年";
		System.out.println("替換前的標題:" + title);
		title = title.replace("爪窪", "Java");
		System.out.println("替換後的標題:" + title);
	}
}

說明:

  • 該案例演示將新聞標題中的“爪窪”換成“Java”。
  • 使用字符串replace(String regex, String replacement)方法可以將原字符串指定內容替換成另外內容

3.4 案例4

下面是一個示例:

public class Test {
	public static void main(String[] args) {
		// 截取路徑中的文件名稱
		String path = "C:\\HTML\\front\\assets\\img\\pc\\logo.png";
		int startIndex = path.lastIndexOf("\\");
		int endIndex = path.lastIndexOf(".");
		String fileName = path.substring(startIndex + 1, endIndex);
		System.out.println(path + "路徑中的文件是:" + fileName);
	}
}

說明:

  • 該案例演示截取路徑中的文件名稱。
  • 使用字符串substring(int beginIndex, int endIndex)方法可以獲取指定索引處char

3.5 案例5

下面是一個示例:

public class Test {
	public static void main(String[] args) {
		/*
		  檢測輸入的單詞是否是迴文單詞
		  迴文單詞即單詞倒過來與原單詞一樣,比如“level”、“pop”、“noon”等
		 */
		Scanner input = new Scanner(System.in);
		System.out.println("請輸入需要檢測的單詞:");
		String word = input.next();
		// 檢測迴文單詞的邏輯
		boolean isPalindromeWord = true;
		for (int i = 0; i < word.length() / 2; i++) {
			if (word.charAt(i) != word.charAt(word.length() - 1 - i)) {
				isPalindromeWord = false;
			}
		}
		System.out.println(word + (isPalindromeWord ? "是" : "不是") + "迴文單詞");
	}
}

說明:

  • 該案例演示迴文單詞檢測。
  • 使用字符串charAt(int index)方法可以從指定開始位置結束位置截取字符串

3.6 案例6

下面是一個示例:

public class Test {
	public static void main(String[] args) {
		// 字符串格式化
		String str1 = String.format("見過,%s及%s", "晁天王", "衆位頭領");
		System.out.println(str1);
		String str2 = String.format("字母a的大寫是:%c", 'A');
		System.out.println(str2);
		String str3 = String.format("3 > 7的結果是:%b", 3 > 7);
		System.out.println(str3);
		String str4 = String.format("100的一半是:%d", 100 / 2);
		System.out.println(str4);
		//使用printf代替format方法來格式化字符串
		System.out.printf("50元的書打8.5折扣是:%f 元", 50 * 0.85);
	}
}

說明:

  • 輸出結果爲:
    見過,晁天王及衆位頭領
    字母a的大寫是:A
    3 > 7的結果是:false
    100的一半是:50
    50元的書打8.5折扣是:42.500000
  • String類的format(String format, Object... args)方法用於創建格式化字符串。該方法無需String類型的變量直接使用類名String即可調用。該方法第一個參數被格式化字符串第二個參數替換格式符的字符串,第二個參數中的表示方法可變參數,即參數個數根據格式符個數來確定字符串格式化就是使用第二個可變參數中的值按照順序替換第一個參數中的格式符format(String format, Object... args)方法的格式符定義如下:
    格式符 說明 示例
    %s 字符串類型 "李逵"
    %c 字符類型 'm'
    %b 布爾類型 true
    %d 整數類型(十進制) 100
    %x 整數類型(十六進制) FF
    %o 整數類型(八進制) 77
    %f 浮點類型 99.99
  • 字符串的格式化避免了使用+來連接字符串,使得字符串的構建更方便。
  • System.out.printf(String format, Object ... args)具有相同功能,但是能用於一次輸出

三、字符串與基本數據類型的轉換

在實際開發中,經常遇到字符串類型基本類型轉換操作

1、基本數據類型轉換爲String類型

基本類型的數據轉換字符串類型有通常有兩種方法:

  1. 通過字符串連接將基本類型轉換成String類型。
  2. 通過String類型提供的valueOf(基本類型數據 變量名)方法將基本類型轉換成字符串類型。

下面是一個示例:

public class Test {
	public static void main(String[] args) {
		// 通過連接字符串將基本類型轉換爲String類型
		int num1 = 10;
		double num2 = 3.14;
		boolean boolean1 = true;
		String sNum1 = "" + num1;
		String sNum2 = "" + num2;
		String sBoolean1 = "" + boolean1;
		// 通過valueOf(基本類型數據 變量名)方法將基本類型轉換爲String類型
		int num3 = 10;
		double num4 = 3.14;
		boolean boolean2 = true;
		String sNum3 = String.valueOf(num3);
		String sNum4 = String.valueOf(num4);
		String sBoolean2 = String.valueOf(boolean2);
	}
}

說明:

  • 使用+運算符基本類型與字符串類型連接時,Java首先自動將基本類型轉換成字符串類型,然後連接在一起.。
  • 使用String類的valueOf(基本類型數據 變量名)方法可以將基本類型轉換成字符串類型,該方法無需String類型的變量直接使用類名String即可調用

2、String類型轉換爲基本數據類型

字符串類型轉換爲基本類型,需要使用基本類型包裝類。Java爲每一種基本類型都提供了對應包裝類,包裝類提供了一些常用的操作,其中就包括將字符串類型轉換成基本類型。基本類型的包裝類及其轉換方法如下表:

基本類型 包裝類 方法 方法說明
byte Byte parseByte(String s) 將字符串轉換爲byte類型
short Short parseShort(String s) 將字符串轉換爲short類型
int Integer parseInt(String s) 將字符串轉換爲int類型
long Long parseLong(String s) 將字符串轉換爲long類型
float Float parseFloat(String s) 將字符串轉換爲float類型
double Double parseDouble(String s) 將字符串轉換爲double類型
boolean Boolean parseBoolean(String s) 將字符串轉換爲boolean類型

下面是一個示例:

public class Test {
	public static void main(String[] args) {
		String str1 = "1", str2 = "true", str3 = "3.14";
		byte num1 = Byte.parseByte(str1);
		short num2 = Short.parseShort(str1);
		int num3 = Integer.parseInt(str1);
		long num4 = Long.parseLong(str1);
		float num5 = Float.parseFloat(str3);
		double num6 = Double.parseDouble(str3);
		boolean boolean1 = Boolean.parseBoolean(str2);
	}
}

說明:

  • 本例中的轉換方法均無需對應類型的變量,直接使用類名即可調用
  • 字符串與char類型的轉換可以通過字符串的charAt(int index)方法完成。如下例:
    public class Test {
    	public static void main(String[] args) {
    		String str = "Hello World";
    		char char1 = str.charAt(6);
    		System.out.println("Hello World中第6位下標是字符" + char1);
    		// “Hello World中第6位下標是字符W”
    	}
    }
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章