問題及答案來源自《Java程序員面試筆試寶典》第四章 Java基礎知識 4.5字符串與數組
1、字符串創建與存儲的機制是什么?
Java中字符串聲明與初始化主要有兩種情況:
(1)String s1 = new String("abc")與String s2 = new String("abc")語句
執行String s1 = new String("abc")語句,字符串池中不存在"abc",則會創建一個字符串常量"abc",
並將它添加到字符串常量池中,然后new String()會在堆中創建一個新的對象,s1指向堆中的String對象
緊接着創建String s2 = new String("abc")語句,因為字符串常量池中已經有了字符串常量"abc",所以
不會再創建"abc",直接new String()在堆中創建一個新的對象,然后使用s2指向這個對象
s1與s2指向堆中的不同String對象,地址自然也不相同
(2)String s1 = "abc"語句與String s2 = "abc"語句
在JVM中存在着一個字符串常量池,其中保存了着很多String 對象,s1,s2引用的是同一個常量池中的對象。
當創建一個字符串常量時,例如String s1 = "abc",會首先在字符串常量池中查找是否已經有相同的字符串被定義,
若已經定義,則直接獲取對其的引用,此時不需要創建字符串常量"abc",如果沒有定義,則首先創建字符串常量
"abc",然后把它加入到字符串池中,再將引用返回
例子1:
String s1 = new String("abc"); // 先查找常量區有無"abc"常量,若無則將其"abc"添加到常量區,再在堆中創建對象,將s1指向堆中的對象
String s2 = new String("abc"); // 發現在常量區已經有了"abc",在堆中創建對象,將s2指向堆中的對象
例子2:
String s1 = "abc"; // 在常量區里面創建一個"abc"字符串對象,s1獲取對其的引用
String s2 = "abc"; // 發現在常量區已經有了"abc",s2直接獲取對其的引用
引申 - 對於String類型的變量s,賦值語句s=null和賦值語句s=""有什么區別?
s=null,是指s不指向任何一個字符串;s=""中的s指向空字符串
筆試題 - new String("abc")創建了幾個對象?
一個或兩個,如果常量池中原來就有"abc",那么只創建一個對象,否則創建兩個對象
2、==、equals和hashCode有什么區別?
==:是運算符,用於比較兩個變量是否相等。當比較對象時,比較的是對象在內存中的地址
equals方法:用於比較兩個對象是否相等,默認Object類的equals方法是比較兩個對象的地址,跟==的結果一樣
如果一個類沒有自己定義equals方法,它默認的equals方法就是使用"=="運算符,也就是在比較兩個變量指向
的對象是否是同一個對象
hashCode方法:是用來鑒定兩個對象是否相等,默認Object類中的hashCode方法返回對象在內存中地址轉換成
的一個int值,所以如果沒有重寫hashCode()方法,任何對象的hashCode()方法都是不相等的。返回一個離散的int型整數。
在集合類操作中使用,為了提高查詢速度。(HashMap,HashSet等)

