1. 程式人生 > >string類總結第二部分實戰練習

string類總結第二部分實戰練習

指向 可能 都是 相同 內存空間 info 什麽 調用 ascii碼表

第二部門:實戰練習

  昨天由於時間原因,這個部分應該在同一個文章中的,無奈只能今天再開一個了,今天主要是講一些面試題

一:equals和==的區別

  最簡單的面試題,也是最基礎的,我估計每個學習java的人都在網上看到過該問題,答案一額很簡單:equals是方法,當然只能對象來調用,所以equals只能用來比較引用類型,若該類型重寫了equals方法,那比較的就是內容,比如說String類就是比較的每一對對應的字符,==既能比較基本類型,也能比較引用類型,基本類型比較的是值,引用類型比較的是地址值,總之==比較的是棧內存空間中存放的數值。

二:hashCode方法返回的是什麽,有什麽用

  首先看幾行代碼,對於沒重寫hashCode方法的類的對象,返回的是該對象在堆內存空間的內存地址。也就是和System.identityHashCode方法返回的一樣,System.indetityHashCode返回的就是地址值。

        Student stu = new Student();
        System.out.println(stu.hashCode());//1554874502
        System.out.println(System.identityHashCode(stu));//1554874502

對於重寫hashCode方法的類,我們拿String來舉例,每個自字符串的哈希碼其實是通過一個表達式來計算的s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1],s[0]是指下標為0的字符的二進制數的十進制表示,對於a來說就是97(ASCII碼表可查)

        System.out.println("a".hashCode());//97            97=97*31^0
        System.out.println("aa".hashCode());//3104      3104=97+97*31^1
        System.out.println("aaa".hashCode());//96321     96321=3104+97*31^2
        System.out.println("aaaa".hashCode());//2986048    。。。
        System.out.println("aaaaa".hashCode());//
92567585    。。。 System.out.println("aaaaaa".hashCode());//-1425372064    因為返回的是int類型,這裏越界了,所有事負數 System.out.println("aaaaaaa".hashCode());//-1236860927 System.out.println("aaaaaaaa".hashCode());//312017024 System.out.println("aaaaaaaaa".hashCode());//1082593249

所有,我們現在明白了重寫hashCode是通過某個表達式,來規定一個哈希碼,這個值在很大程度上保證了唯一性,但是因為越界有可能存在倆個不同的字符串但卻有相同的哈希碼,所以hashCode的作用是在哈希表數據結構中(比如hashTable,hashMap)來判斷倆個元素是否相等的第一個條件,若不相等,那麽這兩個元素的內容肯定不相等(因為他們都是通過某個同樣的表達式對屬性進行操作得到的),若相等,則再進行equals的比較。

特此聲明:對於將要往哈希表數據結構中存放的類型必須重寫hashCode方法,因為如果不重寫,hashCode返回的就是地址值,那每兩個new出來的對象的地址值一定不相等,但是他們的內容卻一樣,這不亂套了麽

        String s1 = "gollong";
        String s2 = new String(s1);
        System.out.println(s1.hashCode());//204502272
        System.out.println(s2.hashCode());//204502272
        System.out.println(s1==s2);//false
        System.out.println(System.identityHashCode(s1));//1554874502
        System.out.println(System.identityHashCode(s2));//1846274136

例如:由於不重寫hashCode,set集合當中存入了兩個一樣的對象

        HashSet<Student> set = new HashSet<Student>();
        Student s1 = new Student("gol", 1.0, 1);
        Student s2 = new Student("gol", 1.0, 1);
        System.out.println(s1.hashCode());//1554874502
        System.out.println(s2.hashCode());//1846274136
        set.add(s1);
        set.add(s2);
        System.out.println(set);
//[Student [name=gol, scaly=1.0, age=1], Student [name=gol, scaly=1.0, age=1]]

三:intern方法的作用

  我相信知道這個方法的人很少,明白它是做什麽的更少,這就涉及到昨天的那個String s = new String("gollong")創建了幾個對象,答案在下圖中

技術分享圖片

現在存在兩個對象,一個在堆中,一個在常量池中,我們知道現在的這個s引用指向的是堆內存中的這個"gollong",而intern方法的作用就是將這個引用的指向直接改到常量池中的"gollong"上,也就是變成這樣:

技術分享圖片

所以在下面的代碼中,s1和s2.intern的地址值是一樣的,本來s2是指向堆內存中的。現在指向了常量池中。

        String s1 = "gollong";
        String s2 = new String(s1);
        System.out.println(s1.hashCode());//204502272
        System.out.println(s2.hashCode());//204502272
        System.out.println(s1==s2);//false
        System.out.println(System.identityHashCode(s1));//1554874502
        System.out.println(System.identityHashCode(s2));//1846274136

        System.out.println(s1==s2.intern());//true
        System.out.println(System.identityHashCode(s1));//1554874502
        System.out.println(System.identityHashCode(s2.intern()));//1554874502

四:s = "" 和 s = new String("")有什麽區別

  這個問題,咋一看你覺得自己不知道,其實他倆和s = "gollong" ,s = new String(""gollong)沒有任何區別,都有對象的引用,字符串(雖然書空的)也分別存放在堆中和常量池中,詳情見下面代碼中的地址值。空字符串的哈下面默認為0.

        String s3 = "";
        String s4 = new String();
        System.out.println(s3==s4);//false
        System.out.println(s3.hashCode());//0
        System.out.println(s4.hashCode());//0
        System.out.println(System.identityHashCode(s3));//1554874502
        System.out.println(System.identityHashCode(s4));//1846274136

五:String對象的初始化時機:

  我們都知道java程序是需要通過先編譯生成class文件再執行的,那麽在編譯的時候其實是對一些代碼進行轉換到,比如說下面的代碼同樣是+號鏈接倆個字符串對象,為什麽s3==s4是true

        String s1 = "gol";
        String s2 = "long";
        String s3 = "gollong";
        String s4 = "gol"+"long";
        String s5 = s1+s2;    
        String s6 = s1+"long";
        String s7 = "gol"+s2;
        System.out.println(s3==s4);//true        
        System.out.println(s3==s5);//false
        System.out.println(s3==s6);//false
        System.out.println(s3==s7);//false

下面是通過反編譯器的到的class文件代碼

        String s1 = "gol";
        String s2 = "long";
        String s3 = "gollong";
        String s4 = "gollong";
        String s5 = (new StringBuilder(String.valueOf(s1))).append(s2).toString();
        String s6 = (new StringBuilder(String.valueOf(s1))).append("long").toString();
        String s7 = (new StringBuilder("gol")).append(s2).toString();
        System.out.println(s3 == s4);
        System.out.println(s3 == s5);
        System.out.println(s3 == s6);
        System.out.println(s3 == s7);

我們很清楚的看到了雖然都是+號,但是是有區別的,當編譯器識別到左右兩端都是常量時,將直接在常量池中創建該對象,然後直接賦值,而對於變量名s1或s2由於編譯器並不不知道他們的值是多少,其實得到的是一個在堆中的String對象,其地址值當然和常量池中的不一樣了!

string類總結第二部分實戰練習