Preface
Hello everyone, today I will share with you the basics of java String.
Needless to say, the importance of the String class. It can be said to be the most used class in our back-end development, so it is necessary to talk about it.
The main content of this article is as follows:
##String Introduction
Let’s first talk about the eight major data types in java, and then talk about String. Eight basic data typesbyte: 8 bits, the maximum amount of data stored is 255, and the range of stored data is -128~127. short: 16 bits, the maximum data storage capacity is 65536, and the data range is -32768~32767. int: 32 bits, the maximum data storage capacity is 2 to the 32nd power minus 1, the data range is negative 2 to the 31st power to positive 2 to the 31st power minus 1. long: 64 bits, the maximum data storage capacity is 2 to the 64th power minus 1, the data range is negative 2 to the 63rd power to positive 2 to the 63rd power minus 1. float: 32 bits, the data range is 3.4e-45~1.4e38. When assigning directly, f or F must be added after the number. double: 64 bits, data range is 4.9e-324~1.8e308, d or D can be added or not added when assigning value. boolean: There are only two values: true and false.char: 16 bits, stores Unicode code, assigns value with single quotes.
In addition to these eight major data types (the eight major data types also have corresponding encapsulation types, I believe you know), there is also a special type in Java: String, which literally means string.
StringOfficial introduction
English version
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html
rt.ar package under the
JDK directory we installed. The full path name is:
java.lang.String. String is used to represent strings in our java code, for example:
String str = "中國夢,我的夢"; String name = "zhangsan";Just know these for now.
String usage
Definition typeIn daily development, there are too many places where String is used, especially when using To define the types of variables and constants, basically as long as you code, you can always see it. For example: user information is represented by the entity class User.public class User{ private Long id; private String userName; private String address; private String password; .... }Common method demonstrationThe String class has more than 20 methods. A usage example is given below (most of the methods are demonstrated here, and you can try the rest by yourself).
//案例代碼,來源于網(wǎng)絡 public class StringDemo { public static void main(String[] args) throws Exception { String str1 = "Hello World"; String str2 = "Hello World"; String str3 = "hello world"; String str4 = " hello world "; //返回字符串的長度 System.out.println("r1: " + str1.length()); //比較兩個字符串的大小compareTo(返回的是int),0相等,復數(shù)小于,正數(shù)大于 System.out.println("r2 : " + str1.compareTo(str2)); //比較兩個字符串的大小compareTo(返回的是int),0相等,復數(shù)小于,正數(shù)大于 System.out.println("r3 : " + str1.compareTo(str3)); //字符串比較compareToIgnoreCase,忽略大小寫。0相等,復數(shù)小于,正數(shù)大于 System.out.println("r4 : " + str1.compareToIgnoreCase(str3)); //字符串查找indexOf,返回的是找到的第一個的位置,沒找到返回-1。從0開始 System.out.println("r5 : " + str1.indexOf("o")); //查找字符串最后一次出現(xiàn)的位置lastIndexOf System.out.println("r6 : " + str1.lastIndexOf("o")); //刪除字符串中的一個字符,字符串從0開始的 substring(a, b) //返回指定起始位置(含)到結束位置(不含)之間的字符串 System.out.println("r7 : " + str1.substring(0, 5) + str1.substring(6)); //字符串替換,替換所有 System.out.println("r8 : " + str1.replace("o", "h")); //字符串替換,替換所有 System.out.println("r9 : " + str1.replaceAll("o", "h")); //字符串替換,替換第一個 System.out.println("r10 : " + str1.replaceFirst("o", "h")); //字符串反轉 System.out.println("r11 : " + new StringBuffer(str1).reverse()); //字符串反轉 System.out.println("r11’: " + new StringBuilder(str1).reverse()); //字符串分割 String[] temp = str1.split("\\ "); for (String str : temp) { System.out.println("r12 : " + str); } //字符串轉大寫 System.out.println("r13 : " + str1.toUpperCase()); //字符串轉小寫 System.out.println("r14 : " + str1.toLowerCase()); //去掉首尾空格 System.out.println("r15 : " + str4.trim()); //是否包含,大小寫區(qū)分 System.out.println("r16 : " + str1.contains("World")); //返回指定位置字符 System.out.println("r17 : " + str1.charAt(4)); //測試此字符串是否以指定的后綴結束 System.out.println("r18 : " + str1.endsWith("d")); //測試此字符串是否以指定的前綴開始 System.out.println("r19 : " + str1.startsWith("H")); //測試此字符串從指定索引開始的子字符串是否以指定前綴開始 System.out.println("r20 : " + str1.startsWith("ll", 2)); //將指定字符串連接到此字符串的結尾。等價于用“+” System.out.println("r21 : " + str1.concat("haha")); //比較字符串的內(nèi)容是否相同 System.out.println("r22 : " + str1.equals(str2)); //與equals方法類似,忽略大小寫 System.out.println("r23 : " + str1.equalsIgnoreCase(str2)); //判斷是否是空字符串 System.out.println("r24: " + str1.isEmpty()); } }
我們開發(fā)中差不多也就是這么使用了,但是如果你僅僅是使用很牛了,貌似遇到面試照樣會掛。所以,學知識,不能停留在使用層面,需要更深層次的學習。
下面我們就來深層次的學習String,希望大家?guī)е活w平常的心學習,不要害怕什么,燈籠是張紙,捅破不值錢。
String核心部分源碼分析
備注:JDK版本為1.8+,因為JDK9版本中和舊版本有細微差別。
String類源碼注釋
/** * The {@code String} class represents character strings. All * string literals in Java programs, such as {@code "abc"}, are * implemented as instances of this class. * 這個String類代表字符串。java編程中的所有字符串常量。 * 比如說:"abc"就是這個String類的實例 * <p> * Strings are constant; their values cannot be changed after they * are created. * 字符串是常量,他們一旦被創(chuàng)建后,他們的值是不能被修改。(重點) * String buffers support mutable strings. * String緩存池支持可變的字符串, * Because String objects are immutable they can be shared. For example: * 因為String字符串不可變,但他們可以被共享。比如: * <blockquote><pre class="brush:php;toolbar:false"> * String str = "abc"; *
* is equivalent to: *
* char data[] = {'a', 'b', 'c'}; * String str = new String(data); *
* Here are some more examples of how strings can be used: * String使用案例 * System.out.println("abc"); * String cde = "cde"; * System.out.println("abc" + cde); * String c = "abc".substring(2,3); * String d = cde.substring(1, 2); *
* The class {@code String} includes methods for examining * individual characters of the sequence, for comparing strings, for * searching strings, for extracting substrings, and for creating a * copy of a string with all characters translated to uppercase or to * lowercase. Case mapping is based on the Unicode Standard version * specified by the {@link java.lang.Character Character} class. * 這個String類包含了一些測評單個字符序列的方法,比如字符串比較,查找字符串, * 提取字符串,和拷貝一個字符串的大小寫副本。 * 大小寫映射的是基于Character類支持的Unicode的字符集標準版本。 *
* The Java language provides special support for the string * concatenation operator ( + ), and for conversion of * other objects to strings. * java語言提供了對字符串的特殊支持,如:可以通過"+"號來進行字符串的拼接操作, * 為其他類提供了與字符串轉換的操作 * String concatenation is implemented * through the {@code StringBuilder}(or {@code StringBuffer}) * class and its {@code append} method. * 字符串的+號拼接操作是通過StringBuilder或者StringBuffer類的append()方法 * 來實現(xiàn)的 * String conversions are implemented through the method * {@code toString}, defined by {@code Object} and * inherited by all classes in Java. * 對象與字符串的轉換操作是通過所有類的父類Object中定義的toString()方法來實現(xiàn)的 * For additional information on * string concatenation and conversion, see Gosling, Joy, and Steele, * The Java Language Specification. * *
Unless otherwise noted, passing a null argument to a constructor * or method in this class will cause a {@link NullPointerException} to be * thrown. * 除非有特殊說明,否則傳一個null給String的構造方法或者put方法,會報空指針異常的 *
A {@code String} represents a string in the UTF-16 format * in which supplementary characters are represented by surrogate * pairs (see the section Unicode * Character Representations in the {@code Character} class for * more information). * 一個String 對象代表了一個UTF-16編碼語法組成的字符串 * Index values refer to {@code char} code units, so a supplementary * character uses two positions in a {@code String}. *
The {@code String} class provides methods for dealing with * Unicode code points (i.e., characters), in addition to those for * dealing with Unicode code units (i.e., {@code char} values). * 索引值指向字符碼單元,所以一個字符在一個字符串中使用兩個位置, * String 類提供了一些方法區(qū)處理單個Unicode編碼,除了那些處理Unicode代碼單元。 * @since JDK1.0 */
以上便是String類注釋的整個片段,后面剩下的就是作者、相關類、相關方法以及從JDK哪個版本開始有的。
String類定義
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { .... }
類圖
String類被final修飾,表示String不可以被繼承。下面我們來說說String實現(xiàn)三個接口有什么用處:
實現(xiàn)Serializable,可以被序列化 實現(xiàn)Comparable,可以用于比較大?。ò错樞虮容^單個字符的ASCII碼) 實現(xiàn)CharSequence,表示是一個有序字符的序列,(因為String的本質(zhì)是一個char類型數(shù)組)
簡單介紹final
修飾類:類不可被繼承,也就是說,String類不可被繼承了
修飾方法:把方法鎖定,以訪任何繼承類修改它的涵義
修飾遍歷:初始化后不可更改
重要成員
/** The value is used for character storage. */ // 來用存儲String內(nèi)容的 private final char value[]; // 存儲字符串哈希值,默認值為0 private int hash; // Default to 0 // 實現(xiàn)序列化的標識 private static final long serialVersionUID = -6849794470754667710L;
char value[]
被final修飾,說明value[]數(shù)組是不可變的。
構造方法
/** * Initializes a newly created {@code String} object so that it represents * an empty character sequence. Note that use of this constructor is * unnecessary since Strings are immutable. * 初始化新創(chuàng)建的String對象,時期表示空字符串序列。 * 注意:這個構造方法的用法是沒必要的,因為字符串是不可變的 */ public String() { this.value = "".value; }
無參構造方法中是將一個空字符串的value值賦給當前value。
/** * Initializes a newly created {@code String} object so that it represents * the same sequence of characters as the argument; in other words, the * newly created string is a copy of the argument string. Unless an * explicit copy of {@code original} is needed, use of this constructor is * unnecessary since Strings are immutable. * 初始化創(chuàng)建的String對象,時期表示與參數(shù)相同的字符串序列。 * 換句話說:新創(chuàng)建的字符串是參數(shù)自粗糙的副本。 * 除非,如果需要original的顯示副本,否則也是沒有必要使用此構造方法的 * 因為字符串是不可變的 * @param original * A {@code String} */ public String(String original) { this.value = original.value; this.hash = original.hash; } //案例: String str=new String("abc");
把original的value賦給當前的value,并把original的hash賦給當前的hash。
/** * Allocates a new {@code String} so that it represents the sequence of * characters currently contained in the character array argument. The * contents of the character array are copied; subsequent modification of * the character array does not affect the newly created string. * 分配一個新的{@code String},以便它表示字符數(shù)組參數(shù)中當前包含的字符。這個 * 復制字符數(shù)組的內(nèi)容;隨后修改字符數(shù)組不影響新創(chuàng)建的字符串。 * @param value * The initial value of the string */ public String(char value[]) { //注:將傳過來的char數(shù)組copy到value數(shù)組里 this.value = Arrays.copyOf(value, value.length); } //Arrays類中的copyOf方法 public static char[] copyOf(char[] original, int newLength) { //創(chuàng)建一個新的char數(shù)組 char[] copy = new char[newLength]; //把original數(shù)組中內(nèi)容拷貝到新建的char數(shù)組中 System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); //返回新建的char數(shù)組 return copy; }
使用Arrays類的copyOf方法,新建一個char數(shù)組,將original的內(nèi)容放到新建的char數(shù)組中。
然后,把新建的char數(shù)組賦給當前的vlaue。
public String(StringBuffer buffer) { synchronized(buffer) { this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); } }
因為StringBuffer是線程安全類,所以,這里加了同步鎖,保證線程安全。
public String(StringBuilder builder) { this.value = Arrays.copyOf(builder.getValue(), builder.length()); }
StringBuilder是非線程安全的,這里也就沒有做線程安全處理,其他內(nèi)容和前面一樣。
注:很多時候我們不會這么去構造,因為StringBuilder跟StringBuffer有toString方法如果不考慮線程安全,優(yōu)先選擇StringBuilder
這里就講這么多構造方法,其他很復雜,也基本不用,所以,了解這些就夠了。如果對其他感興趣的,可以自行去研究研究。
常用方法分析
前面的使用案例中,我們已經(jīng)對String的大部分方法進行演示一波,這里我們就挑幾個相對重要的方法進行深度解析。
hashCode方法
hashCode()方法是在Object類中定義的,String對其進行了重寫。
public int hashCode() { int h = hash; if (h == 0 && value.length > 0) { char val[] = value; //hash算法,s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] //使用{@codeint}算法,其中{@codes[i]}是<i> i</i>字符串的第個字符, //{@code n}是字符串,{@code^}表示指數(shù)運算。 for (int i = 0; i < value.length; i++) { h = 31 * h + val[i]; } hash = h; } return h; }
hashCode的一個具體實現(xiàn),由于java體系中每個對象都可以轉換成String,因此他們應該都是通過這個hash來實現(xiàn)的
接著,我們看看equals()方法;
equals()方法
equals()方法也是Object類中定義的,String類對其進行了重寫。
public boolean equals(Object anObject) { //首先會判斷是否是同一個對象 if (this == anObject) { return true; } //判斷是否為String類型 if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; //長度是否相同 if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; //逐個遍歷判斷是否相等 //從后往前單個字符判斷,如果有不相等,返回假 while (n-- != 0) { //不相等,直接返回false if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
補充:==比較
==比較基本數(shù)據(jù)類型,比較的是值 ==比較引用數(shù)據(jù)類型,比較的是地址值
substring()方法
substring方法在工作使用的也是相當?shù)亩?,作用就是截取一段字符串?/p>
public String substring(int beginIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } int subLen = value.length - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } //如果beginIndex==0,返回的是當前對象, //否則這里是new的一個新對象,其實String中的很多函數(shù)都是這樣的操作 return (beginIndex == 0) ? this : new String(value, beginIndex, subLen); }
intern()方法
intern()
方法是native
修飾的方法,表示該方法為本地方法。
/* * When the intern method is invoked, if the pool already contains a * string equal to this {@code String} object as determined by * the {@link #equals(Object)} method, then the string from the pool is * returned. Otherwise, this {@code String} object is added to the * pool and a reference to this {@code String} object is returned. */ public native String intern();
方法注釋會有寫到,意思就是調(diào)用方法時,如果常量池有當前String
的值,就返回這個值,沒有就加進去,返回這個值的引用。
案例如下
public class StringDemo { public static void main(String[] args) throws Exception { String str1 = "a"; String str2 = "b"; String str3 = "ab"; String str4 = str1 + str2; String str5 = new String("ab"); System.out.println(str5 == str3);//堆內(nèi)存比較字符串池 //intern如果常量池有當前String的值,就返回這個值,沒有就加進去,返回這個值的引用 System.out.println(str5.intern() == str3);//引用的是同一個字符串池里的 System.out.println(str5.intern() == str4);//變量相加給一個新值,所以str4引用的是個新的 System.out.println(str4 == str3);//變量相加給一個新值,所以str4引用的是個新的 } }
運行結果
false true false false
length()方法
獲取字符串長度,實際上是獲取字符數(shù)組長度 ,源碼就非常簡單了,沒什么好說的。
public int length() { return value.length; }
isEmpty() 方法
判斷字符串是否為空,實際上是盼復字符數(shù)組長度是否為0
,源碼也是非常簡單,沒什么好說的。
public boolean isEmpty() { return value.length == 0; }
charAt(int index) 方法
根據(jù)索引參數(shù)獲取字符 。
public char charAt(int index) { //索引小于0或者索引大于字符數(shù)組長度,則拋出越界異常 if ((index < 0) || (index >= value.length)) { throw new StringIndexOutOfBoundsException(index); } //返回字符數(shù)組指定位置字符 return value[index]; }
getBytes()方法
獲取字符串的字節(jié)數(shù)組,按照系統(tǒng)默認字符編碼將字符串解碼為字節(jié)數(shù)組 。
public byte[] getBytes() { return StringCoding.encode(value, 0, value.length); }
compareTo()方法
這個方法寫的很巧妙,先從0開始判斷字符大小。如果兩個對象能比較字符的地方比較完了還相等,就直接返回自身長度減被比較對象長度,如果兩個字符串長度相等,則返回的是0,巧妙地判斷了三種情況。
public int compareTo(String anotherString) { //自身對象字符串長度len1 int len1 = value.length; //被比較對象字符串長度len2 int len2 = anotherString.value.length; //取兩個字符串長度的最小值lim int lim = Math.min(len1, len2); char v1[] = value; char v2[] = anotherString.value; int k = 0; //從value的第一個字符開始到最小長度lim處為止,如果字符不相等, //返回自身(對象不相等處字符-被比較對象不相等字符) while (k < lim) { char c1 = v1[k]; char c2 = v2[k]; if (c1 != c2) { return c1 - c2; } k++; } //如果前面都相等,則返回(自身長度-被比較對象長度) return len1 - len2; }
startsWith()方法
public boolean startsWith(String prefix, int toffset) { char ta[] = value; int to = toffset; char pa[] = prefix.value; int po = 0; int pc = prefix.value.length; // Note: toffset might be near -1>>>1. //如果起始地址小于0或者(起始地址+所比較對象長度)大于自身對象長度,返回假 if ((toffset < 0) || (toffset > value.length - pc)) { return false; } //從所比較對象的末尾開始比較 while (--pc >= 0) { if (ta[to++] != pa[po++]) { return false; } } return true; } public boolean startsWith(String prefix) { return startsWith(prefix, 0); } public boolean endsWith(String suffix) { return startsWith(suffix, value.length - suffix.value.length); }
起始比較和末尾比較都是比較經(jīng)常用得到的方法,例如:在判斷一個字符串是不是http協(xié)議的,或者初步判斷一個文件是不是mp3文件,都可以采用這個方法進行比較。
concat()方法
public String concat(String str) { int otherLen = str.length(); //如果被添加的字符串為空,返回對象本身 if (otherLen == 0) { return this; } int len = value.length; char buf[] = Arrays.copyOf(value, len + otherLen); str.getChars(buf, len); return new String(buf, true); }
concat方法也是經(jīng)常用的方法之一,它先判斷被添加字符串是否為空來決定要不要創(chuàng)建新的對象。
replace()方法
public String replace(char oldChar, char newChar) { //新舊值先對比 if (oldChar != newChar) { int len = value.length; int i = -1; char[] val = value; //找到舊值最開始出現(xiàn)的位置 while (++i < len) { if (val[i] == oldChar) { break; } } //從那個位置開始,直到末尾,用新值代替出現(xiàn)的舊值 if (i < len) { char buf[] = new char[len]; for (int j = 0; j < i; j++) { buf[j] = val[j]; } while (i < len) { char c = val[i]; buf[i] = (c == oldChar) ? newChar : c; i++; } return new String(buf, true); } } return this; }
這個方法也有討巧的地方,例如最開始先找出舊值出現(xiàn)的位置,這樣節(jié)省了一部分對比的時間。replace(String oldStr,String newStr)方法通過正則表達式來判斷。
trim()方法
public String trim() { int len = value.length; int st = 0; char[] val = value; /* avoid getfield opcode */ //找到字符串前段沒有空格的位置 while ((st < len) && (val[st] <= ' ')) { st++; } //找到字符串末尾沒有空格的位置 while ((st < len) && (val[len - 1] <= ' ')) { len--; } //如果前后都沒有出現(xiàn)空格,返回字符串本身 return ((st > 0) || (len < value.length)) ? substring(st, len) : this; }
trim方法就是將字符串中的空白字符串刪掉。
valueOf()方法
public static String valueOf(boolean b) { //如果b為true就返回"true"否則返回"false" return b ? "true" : "false"; } public static String valueOf(char c) { //創(chuàng)建data[]數(shù)組 并把c添加進去 char data[] = {c}; //創(chuàng)建一個新的String對象并進行返回 return new String(data, true); } public static String valueOf(int i) { //調(diào)用Integer對象的toString()方法并進行返回 return Integer.toString(i); } //Integer類中的toString(i)方法 public static String toString(int i) { //是否為Integer最小數(shù),是直接返回 if (i == Integer.MIN_VALUE) return "-2147483648"; //這個i有多少位 int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); //創(chuàng)建一個char數(shù)組 char[] buf = new char[size]; //把i內(nèi)容方法char數(shù)組中區(qū) getChars(i, size, buf); //返回一個String對象 return new String(buf, true); }
split() 方法
public String[] split(String regex) { return split(regex, 0); } //使用到了正則表達式 public String[] split(String regex, int limit) { //.... //源碼有點多了,反正就是里面使用到了正則表達式,進行切分 }
split()
方法用于把一個字符串分割成字符串數(shù)組,返回一個字符串數(shù)組返回的數(shù)組中的字串不包括 regex
自身??蛇x的“limit
”是一個整數(shù),第一個方法中默認是0,允許各位指定要返回的最大數(shù)組的元素個數(shù)。
常見方法源碼分析就這么多了,下面我們再回顧到使用場景中來,尤其是面試中。
String在面試中常見問題
如何比較字符串相同?
在java中比較對象是否相同,通常有兩種方法:
==
equals
方法
注意==
用于基本數(shù)據(jù)類型的比較和用于引用類型的比較的區(qū)別。
==比較基本數(shù)據(jù)類型,比較的是值
==比較引用數(shù)據(jù)類型,比較的是地址值
另外,String
對equals
方法進行了重寫,所以比較字符串咱們還是要使用equals
方法來比較。主要是String
的equals
方法里包含了==
的判斷(請看前面源碼分析部分)。
案例
public class StringDemo { public static void main(String[] args) { String st1 = "abc"; String st2 = "abc"; System.out.println(st1 == st2); System.out.println(st1.equals(st2)); } }
輸出
true true
String str=new String("abc");這行代碼創(chuàng)建了幾個對象?
看下面這段代碼:
String str1 = "abc"; // 在常量池中 String str2 = new String("abc"); // 在堆上
關于這段代碼,創(chuàng)建了幾個對象,網(wǎng)上答案有多重,1個,2個還有3個的。下面我們就來聊聊到底是幾個?
First of all, we need to make it clear that whether it is str1 or str2, they are all String type variables, not objects. Usually, we may call it str2 object. That is just for ease of understanding. In essence, str2 and str1 are both Not an object.
Secondly, String str="abc";
, the string "abc" will be stored in the string constant pool, with only 1 copy. The assignment operation at this time is equal to Creates 0 or 1 objects. If "abc" already exists in the constant pool, no object will be created and the reference will be assigned directly to str1; if "abc" does not exist in the constant pool, an object will be created and the reference will be assigned to str1.
So, what about the form of new String("abc");?
The answer is 1 or 2.
When the JVM encounters the above code, it will first search whether "abc" exists in the constant pool. If the string "abc" does not exist, it will first create this character in the constant pool. string. Then perform the new operation, a String object storing "abc" will be created in the heap memory, and the reference of the object will be assigned to str2. This process creates 2 objects.
Of course, if the corresponding string is found when retrieving the constant pool, only a new String object will be created in the heap, and only one object will be created in this process.
Finally, if you ask String str=new String("abc");
several objects are created, remember: whether "abc" exists in the constant pool, if it exists, create an object ; There is no creation of two objects.
The difference between String and StringBuilder and StringBuffer
Thread safety
Objects in String are immutable and can be understood as constants. Thread safe. AbstractStringBuilder is the common parent class of StringBuilder and StringBuffer, and defines some basic string operations, such as expandCapacity, append, insert, indexOf and other public methods. StringBuffer adds a synchronization lock to the method or adds a synchronization lock to the calling method, so it is thread-safe. StringBuilder does not add synchronization locks to the method, so it is not thread-safe.
Performance
Every time the String type is changed, a new String object will be generated, and then the pointer will point to the new String object. StringBuffer operates on the StringBuffer object itself every time, rather than generating new objects and changing object references. Under the same circumstances, using StringBuilder can only achieve a performance improvement of about 10% to 15% compared to using StringBuffer, but it will run the risk of multi-threading insecurity.
Summary on the use of the three:
操作少量的數(shù)據(jù) ,推薦使用 String
單線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù),推薦使用 StringBuilder
多線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù) ,推薦使用 StringBuffer
String 和 JVM有什么關系?
String 常見的創(chuàng)建方式有兩種,new String() 的方式和直接賦值的方式,直接賦值的方式會先去字符串常量池中查找是否已經(jīng)有此值,如果有則把引用地址直接指向此值,否則會先在常量池中創(chuàng)建,然后再把引用指向此值;而 new String() 的方式一定會先在堆上創(chuàng)建一個字符串對象,然后再去常量池中查詢此字符串的值是否已經(jīng)存在,如果不存在會先在常量池中創(chuàng)建此字符串,然后把引用的值指向此字符串。
JVM中的常量池
字面量—文本字符串,也就是我們舉例中的 public String s = " abc ";
中的 "abc"。
用 final 修飾的成員變量,包括靜態(tài)變量、實例變量和局部變量。
請看下面這段代碼:
String s1 = new String("Java"); String s2 = s1.intern(); String s3 = "Java"; System.out.println(s1 == s2); // false System.out.println(s2 == s3); // true
它們在 JVM 存儲的位置,如下圖所示:
注意:JDK 1.7 之后把永生代換成的元空間,把字符串常量池從方法區(qū)移到了 Java 堆上。
除此之外編譯器還會對 String 字符串做一些優(yōu)化,例如以下代碼:
String s1 = "Ja" + "va"; String s2 = "Java"; System.out.println(s1 == s2);
雖然 s1 拼接了多個字符串,但對比的結果卻是 true,我們使用反編譯工具,看到的結果如下:
Compiled from "StringExample.java" public class com.lagou.interview.StringExample { public com.lagou.interview.StringExample(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 3: 0 public static void main(java.lang.String[]); Code: 0: ldc #2 // String Java 2: astore_1 3: ldc #2 // String Java 5: astore_2 6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 9: aload_1 10: aload_2 11: if_acmpne 18 14: iconst_1 15: goto 19 18: iconst_0 19: invokevirtual #4 // Method java/io/PrintStream.println:(Z)V 22: return LineNumberTable: line 5: 0 line 6: 3 line 7: 6 line 8: 22 }
從編譯代碼 #2 可以看出,代碼 "Ja"+"va" 被直接編譯成了 "Java" ,因此 s1==s2 的結果才是 true,這就是編譯器對字符串優(yōu)化的結果。
如何判斷兩個字符串中含有幾個相同字符
將字符串轉化成數(shù)組 HashMap 方法 字符串直接進行比較 正則表達式 HashSet 方法
String有沒有長度限制?是多少?為什么?
下面先看看length方法源碼:
private final char value[]; public int length() { return value.length; }
length()方法返回的是int類型,那可以得知String類型的長度肯定不能超過Integer.MAX_VALUE
的。
答:首先字符串的內(nèi)容是由一個字符數(shù)組 char[] 來存儲的,由于數(shù)組的長度及索引是整數(shù),且String類中返回字符串長度的方法length() 的返回值也是int ,所以通過查看java源碼中的類Integer我們可以看到Integer的最大范圍是2^31 -1,由于數(shù)組是從0開始的,所以**數(shù)組的最大長度可以使【0~2^31】**通過計算是大概4GB。
但是通過翻閱java虛擬機手冊對class文件格式的定義以及常量池中對String類型的結構體定義我們可以知道對于索引定義了u2,就是無符號占2個字節(jié),2個字節(jié)可以表示的最大范圍是2^16 -1 = 65535。
但是由于JVM需要1個字節(jié)表示結束指令,所以這個范圍就為65534了。超出這個范圍在編譯時期是會報錯的,但是運行時拼接或者賦值的話范圍是在整形的最大范圍。
字符串對象能否用在switch表達式中?
從JDK7
開始的話,我們就可以在switch條件表達式中使用字符串了,也就是說7之前的版本是不可以的。
switch (str.toLowerCase()) { case "tian": value = 1; break; case "jiang": value = 2; break; }
說說String中intern方法
在JDK7
之前的版本,調(diào)用這個方法的時候,會去常量池中查看是否已經(jīng)存在這個常量了,如果已經(jīng)存在,那么直接返回這個常量在常量池中的地址值,如果不存在,則在常量池中創(chuàng)建一個,并返回其地址值。
但是在JDK7
以及之后的版本中,常量池從perm區(qū)搬到了heap區(qū)。intern檢測到這個常量在常量池中不存在的時候,不會直接在常量池中創(chuàng)建該對象了,而是將堆中的這個對象的引用直接存到常量池中,減少內(nèi)存開銷。
下面的案例
public class InternTest { public static void main(String[] args) { String str1 = new String("hello") + new String("world"); str1.intern(); String str2 = "helloworld"; System.out.println(str1 == str2);//true System.out.println(str1.intern() == str2);//true } }
好了,關于Stirng類的分享就到此,歡迎找我探討更多技術問題。
The above is the detailed content of 2w words detailed explanation String, yyds. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

Convert basic data types to strings using Java's String.valueOf() function In Java development, when we need to convert basic data types to strings, a common method is to use the valueOf() function of the String class. This function can accept parameters of basic data types and return the corresponding string representation. In this article, we will explore how to use the String.valueOf() function for basic data type conversions and provide some code examples to

Method of converting char array to string: It can be achieved by assignment. Use {char a[]=" abc d\0efg ";string s=a;} syntax to let the char array directly assign a value to string, and execute the code to complete the conversion.

Replace characters (strings) in a string using Java's String.replace() function In Java, strings are immutable objects, which means that once a string object is created, its value cannot be modified. However, you may encounter situations where you need to replace certain characters or strings in a string. At this time, we can use the replace() method in Java's String class to implement string replacement. The replace() method of String class has two types:

Use Java's String.length() function to get the length of a string. In Java programming, string is a very common data type. We often need to get the length of a string, that is, the number of characters in the string. In Java, we can use the length() function of the String class to get the length of a string. Here is a simple example code: publicclassStringLengthExample{publ

Hello everyone, today I will share with you the basic knowledge of Java: String. Needless to say the importance of the String class, it can be said to be the most used class in our back-end development, so it is necessary to talk about it.

Use Java's String.startsWith() function to determine whether a string starts with a specified prefix. In Java programming, we often need to process and determine strings. One common operation is to determine whether a string begins with a specified prefix. Java provides the startsWith() function of the String class, which can easily implement this function. String's startsWith() function The String class is the most commonly used class in Java

The String.toLowerCase() function is a very useful and common string processing function in Java, which can convert a string to lowercase. In this article, we will introduce how to use this function and give some related code examples. First, let's take a look at the basic syntax of the String.toLowerCase() function. It doesn't have any parameters, just call it. Here is the sample code: Stringstr="Hel

1. Understanding String1. String in JDK First, let’s take a look at the source code of the String class in the JDK. It implements many interfaces. You can see that the String class is modified by final. This means that the String class cannot be inherited and there is no subclass of String. class, so that all people using JDK use the same String class. If String is allowed to be inherited, everyone can extend String. Everyone uses different versions of String, and two different people Using the same method shows different results, which makes it impossible to develop the code. Inheritance and method overriding not only bring flexibility, but also cause many subclasses to behave differently.
