String類的創(chuàng)建
//方式一
String str = "hello word";
//方式二
String str = new String("hello word");
//方式三
char[] arr = {'a', 'b', 'c'};
String str = new String(arr);
我們看看以上代碼的內(nèi)存分布圖:
引用類型有點類似c語言中的指針囊卜,我們在內(nèi)存中開辟了一小塊內(nèi)存空間保存一個地址,但是與c語言不同的是指針能進行數(shù)字運算,但引用不行肩钠,java中String就是引用類型.
由于String是引用類型碾褂,因此我們分析以下代碼的內(nèi)存分布:
String str1 = "hello word';
String str2 = str1;
修改str1的值兽间,看看str2是否會隨之改變:
public class test {
public static void main(String[] args) throws NameException, PasswordException {
String str1 = "hello word";
String str2 = str1;
str1 = "zmm";
System.out.println(str2);
}
}
從內(nèi)存分布分析此結果:
可以看出我們修改str1并不會影響str2的結果,也就意味著我們修改了str1其實只是修改了str1的指向正塌,讓str1引用指向了一個新的String對象
字符串比較相等
import java.util.Arrays;
public class test {
public static void main(String[] args) throws NameException, PasswordException {
String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2);
}
}
那么根據(jù)以上結果嘀略,是否就能確定判斷字符串相等可以用 '' == '', 我們再看一個例子:
import java.util.Arrays;
public class test {
public static void main(String[] args) throws NameException, PasswordException {
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1 == str2);
}
}
我們分析兩種創(chuàng)建String方式的差異
第一種:
str1 和str2 是指向同一個對象,是由于像 " hello '' 這樣的字符串乓诽,我們是將它保存在字符串常量池中的帜羊,如果我們需要使用 '' hello '' 這個字符串常量,就直接引用字符串常量池的這個位置就可.
第二種:
用 String str1 = new String(" hello ") 這個方式創(chuàng)建對象鸠天,我們在內(nèi)存中開了兩塊空間讼育,來保存 " hello "這個字符串,因此在用" == " 比較兩個字符串是否相等的時候稠集,其實是在比較兩個字符串的地址是否一樣奶段,而不是比較字符串的內(nèi)容
equals方法來比較字符串內(nèi)容是否相等
import java.util.Arrays;
public class test {
public static void main(String[] args) throws NameException, PasswordException {
String str1 = new String("hello");
String str2 = new String("hello");
System.out.println(str1.equals(str2));
}
}
使用 equals 的注意事項
//方式一
System.out.println(" hello ".equals(str2));
//方式二
System.out.println(str1.equals(str2));
以上兩種方式我們更推薦方式一,若一旦 " str1 = null "那么就會拋出異常
方式二
import java.util.Arrays;
public class test {
public static void main(String[] args) throws NameException, PasswordException {
String str1 = null;
String str2 = new String("hello");
System.out.println(str1.equals(str2));
}
}
方式一
import java.util.Arrays;
public class test {
public static void main(String[] args) throws NameException, PasswordException {
String str1 = null;
String str2 = new String("hello");
System.out.println("hello".equals(str1));
}
}
字符串常量池
直接賦值
import java.util.Arrays;
public class test {
public static void main(String[] args) throws NameException, PasswordException {
String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2);
}
}
在JVM底層實際上會自動維護一個對象池(字符串常量池)
- 如果現(xiàn)在采用了直接賦值的模式進行String類的對象實例化操作剥纷,那么該實例化對象(字符串內(nèi)容)將自動保存到這個對象池之中.
- 如果下次繼續(xù)使用直接賦值的模式聲明String類對象痹籍,此時對象池之中如若有指定內(nèi)容,將直接行引用
- 如若沒有晦鞋,則開辟新的字符串對象而后將其保存在對象池之中以供下次使用
采用構造方法
String str = new String(" hello ");
這樣的做法有兩個缺點:
- 如果使用String構造方法就會開辟兩塊堆內(nèi)存空間蹲缠,并且其中一塊堆內(nèi)存將成為垃圾空間(字符串常量 "hello" 也是一個匿名對象, 用了一次之后就不再使用了, 就成為垃圾空間, 會被 JVM 自動回收掉).
- 字符串共享問題,同一個字符串可能被儲存多次悠垛,比較浪費空間
intern 方法可以顯示把對象入池
import java.util.Arrays;
//判斷str是否入池
public class test {
public static void main(String[] args) throws NameException, PasswordException {
String str1 = new String("hello");
System.out.println(str1 == "hello");
}
}
import java.util.Arrays;
public class test {
public static void main(String[] args) {
//顯示入池
String str1 = new String("hello").intern();
System.out.println(str1 == "hello");
}
}
結論
- 直接賦值:只會開辟一塊堆內(nèi)存空間线定,并且該字符串對象可以自動保存在對象池中以供下次使用。
- 構造方法:會開辟兩塊堆內(nèi)存空間确买,不會自動保存在對象池中斤讥,可以使用intern()方法手工入池
- 我們一般采用直接賦值的方式創(chuàng)建String類對象
字符串的不可變
字符串是一種不可變對象,它的內(nèi)容不可變
String類的內(nèi)部實現(xiàn)也是基于char[ ]來實現(xiàn)湾趾,但是String類并沒有提供set方法來修改內(nèi)部字符數(shù)組
public class example {
public static void main(String[] args) {
String str = "hello ";
str = str + "word";
str += "!!!";
System.out.println(str);
}
}
內(nèi)存分布圖
字符串str拼接以后周偎,str的引用指向改變了抹剩,但String的對象并沒有改變
String不可變的好處:
- 方便實現(xiàn)字符串對象池
- 不可變對象線程安全
- 不可變對象更方便緩存 hash code, 作為 key 時可以更高效的保存到 HashMap 中
字符、字節(jié)與字符串
方法名稱 | 類型 | 描述 |
---|---|---|
public String(char value[]) | 構造 | 使用字符數(shù)組的內(nèi)容構造一個字符串 |
public String(char value[]蓉坎,int offset, int court) | 構造 | 使用字符數(shù)組中以offset下標開始的連續(xù)count個字符構造一個字符串 |
public char charAt(int index) | 普通 | 獲得字符串中指定索引的字符 |
public char[] toCharArray() | 普通 | 將字符串變?yōu)樽址麛?shù)組返回 |
- 示例代碼1:字符數(shù)組轉(zhuǎn)字符串
import java.util.Scanner;
public class example {
public static void main(String[] args) {
char[] arr = {'a','b','c'};
String str = new String(arr);
System.out.println(str);
}
}
- 示例代碼2:將指定字符轉(zhuǎn)成字符串
import java.util.Scanner;
public class example {
public static void main(String[] args) {
char[] arr = {'a','b','c','d','f'};
String str = new String(arr,2,3);
System.out.println(str);
}
}
- 示例代碼3:獲得字符串中的某個字符
import java.util.Scanner;
public class example {
public static void main(String[] args) {
String str = "hello";
char ch = str.charAt(4);
System.out.println(ch);
}
}
- 示例代碼4:字符串轉(zhuǎn)成字符數(shù)組
import java.util.Arrays;
import java.util.Scanner;
public class example {
public static void main(String[] args) {
String str = "hello";
char[] str1 = str.toCharArray();
System.out.println(Arrays.toString(str1));
}
}
字符串常見操作
字符串常見比較
方法名稱 | 類型 | 描述 |
---|---|---|
public boolean equals(Object anObject) | 普通 | 區(qū)分大小寫的比較 |
public boolean equalsIgnoreCase(String anotherString) | 普通 | 不區(qū)分大小寫的比較 |
public int compareTo(String anotherString) | 普通 | 兩個字符串比較關系 |
- 示例代碼1:
import java.util.Arrays;
import java.util.Scanner;
public class example {
public static void main(String[] args) {
String str1 = "hello";
String str2 = "Hello";
System.out.println(str1.equals(str2));
}
}
- 示例代碼2:
import java.util.Arrays;
import java.util.Scanner;
public class example {
public static void main(String[] args) {
String str1 = "hello";
String str2 = "Hello";
System.out.println(str1.equalsIgnoreCase(str2));
}
}
- 示例代碼3:
import java.util.Arrays;
import java.util.Scanner;
public class example {
public static void main(String[] args) {
String str1 = "hello";
String str2 = "Hello";
System.out.println(str1.compareTo(str2));
}
}
public class example {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = "hello";
System.out.println(str1.compareTo(str2));
}
}
public class example {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1.compareTo(str2));
}
}
使用compareTo()這個方法澳眷,若兩個字符串相等,則返回0蛉艾;反之返回將字符串中的字符一一比較返回其差值;
不區(qū)分大小寫的字符串比較
public class example {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1.equalsIgnoreCase(str2));
}
}
字符串查找
所謂字符串查找就是從一個完整的字符串之中可以判斷指定內(nèi)容是否存在
方法名稱 | 類型 | 描述 |
---|---|---|
public boolean contains(CharSequence s) | 普通 | 判斷一個子字符串是否存在 |
public int indexOf(String str) | 普通 | 從左開始向右查找指定字符串是否存在钳踊,查到了返回位置的開始索引,如果查不到返回-1 |
public int indexOf(String str, int fromIndex) | 普通 | 從指定位置開始查找子串的位置 |
public int lastIndexOf(String str) | 普通 | 由右向左查找子字符串位置 |
public int lastIndexOf(String str, int fromIndex) | 普通 | 從指定位置由右向左查找子串 |
public boolean startsWith(String prefix) | 普通 | 判斷是否以指定字符串開頭 |
public boolean startsWith(String prefix, int toffset) | 普通 | 從指定位置開始判斷是否以指定字符串開頭 |
public boolean endsWith(String suffix) | 普通 | 判斷是否以指定字符串結尾 |
- 示例代碼1:
public class example {
public static void main(String[] args) {
String str1 = "Hello word";
System.out.println(str1.contains("word"));
}
}
- 示例代碼2:
public class example {
public static void main(String[] args) {
String str1 = "Hello word";
String str2 = "word";
System.out.println(str1.indexOf(str2));
}
}
- 示例代碼3:
package xsk;
public class Test {
public static void main(String[] args) {
String str = "adcmfro";
String str1 = "m";
System.out.println(str.indexOf(str1, 3));
}
}
- 示例代碼4:
package xsk;
public class Test {
public static void main(String[] args) {
String str = "adrmfro";
String str1 = "r";
System.out.println(str.lastIndexOf(str1));
}
}
- 示例代碼5:
package xsk;
public class Test {
public static void main(String[] args) {
String str = "adrmfro";
String str1 = "r";
System.out.println(str.lastIndexOf(str1, 4));
}
}
- 示例代碼6:
package xsk;
public class Test {
public static void main(String[] args) {
String str = "adrmfro";
System.out.println(str.startsWith("amm"));
}
}
- 示例代碼7:
package xsk;
public class Test {
public static void main(String[] args) {
String str = "adrmfro";
System.out.println(str.startsWith("ad",0));
}
}
- 示例代碼8:
package xsk;
public class Test {
public static void main(String[] args) {
String str = "adrmfro";
System.out.println(str.endsWith("r"));
}
}
字符串替換
使用一個指定的新的字符串替換掉已有的字符串數(shù)據(jù)勿侯,可用方法如下
方法名稱 | 類型 | 描述 |
---|---|---|
public String replaceAll(String regex, String replacement) | 普通 | 替換所有的指定內(nèi)容 |
public String replaceFirst(String regex, String replacement) | 普通 | 替換首個內(nèi)容 |
- 示例代碼:
package xsk;
public class Test {
public static void main(String[] args) {
String str = "helloword";
System.out.println(str.replaceAll("l", "m"));
System.out.println(str.replaceFirst("l", "x"));
System.out.println(str.replace("w", "o"));
}
}
注意事項: 由于字符串是不可變對象, 替換不修改當前字符串, 而是產(chǎn)生一個新的字符串.
字符串拆分
可以將一個完整的字符串按照指定的分隔符劃分為若干個子字符串
方法名稱 | 類型 | 描述 |
---|---|---|
public String[] split(String regex) | 普通 | 將字符串全部拆分 |
public String[] split(String regex, int limit) | 普通 | 將字符串部分拆分拓瞪,該數(shù)組的長度極限就是limit |
- 示例代碼1:
package xsk;
public class Test {
public static void main(String[] args) {
String str = "hello word";
String[] s = str.split(" ");
for(String x: s) {
System.out.println(x);
}
}
}
- 示例代碼2:
package xsk;
public class Test {
public static void main(String[] args) {
String str = "hello word hello zmm";
String[] s = str.split(" ", 2);
for(String x: s) {
System.out.println(x);
}
}
}
- 示例代碼3:
package xsk;
public class Test {
public static void main(String[] args) {
String str = "192.168.1.1";
String[] s = str.split("\\.");
for(String x: s) {
System.out.println(x);
}
}
}
注意事項:
- 字符"|","*","+"都得加上轉(zhuǎn)義字符,前面加上" \ "即可.
- 而如果是""助琐,那么就得寫成"\".
- 如果一個字符串中有多個分隔符祭埂,則可以用" | "作為連接符
字符串截取
從一個完整字符串之中截取部分內(nèi)容,可用方法如下
方法名稱 | 類型 | 描述 |
---|---|---|
public String substring(int beginIndex) | 普通 | 從指定索引截取到結尾 |
public String substring(int beginIndex, int endIndex) | 普通 | 截取部分內(nèi)容 |
- 示例代碼1:
package xsk;
public class Test {
public static void main(String[] args) {
String str = "helloword";
System.out.println(str.substring(4));
}
}
- 示例代碼2:
package xsk;
public class Test {
public static void main(String[] args) {
String str = "helloword";
System.out.println(str.substring(4, 6));
}
}
注意事項:
- 索引從0開始
- 注意前閉后開區(qū)間的寫法, substring(0, 5) 表示包含 0 號下標的字符, 不包含 5 號下標
其他操作方法
方法名稱 | 類型 | 描述 |
---|---|---|
public String trim() | 普通 | 去掉字符串中的左右空格兵钮,保留中間空格 |
public String toUpperCase() | 普通 | 字符串轉(zhuǎn)大寫 |
public String toLowerCase() | 普通 | 字符串轉(zhuǎn)小寫 |
public native String intern() | 普通 | 字符串入池操作 |
public String concat(String str) | 普通 | 字符串連接蛆橡,等同于"+",不入池 |
public int length() | 普通 | 取得字符串長度 |
public boolean isEmpty() | 普通 | 判斷字符串是否為空(不是null掘譬,而是長度為0) |
- 示例代碼1:
package xsk;
public class Test {
public static void main(String[] args) {
String str = " hello bit hello ";
System.out.println(str.trim());
}
}
- 示例代碼2:
package xsk;
public class Test {
public static void main(String[] args) {
String str = " Hello bit hello ";
System.out.println(str.toUpperCase());
}
}
- 示例代碼3:
package xsk;
public class Test {
public static void main(String[] args) {
String str = " Hello bit hello ";
System.out.println(str.toLowerCase());
}
}
- 示例代碼4:
package xsk;
public class Test {
public static void main(String[] args) {
String str = " Hello bit hello ";
String str1 = "aaa";
System.out.println(str.concat(str1));
}
}
- 示例代碼5:
public class Test {
public static void main(String[] args) {
String str = " Hello bit hello ";
System.out.println(str.isEmpty());
System.out.println(str.length());
}
}
String Buffer 和 String Builder
StringBuffer和StringBuilder是為了解決String是不可變對象帶來的麻煩
我們來看一下之前那個頻繁拼接字符串的代碼:
public class Test {
public static void main(String[] args) {
String str = "hooo";
for(int i = 0; i < 10; i++) {
str += i;
}
System.out.println(str);
}
}
我們知道String是不可變對象泰演,所以這個代碼的效率很低,就是在頻繁的創(chuàng)建新的對象葱轩。我們使用StringBuffer和StringBuilder來分別優(yōu)化一下這個代碼:
public class Test {
public static void main(String[] args) {
StringBuffer str = new StringBuffer("hoooo");
for(int i = 0; i < 10; i++) {
str.append(i);
}
System.out.println(str);
}
}
String睦焕、StringBuffer和StringBuilder的區(qū)別
- String是不可變的,StringBuffer和StringBuilder都是可變的靴拱;
- StringBuffer和StringBuilder大部分功能都是類似的垃喊;
- StringBuffer采用同步處理,屬于線程安全袜炕;StringBuilder沒有采用同步操作本谜,線程不安全。