equals方法實際使用:
1 class Person{ 2 3 } 4 5 public class ObjectDemo { 6 public static void main(String[] args) { 7 // Person類沒有重寫equals方法 而Object類中的equals方法是比較兩個對象的地址 8 Person p1 = new Person(); 9 Person p2 = new Person(); 10 System.out.println(p1.equals(p2)); // false 11 12 // String中重寫了equals方法 比較的是兩個字符串的內容 13 String s1 = new String("hello"); 14 String s2 = new String("hello"); 15 System.out.println(s1.equals(s2)); // true 16 17 String s3 = "hello"; 18 String s4 = "hello"; 19 System.out.println(s3.equals(s4)); // true 20 } 21 }
java中的數據類型,可分為兩類:
(1)基本數據類型
Java中基本數據類型有八種:byte,short,char,int,long,float,double,boolean,
他們之間的比較,用雙等號(==),比較的是他們的值。
(2)復合數據類型(類)
當他們用(==)進行比較的時候,比較的是他們在內存中的存放地址,所以,除非是同一個new
出來的對象,他們的比較后的結果為true,否則比較后結果為false。
在Java的Object中的基類中定義了一個equals的方法,這個方法的初始行為是比較對象的內存地址,
但在一些類庫當中這個方法被覆蓋掉了,如String等,在這些類當中equals有其自身的實現,而不再是
比較對象的內存地址了
equals方法和hashCode方法的關系(用於set、map):
hashCode方法只有在set或map集合中用到
當覆蓋了equals方法時,比較對象是否相等將通過覆蓋后的equals方法進行比較(判斷對象的內容是否相等)
將對象放入到集合中時,首先判斷要放入對象的hashCode值與集合中的任意一個元素的hashCode值是否相等,
如果不相等直接將該對象放入集合中。如果hashCode值相等,然后再通過equals方法判斷要放入對象與集合中的
任意一個對象是否相等,如果equals判斷不相等,直接將該元素放入到集合中,否則不放入。

3、String、StringBuffer、StringBuilder和SringTokenizer有什么區別?
Java中有5個類可以對字符或字符串進行操作,分別是Character、String、StringBuffer、StringBuilder
和SringTokenizer,其中Character用於單個字符操作,String用於字符串操作(屬於不可變類),而
StringBuffer也是用於字符串操作(屬於可變類)
Character的API:
- boolean isDigit(char ch) 確定指定字符是否為數字。
- boolean isLowerCase(char ch) 確定是否是小寫字母字符
- boolean isUpperCase(char ch) 確定是否大寫字母字符
- char toLowerCase(char ch) 將給定字符轉換成大寫字符
- char toUpperCase(char ch) 將給定字符轉換成小寫字符
String的API:
1 引用類型String中的方法(4532) 2 第一組: 判斷方法 3 boolean equals(String str); // 比較兩個字符串的內容是否相等 4 boolean equalsIgnoreCase(String str); // 比較兩個字符串的內容是否相等(忽略大小寫) 5 boolean startsWith(String subStr); // 判斷某個字符串是否以指定的子串開頭 6 boolean endsWith(String subStr): // 判斷某個字符串是否以指定的子串結尾 7 8 第二組: 獲取方法 9 int length(); // 獲取字符串中字符個數 10 char charAt(int index); // 謀取字符串某一個字符(指定下標) 11 String subString(int startIndex); // 從指定下標開始截取字符串,直到字符串末尾 12 String subString(int startIndex, int endIndex); // 從指定下標開始截取字符串,到指定下標結束(左閉右開) 13 int indexOf(String subStr); // 獲取子串第一次出現的下標 14 15 第三組: 轉換方法 16 String toLowerCase(); // 轉成小寫串 17 String toUpperCase(); // 轉成大寫串 18 Char[] toCharArray(); // 變成字符數組 19 20 第四組: 其他方法 21 String trim(); // 去掉字符串兩端的空格 22 String[] split(String str); // 切割字符串
String和StringBuffer的區別:
String是不可變類,一旦對象創建其值將不能改變,而StringBuffer是可變類,對象創建后值可以改變
實例化String時可以用賦值也可以用new初始化,而實例化StringBuffer只能用new初始化
String字符串修改的原理:
當用String類型來對字符串進行修改時,其實現方法是首先創建一個StringBuffer對象,然后調用StringBuffer的
append方法,最后調用StringBuffer的toString方法把結果返回
1 String s = "Hello"; 2 s += " World"; 3 // 以上代碼等價於 4 StringBuffer sb = new StringBuffer(s); 5 sb.append(" World"); 6 s = sb.toString();
StringBuilder和StringBuffer:
StringBuilder也是可以被修改的字符串,它與StringBuffer類似,都是字符串緩沖區,但是StringBuilder
不是線程安全的,如果只是在單線程中使用字符串緩沖區,那么StringBuilder的效率會更高些
而StringBuffer在必要時可以對方法進行同步,所以任意特定實例上的操作就好像是以串行順序
發生,該順序與所涉及的每個線程進行的方法調用順序一致
因此在只有單線程訪問時可以使用StringBuilder,當有多個線程訪問時最好用線程安全的StringBuffer
StringTokenizer:
StringTokenizer是用來分割字符串的工具類,示例如下:
1 public class Demo{ 2 public static void main(String[] args) { 3 StringTokenizer st = new StringTokenizer("Welcome to our country"); 4 while(st.hasMoreTokens()){ 5 System.out.println(st.nextToken()); 6 } 7 } 8 }
輸出結果:
Welcome
to
our
country
總結:
在執行效率方面,StringBuilder最高,StringBuffer次之,String最低,使用如下:
- 如果操作的數據量比較小,應優先使用String類
- 如果是在單線程下操作大量數據應優先使用StringBuilder類
- 如果是在多線程下操作大量數據應優先使用StringBuffer類
4、Java中數組是不是對象?
數組是指具有相同類型的數據的集合,它們一般具有固定的長度,並且在內存中占據連續的空間
在C/C++中,數組名只是一個指針,這個指針指向了數組的首元素,沒有屬性也沒有方法
而在Java中,數組不僅具有自己的屬性,還有方法可以調用,故在Java中數組是對象
每個數組類型都有其對於的類型,可以通過instanceof來判斷數據的類型,如下:
1 public class FindLeftAndRightBigger { 2 public static void main(String[] args) { 3 int[] a = {1, 2, 3}; 4 int[][] b = new int[2][3]; 5 String[] s = {"asfd", "saf"}; 6 if(a instanceof int[]){ 7 System.out.println("int[]"); 8 } 9 if(b instanceof int[][]){ 10 System.out.println("int[][]"); 11 } 12 if(s instanceof String[]){ 13 System.out.println("String[]"); 14 } 15 } 16 } 17 // 輸出結果: 18 // int[] 19 // int[][]
5、Java數組的初始化方法有哪幾種?
Java數組的初始化方法如下:
1 // 一維數組聲明: 2 type arrayName[] 或 type[] arrayName 3 // 二維數組聲明: 4 type arrayName[][] 或 type[][] arrayName 或 type[] arrayName[] 5 // 數組初始化:可以在聲明時直接賦值也可以在之后new 6 // 注:type既可以是基本數據類型也可以是類 7 8 // 一維數組初始化實例: 9 int[] a = new int[5]; // 動態創建了一個包含5個整形值得數組,默認值為0 10 int[] a = {1, 2, 3}; // 聲明一個數組並初始化 11 12 // 二維數組初始化實例: 13 // Java中二維數組的第二維長度可以不同 14 int[][] arr = {{1, 2}, {3, 4, 5}}; 15 int[][] arr = new int[2][]; 16 arr[0] = new int[]{1, 2}; 17 arr[1] = new int[]{3, 4, 5};
注:原生類指未被實例化的類,數組是實例化、被分配空間的類,數組不屬於原生類
6、length屬性和length()方法有什么區別?
- length屬性:獲取數組長度
- length方法:計算字符串長度
1 public class test{f 2 public static void testArray(int[] arr){ 3 System.out.println("數組長度為" + arr.length); 4 } 5 6 public static void testString(String s){ 7 System.out.println("字符串長度為" + s.length()); 8 } 9 10 public static void main(String[] args) { 11 testArray(new int[]{1, 2, 3}); // 3 12 testString("hello"); // 5 13 } 14 }
