面試話癆(二)C:JAVA String,別覺得你穿個馬甲我就不認識你了

  面試話癆系列是從技術廣度的角度去回答面試官提的問題,適合萌新觀看!html

   面試官,別再問我火箭怎麼造了,我知道螺絲的四種擰法,你想聽嗎?java


  String相關的題目,是面試中常常考察的點,當面試中遇到了String相關的問題,咱們能夠這麼聊:面試

一:String底層結構算法

  從底層結構上來講,jdk1.8的String,底層是char[]。我在工做中幾乎不多用到char[],由於List太好用了,我寧願用List<Character>也不想用char[],由於之前學C時體會過用char[]的痛苦,長度必須事先設定好,也沒有豐富的API去處理數據。因此剛開始用java時,以爲String這個類簡直太好用了(直視面試官的眼睛,露出有點很差意思的笑)。後來才知道,原來是源碼在替咱們負重前行。數據庫

  好比咱們都知道String的拼接,大部分狀況下都是新建一個空間(這裏能夠用稍微緩慢不肯定的語氣說這句話,稍有心的面試官就會問你什麼狀況下不會新建空間,你就能夠回答:1.連續相加時jdk會自動優化成一次空間的新建,2.兩個字符串常量相加的值若是是已經存在的字符串常量,那麼會直接指向這個已存在的字符常量),新建的緣由就是由於底層是一個char[],沒法直接擴容。而新建空間的花費不少,因此對於有屢次拼接需求的狀況,我會酌情選擇StringBuffer和StringBuilder,主要是看是否存在多線程的狀況,存在的話就用StringBuffer(線程安全的方式,涉及的知識點不少,能夠專門寫一章,先留個傳送門:面試話癆(三)我會鎖的三種配法,您配嗎?)。編程

 

  另外,我有次看jdk11裏面String的底層已經變成了byte[],順手查了一下,說是用byte[]存的話,能夠減小大概一半的內存佔用,再具體的怎麼實現怎麼優化的沒太仔細瞭解。(對於本身不太懂的知識,介紹完本身懂得部分之後,必定要接一句:再往下本身就不清楚了之類的話。這樣面試官就不會再接着問你這個問題)。數組

  題外話:字符之間能夠直接使用‘+’和‘-’運算符,實際計算的是字符對應的ASCII碼的位置的加減,如 ‘1’ - ‘0’ = (int)1,用這個方法能夠快速的完成char到int的強轉,不少String類的題目都須要用到這個方法。緩存

 

二:final修飾符安全

  由於char[]自己就是一個不能擴容的數組,因此用不可變的常量去修飾字符串就很適合。另外,final的特性也能爲String帶來不少的好處:數據結構

  1. 安全方面來講,密碼、我的信息等基本都是以String爲載體來進行存儲的,final修飾的String類不能夠被繼承,建立的對象也不能夠被改變,能夠保證關鍵數據的安全性。

  2. 性能方面來講,final修飾後,String就被放入了常量池,常量池中有專門的字符串常量池,JVM能夠將多個同樣的String指向同一個地址,其中有任意一個String改變時,由於final的特性都會去從新建一個地址(或者指向另一個值剛好相同的地址),不會影響原來的值;另外,String的不可變性讓它的hashcode是固定的,能夠被緩存的,用來作Map的key運算更快捷;還有,final修飾後,也不會存在多線程安全的問題。

  (原本想接着說一下常量的,可是一想到常量,腦子裏就蹦出了方法區,堆,棧,JDK版本更替、GC方式,類加載,JVM內存模型,Java內存模型,可見性,volatile,自旋鎖,太多了。先留個坑吧。面試話癆(四)常量在哪裏呀,常量在哪裏)。

  與final長得像的,還有finally和finalize。finally就是跟在try或者catch後面的一個關鍵字,之前咱們學的是,try以後一定會執行finally,但其實這個是有前提的,就是程序不崩潰或者不被強制結束,try中加一句 System.exit(0); ,finally就不會被執行。

  至於finalize,這個方法幾乎沒人用了,它是Object類中就自帶的方法,學名析構函數,據說是在java剛出生時,爲了迎合C編程人員的習慣添加的方法。在對象快要被回收時調用且只會被調用一次。若是finalize中的代碼將另一個指針指向了該對象,那麼JVM就會放棄該對象的回收。等到下一次該對象又不可達了,JVM就會直接回收,不會再調用finalize方法。所以方法存在不肯定性,不多被使用。

 

