【面試篇】字符串的相關問題與回答,持續更新

注:本欄所有內容來源均從互聯網其他博主查閱而來,整理在一起 供大家閱讀學習,後續會一直補充 轉載來源如下: https://www.cnblogs.com/rookieagle/p/10726694.html

1. 關於equals和==的區別?

(1)對於==,如果作用於基本數據類型的變量(byte,short,char,int,long,float,double,boolean ),則直接比較其存儲的"值"是否相等;如果作用於引用類型的變量(String),則比較的是所指向的對象的地址(即是否指向同一個對象)。

(2)equals方法是基類Object中的方法,因此對於所有的繼承於Object的類都會有該方法。在Object類中,equals方法是用來比較兩個對象的引用是否相等,即是否指向同一個對象。

(3)對於equals方法,注意:equals方法不能作用於基本數據類型的變量。如果沒有對equals方法進行重寫,則比較的是引用類型的變量所指向的對象的地址;而String類對equals方法進行了重寫,用來比較指向的字符串對象所存儲的字符串是否相等。其他的一些類諸如Double,Date,Integer等,都對equals方法進行了重寫用來比較指向的對象所存儲的內容是否相等。

2. String str1="abc"與String str2=new String(“abc”)的區別?

String str2=new String(“abc”)
在編譯器編譯的時候,當編譯器注意到參數爲(“abc”)字符串常量時,即將字符串作爲常量放在class文件中的常量區,並在類加載時放入運行時常量池中.在new操作符被調用時,在java堆中創建一個新的對象,然後對象的內容爲常量池裏面對應字符串的一個拷貝。所以str2是在堆上創建的一個對象.

String str1=“abc”
str1是常量池中的對象.(字符串作爲常量放在class文件中的常量區,並在類加載時放入運行時常量池中)而str2是中的對象
在調用new操作時,對象始終存在於堆中(new操作是進行在程序運行時,即JVM已經初始化結束),而直接字符串賦值時對象實際上是存在於常量池中的,
補充: str1和str2都存在於棧中,str1指向常量池中的常量對象,str2指向堆中的對象

3. String str = new String(“abc”); 創建了幾個對象?
abc 字符串之前沒有用過,這毫無疑問創建了兩個對象,一個是new String 創建的一個新的對象,一個是常量“abc”對象的內容創建出的一個新的String對象.如果:

String a="abc";
 String str = new String("abc"); //這是此語句就創建了一個對象.所以創建幾個對象還需要看字符串常量是不是第一次出現

3. String、StringBuffer、StringBuilder的區別?

(1)可變與不可變:String是不可變字符串對象,StringBuilder和StringBuffer是可變字符串對象(其內部的字符數組長度可變)。

(2)是否多線程安全:String中的對象是不可變的,也就可以理解爲常量,顯然線程安全。StringBuffer 與 StringBuilder 中的方法和功能完全是等價的,只是StringBuffer 中的方法大都採用了synchronized 關鍵字進行修飾,因此是線程安全的,而 StringBuilder 沒有這個修飾,可以被認爲是非線程安全的。

(3)String、StringBuilder、StringBuffer三者的執行效率:
StringBuilder > StringBuffer > String 當然這個是相對的,不一定在所有情況下都是這樣。比如String str = “hello”+ "world"的效率就比 StringBuilder st = new StringBuilder().append(“hello”).append(“world”)要高。因此,這三個類是各有利弊,應當根據不同的情況來進行選擇使用:
當字符串相加操作或者改動較少的情況下,建議使用 String str="hello"這種形式;
當字符串相加操作較多的情況下,建議使用StringBuilder,如果採用了多線程,則使用StringBuffer。

4. 談一談常量池

我們知道字符串的分配和其他對象分配一樣,是需要消耗高昂的時間和空間的,而且字符串我們使用的非常多。JVM爲了提高性能和減少內存的開銷,在實例化字符串的時候進行了一些優化:使用字符串常量池。每當我們創建字符串常量時,JVM會首先檢查字符串常量池,如果該字符串已經存在常量池中,那麼就直接返回常量池中的實例引用。如果字符串不存在常量池中,就會實例化該字符串並且將其放到常量池中。由於String字符串的不可變性我們可以十分肯定常量池中一定不存在兩個相同的字符串(這點對理解上面至關重要)。

Java中的常量池,實際上分爲兩種形態:靜態常量池和運行時常量池。
所謂靜態常量池,即*.class文件中的常量池,class文件中的常量池不僅僅包含字符串(數字)字面量,還包含類、方法的信息,佔用class文件絕大部分空間。

而運行時常量池,則是jvm虛擬機在完成類裝載操作後,將class文件中的常量池載入到內存中,並保存在方法區中,我們常說的常量池,就是指方法區中的運行時常量池

5. 談一談字符串池的優缺點:
字符串池的優點就是避免了相同內容的字符串的創建,節省了內存,省去了創建相同字符串的時間,同時提升了性能;另一方面,字符串池的缺點就是犧牲了JVM在常量池中遍歷對象所需要的時間,不過其時間成本相比而言比較低。

6. 爲什麼針對安全保密高的信息,char[]比String更好?

