string類總結第二部分實戰練習
第二部門:實戰練習
昨天由於時間原因,這個部分應該在同一個文章中的,無奈只能今天再開一個了,今天主要是講一些面試題
一: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類總結第二部分實戰練習