三:equals

  在Obejct中,equals和==是同樣的,都是直接比較數據的存放地址是否一致,而在String中,equals方法被重寫成三個步驟的判斷。

  

  HashMap中的equals大體也是使用了這三個步驟的判斷:地址是否相等 --> size是否相等 --> 每個key是否有equals的key,對應的value是否equals。

  不一樣的類對於equals的實現方式不同,但他們都遵循若hashcode不相等,則equals也不相等的原則。這樣作的目的主要是爲了讓Hash類集合插入值時的重複斷定更合理:

   

  試想一下,假設兩個身份證對象的hashcode不相等,equals卻相等,那麼兩個相同身份證信息就會被放入HashSet中了!這會對咱們的編碼形成很大的困擾,因此對於須要被用於Hash的key值的對象(HashSet的值插入至關因而將值放入了key中,再插入了一個固定value值的HashMap),咱們須要知足若hashcode不相等,則equals也不相等的原則。

  HashSet中的元素重複斷定,優先斷定hashcode的緣由是:hashcode經過與HashSet的大小取餘之後,能夠快速的定位到可能相等的元素的位置,這時再對該位置上存在的元素的進行equals就行,只要hash分佈的足夠均勻,該操做的時間複雜度就接近於O(1),而若去掉hashcode尋址,直接使用equals對n個數進行對比的話,時間複雜度就是O(n)。

  說到這裏還能夠提一個常常被問到的問題,HashMap的查詢時間複雜度是多少?

 

  如我上面的分析同樣,時間複雜度是接近於O(1)的,固然也會有O(n)的狀況。好比新建了一個類,hashcode值所有返回1,這樣全部的值都會接到一個鏈表上,數組+鏈表的結構就退化成純鏈表,時間複雜度就變成了O(n)。jdk1.8之後,鏈表長度超過8,而且數組長度大於64時,鏈表會變成紅黑樹,紅黑樹的遍歷時間複雜度爲O(log(n))。(紅黑樹、B樹、索引、數據庫、圖、廣度搜索、深度搜索相關的數據結構及算法後面再說。面試話癆(N)尚未頭緒不知道什麼時間寫)。

 


  最後,送你們一道我特別喜歡的面試題。 

    請將一段字符串轉換成整數,不要使用parseInt。

 

    public int MyParseInt(String str) throws Exception{
        /*
            1.數據校驗
            (無論面試過程當中遇到什麼題,均可以先優雅的寫下
                //1.數據校驗
              而後成竹在胸的寫一些有的沒的校驗,寫的過程當中再想後面應該怎麼寫。
              寫註釋+周全的校驗是寫代碼的基本素養)
         */
        String errMsg = check(str);
        if (errMsg != null) {
            throw new Exception(errMsg);
        }
        char[] c = str.toCharArray();
        int result = 0;
        for (int i = 0; i < c.length; i++) {
            result = result * 10 + (c[i] - '0');
        }
        return result;
    }

    /**
     * 
     * @param str 待校驗的數據
     * @return 錯誤信息,沒有錯誤時返回null
     */
    private String check(String str) {
        if (str == null) {
            return "輸入不能爲空!";
        }
        /*
            不多有人能一遍寫出不用調試沒有bug的代碼,更別說在面試那麼緊張的氣氛中了
            因此,在寫完可以基本實現題目要求的代碼之後,最好能想下代碼中不足的地方,而後坦誠的告訴面試官:
                這個代碼還有須要改進的地方,好比能夠添加對 負號、正號、小數點的支持,好比沒有校驗int的閥值,
                若是是工做中遇到,我確定能解決這些問題。
         */
        if (str.replaceAll("[0-9]","").length() > 0){
            return "包含非法字符";
        }
        return null;
    }

   本章中提到的一些面試回答技巧這裏再寫一下,我我的以爲挺有用的:

  一、 對於本身擅長的問題,能夠在問題中留下一些點,吸引面試官繼續提問。

  二、 對於本身不擅長的問題,回答完本身會的部分之後,能夠直接說再往下本身就不懂了,不要不懂裝懂,讓面試官繼續問下去後再回答不知道。

  三、 若是面試官已經問了你不知道的問題,儘可能從性能和安全方面,說出一個答案。直接先坦誠的告訴面試官你不知道,只是根據我的經驗這個應該是這樣的,由於這樣對於安全/性能的優勢是xxx。


   目錄以下:

   面試話癆(一)讓咱們來熱切的討論這個養豬場吧

   面試話癆(二)C:JAVA String,別覺得你穿個馬甲我就不認識你了

相關文章
相關標籤/搜索