本文源碼: GitHub·點這裏 || GitEE·點這裏
字符串是一個特殊的數據類型,屬於引用類型。String類在Java中使用關鍵字final修飾,因此這個類是不能夠繼承擴展和修改它的方法。String類用處極普遍,在對String對象進行初始化時,和基本類型的包裝器類型同樣,能夠不使用new關鍵字構造對象。(是真的妖嬈...)java
特色:final關鍵字修飾,實現Serializable序列化接口,Comparable比較接口,和CharSequence字符序列接口。git
final class String implements java.io.Serializable, Comparable<String>, CharSequence
兩種方式,常量和建立對象。程序員
String var1 = "cicada" ; String var2 = new String("smile") ;
var1:聲明的是一個常量,顯然是放在常量池中。github
var2:建立字符串對象,對象存放在堆內存中。spring
常量池用來存放常量;堆內存用來存放new出來的引用對象。apache
public class String02 { public static void main(String[] args) { String var1 = "cicada" ; String var2 = "cicada" ; // true;true System.out.println((var1==var2)+";"+var1.equals(var2)); String var3 = new String("cicada"); String var4 = new String("cicada"); // false;true System.out.println((var3==var4)+";"+var3.equals(var4)); // false;true System.out.println((var1==var4)+";"+var2.equals(var4)); String var5 = "ci"+"cada"; // true;true System.out.println((var1==var5)+";"+var5.equals(var4)); String var6 = new String02().getVar6 () ; // true;true System.out.println((var1==var6)+";"+var6.equals(var4)); } public String getVar6 (){ return "cicada" ; } }
==
:對於基本類型,比較的是值,對於引用類型,比較的是地址的值;編程
equals
:該方法源自Object中一個最基礎的通用方法,在Object的方法中使用==
判斷地址的值,只是到了String類中進行了重寫,用於字符內容的比較,該方法在繼承關係中的變化,追蹤JDK源碼,變化很是清楚。segmentfault
字符串在String內部是經過一個char[]數組表示,Unicode統一的編碼表示的字符,char類型的字符編碼由此來。數組
這裏看下構造方法就會明白上面的概念邏輯。緩存
private final char value[]; public String() {this.value = "".value;} public String(String original) { this.value = original.value; this.hash = original.hash; }
不一樣的國家和地區,使用的編碼多是不同的,互聯網中有UTF8編碼又是最經常使用,一次在程序開發中,常常須要編碼之間轉換。
public class String03 { public static void main(String[] args) throws Exception { String value = "Hello,知了"; // UTF-8 byte[] defaultCharset = value.getBytes(Charset.defaultCharset()); System.out.println(Arrays.toString(defaultCharset)); System.out.println(new String(defaultCharset,"UTF-8")); // GBK byte[] gbkCharset = value.getBytes("GBK"); System.out.println(Arrays.toString(gbkCharset)); System.out.println(new String(gbkCharset,"GBK")); // ISO-8859-1:表示的字符範圍很窄,沒法表示中文字符,轉換以後沒法解碼 byte[] isoCharset = value.getBytes("ISO8859-1"); System.out.println(Arrays.toString(isoCharset)); System.out.println(new String(isoCharset,"ISO8859-1")); // UTF-16 byte[] utf16Charset = value.getBytes("UTF-16"); System.out.println(Arrays.toString(utf16Charset)); System.out.println(new String(utf16Charset,"UTF-16")); } }
兩個基礎概念:
編碼Encode
:信息按照規則從一種形轉換爲另外一種形式的過程,簡稱編碼;
解碼Decode
:解碼就是編碼的逆向過程。
在平常開發中,字符串的格式不會都知足業務要求,一般就須要進行指定格式化操做。
public class String04 { public static void main(String[] args) { // 指定位置拼接字符串 String var1 = formatStr("cicada","smile"); System.out.println("var1="+var1); // 格式化日期:2020-03-07 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); Date date = new Date() ; System.out.println(format.format(date)); // 浮點數:此處會四捨五入 double num = 3.14159; System.out.print(String.format("浮點類型:%.3f %n", num)); } public static String formatStr (String ...var){ return String.format("key:%s:route:%s",var); } }
上面案例演示應用場景:Redis緩存Key生成,日期類型轉換,超長浮點數的截取。
String對象形參傳遞到方法裏的時候,實際上傳遞的是引用的拷貝。
public class String05 { String var1 = "hello" ; int[] intArr = {1,2,3}; public static void main(String[] args) { String05 objStr = new String05() ; objStr.change(objStr.var1,objStr.intArr); // hello 4 System.out.println(objStr.var1); System.out.println(objStr.intArr[2]); } public void change (String var1,int[] intArr){ var1 = "world" ; intArr[2] = 4 ; } }
案例中改變的是var1引用的拷貝,方法結束執行結束,形參var1被銷燬, 原對象的引用保持不變。數組做爲參數傳遞時傳遞是數組在內存中的地址值,這樣直接找到數組在內存中的位置。
字符串的處理在系統開發中十分的常見,一般會提供一個工具類統一處理,能夠基於一個框架中的工具類二次封裝,也能夠所有自行封裝。
class StringUtil { private StringUtil(){} public static String getUUid (){ return UUID.randomUUID().toString().replace("-",""); } }
上面是字符串工具類最基礎的一個。不一樣框架中自帶的工具類也不錯。
org.apache.commons.lang3.StringUtils org.springframework.util.StringUtils com.alibaba.druid.util.StringUtils
這裏推薦第一個,也能夠把自定義的工具類繼承該工具類,提供更豐富的公共方法。
絮叨一句
:代碼整潔之道的基礎,就是有一顆《偷懶》的心,花點心思該封裝的封裝,該刪除的刪除。
字符串修改拼接經常使用的API,內部的實現過程和String相似。
public class String07 { public static void main(String[] args) { StringBuffer var = new StringBuffer(2) ; var.append("what"); var.append("when"); System.out.println(var); } }
看到上面幾行代碼的反應,基本能反應編程的年齡:
一年
:API是這樣用的,沒毛病;
三年
:StringBuffer是線程安全的,效率相對偏低;
五年
:默認字符數組大小是16,這裏自定義字符數組的大小,若是長度不夠須要擴容,因此要預估一下字符串的可能大小,減少消耗;
絮叨一句
:Java中許多容器對象的大小默認是16,且具有動態擴容機制,這就是傳說中的編程思想,在開發中照葫蘆畫瓢的寫兩段,這就是格調。
這個類出現比StringBuffer要晚不少,從JDK1.5纔開始出現。
public class String08 { public static void main(String[] args) { StringBuilder var = new StringBuilder() ; var.append("how").append("what") ; System.out.println(var); } }
用法和StringBuffer差很少,不過是非線程安全操做,效率天然要高。
補刀一句
:對於線程安全和操做和非安全操做,還有初始容量和擴容這種邏輯,均可以在源碼中查看,這是進階程序員的必備意識。
這裏原理解釋同上,根本邏輯是一致的。
public class String09 { public static void main(String[] args) { String var1 = new String("A"); String var2 = new String("B"); StringBuffer var3 = new StringBuffer("C"); StringBuffer var4 = new StringBuffer("D"); join(var1,var2); join(var3,var4); //A<>B System.out.println(var1+"<>"+var2); //C<>DD System.out.println(var3+"<>"+var4); } public static void join (String s1,String s2){ s1 = s2 ; s2 = s1+s2 ; } public static void join (StringBuffer s1,StringBuffer s2){ s1 = s2 ; s2 = s2.append(s1) ; } }
絮叨一句
:String相關API傳參問題,工做前三年跳槽基本都會被問到,若是不瞭解基本原理,心情再有點小慌,還基本會答錯。
GitHub·地址 https://github.com/cicadasmile/java-base-parent GitEE·地址 https://gitee.com/cicadasmile/java-base-parent