因爲String是不可變的,就是說它一旦創建,就不能更改了,直到垃圾收集器將它回收走。而字符數組中的元素是可以更改的(譯者注:這就意味着你就可以在使用完之後將其更改,而不會保留原始的數據)。所以使用字符數組的話,安全保密性高的信息(如密碼之類的)將不會存在於系統中被他人看到。

字符串內部原理的考察問題彙總

public  viod test{
    String  str1="aaa";
    String  str2="aaa";
    System.out.println(str1==str2);
} 
輸出的結果: true
public void test2(){
    String str3=new String("aaa");
    String str4=new String("aaa");
    System.out.println("===========test2============");
    System.out.println(str3==str4);//false 可以看出用new的方式是生成不同的對象
}
輸出的結果爲:false
/**
 * 編譯期確定
 */
public void test3(){
    String s0="helloworld";
    String s1="helloworld";
    String s2="hello"+"world";
    System.out.println("===========test3============");
    System.out.println(s0==s1); //true 可以看出s0跟s1是指向同一個對象
    System.out.println(s0==s2); //true 可以看出s0跟s2是指向同一個對象
}
輸出結果:True,True。
/**
 * 編譯期無法確定
 */
public void test4(){
    String s0="helloworld";
    String s1=new String("helloworld");
    String s2="hello" + new String("world");
    System.out.println("===========test4============");
    System.out.println( s0==s1 ); //false  
    System.out.println( s0==s2 ); //false
    System.out.println( s1==s2 ); //false
}
答案:false,false,false。
**
 * 繼續-編譯期無法確定
 */
public void test5(){
    String str1="abc";   
    String str2="def";   
    String str3=str1+str2;
    System.out.println("===========test5============");
    System.out.println(str3=="abcdef"); //false
輸出結果: false。
/*
 * 編譯期無法確定
 */
public void test7(){
    String s0 = "ab";
    String s1 = "b";
    String s2 = "a" + s1;
    System.out.println("===========test7============");
    System.out.println((s0 == s2)); //result = false
}
/**
 * 比較字符串常量的“+”和字符串引用的“+”的區別
 */
public void test8(){
    String test="javalanguagespecification";
    String str="java";
    String str1="language";
    String str2="specification";
    System.out.println("===========test8============");
    System.out.println(test == "java" + "language" + "specification");
    System.out.println(test == str + str1 + str2);
    執行上述代碼,結果爲:true、false。
}

分析:爲什麼出現上面的結果呢?這是因爲,字符串字面量拼接操作是在Java編譯器編譯期間就執行了,也就是說編譯器編譯時,直接把"java"、“language"和"specification"這三個字面量進行”+“操作得到一個"javalanguagespecification” 常量,並且直接將這個常量放入字符串池中,這樣做實際上是一種優化,將3個字面量合成一個,避免了創建多餘的字符串對象。而字符串引用的"+“運算是在Java運行期間執行的,即str + str2 + str3在程序執行期間纔會進行計算,它會在堆內存中重新創建一個拼接後的字符串對象。總結來說就是:字面量”+“拼接是在編譯期間進行的,拼接後的字符串存放在字符串池中;而字符串引用的”+"拼接運算實在運行時進行的,新創建的字符串存放在堆中。

對於直接相加字符串,效率很高,因爲在編譯器便確定了它的值,也就是說形如"I"+“love”+“java”; 的字符串相加,在編譯期間便被優化成了"Ilovejava"。對於間接相加(即包含字符串引用),形如s1+s2+s3; 效率要比直接相加低,因爲在編譯器不會對引用變量進行優化。

public void test9(){
    String s0 = "ab";
    final String s1 = "b";
    String s2 = "a" + s1;  
    System.out.println("===========test9============");
    System.out.println((s0 == s2)); //result = true
}

輸出結果:true。
分析:和例子7中唯一不同的是s1字符串加了final修飾,對於final修飾的變量,
它在編譯時被解析爲常量值的一個本地拷貝存儲到自己的常量池中或嵌入到它的字節碼流中。
所以此時的"a" + s1和"a" + "b"效果是一樣的。故上面程序的結果爲true。

/**
 * 編譯期無法確定
 */
public void test10(){
    String s0 = "ab";
    final String s1 = getS1();
    String s2 = "a" + s1;
    System.out.println("===========test10============");
    System.out.println((s0 == s2)); //result = false
    
}
 
private static String getS1() {  
    return "b";   
}

執行上述代碼,結果爲:false。

分析:這裏面雖然將s1用final修飾了,但是由於其賦值是通過方法調用返回的,那麼它的值只能在運行期間確定,因此s0和s2指向的不是同一個對象,故上面程序的結果爲false。

基礎知識模塊

String 類的常用方法都有那些?
indexOf():返回指定字符的索引。
charAt():返回指定索引處的字符。
replace():字符串替換。
trim():去除字符串兩端空白。
split():分割字符串,返回一個分割後的字符串數組。
getBytes():返回字符串的 byte 類型數組。
length():返回字符串長度。
toLowerCase():將字符串轉成小寫字母。
toUpperCase():將字符串轉成大寫字符。
substring():截取字符串。
equals():字符串比較。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章