Java SE 之 String 、 Object 類的基本方法 和 集合 丨【奔跑吧!JAVA】(java培訓學校)

      網友投稿 874 2025-03-31

      一、STRING 和 Object 類的基本方法


      1. STRING 類常用的方法

      2. Object 類常用的方法

      Object類有11個成員方法,加上一個構造方法。下面分別簡介一下這些方法。

      (1)public final native Class getClass()//native方法,用于返回當前運行時對象的Class對象,使用了final關鍵字修飾,故不允許子類重寫。

      (2)public native int hashCode()//native方法,用于返回對象的哈希碼,主要使用在哈希表中,比如 JDK中的HashMap。

      (3)public boolean equals(Object obj)//用于比較2個對象的內存地址是否相等,String類對該方法進行了重寫,用戶比較字符串的值是否相等。

      (4)protected native Object clone() throws CloneNotSupportedException//naitive方法,用于創建并返回當前對象的一份拷貝。一般情況下,對于任何對象x,表達式x.clone()!=x為true, x.clone().getClass()==x.getClass()為true。Object本身沒有實現Cloneable接口,所以不重寫clone方法
      并且進行調用的話會發生CloneNotSupportedException異常。

      (5)public String toString(//返回類的名字@實例的哈希碼的16進制的字符串。建議Object所有的子類都重寫這個方法。

      (6)public final native void notify()//native方法,并且不能重寫。喚醒一個在此對象監視器上等待的線程(監視器相當于就是鎖的概念)。如果有多個線程在等待只會任意喚醒一個。

      (7)public final native void notifyAll()//native方法,并且不能重寫。跟notify一樣,唯一的區別就是會喚醒在此對象監視器上等待的所有線程,而不是一個線程。

      (8)public final native void wait(long timeout) throws InterruptedException//native方法,并且不能重寫。暫停線程的執行。注意: sleep方法沒有釋放鎖,而wait方法釋放了鎖。timeout是等待時間。

      (9)public final void wait(long timeout,int nanos) throws InterruptedException/l多了nanos 參數,這個參數表示額外時間(以毫微秒為單位,范圍是0-999999)。所以超時的時間還需要加上nanos毫秒。

      (10)public final void wait()throws InterruptedException//跟之前的2個wait方法一樣,只不過該方法一直等待,沒有超時時間這個概念

      (11)protected void finalize() throws Throwable { //實例被垃圾回收器回收的時候觸發的操作

      三、常見面試題

      1? String . StringBuilder . StringBuffer 的區別?

      Java平臺提供了兩種類型的字符串: String 和 StringBuffer/StringBuilder,它們都可以儲存和操作字符串,區別如下。

      1) String 是只讀字符串,也就意味著String 引用的字符串內容是不能被改變的。初學者可能會有這樣的誤解:

      1.String str ="abc";

      2. str =“bcd";

      如上,字符串str明明是可以改變的呀!其實不然,str僅僅是一個引用對象,它指向一個字符串對象"abc"。第二行代碼的含義是讓 str 重新指向了一個新的字符串"bcd"對象,而"abc"對象并沒有任何改變,只不過該對象已經成為一個不可及對象罷了。

      2)StringBuffer/StringBuilder表示的字符串對象可以直接進行修改。

      3) StringBuilder是Java5中引入的,它和StringBuffer 的方法完全相同,區別在于它是在單線程環境下使用的,因為它的所有方法都沒有被synchronized 修飾,因此它的效率理論上也比StringBuffer要高。

      2? equals()與==的區別

      基本類型(int,char,long,boolean等)都是用==判斷相等性。對象引用的話,判斷引用所指的對象是否是同一個。equals是Object 的成員函數,默認是使用==,都是比較地址值。但一般的類會覆蓋(override)這個方法.用于判斷對象的等價性。例如 String類,兩個引用所指向的String 都是"abc",但可能出現他們實際對應的對象并不是同一個(和jyvm 實現方式有關),因此用判斷他們可能不相等,但用equals判斷一定是相等的。

      3? 是否可以繼承String類?

      String類是final修飾的類,故不可以繼承。

      拓展,String為什么要用final修飾?

      答:為了安全和效率。

      因為只有當字符串是不可變的,字符串池才有可能實現。字符串池的實現可以在運行時節約很多heap空間,因為不同的字符串變量都指向池中的同一個字符串。但如果字符串是可變的,那么String interning將不能實現,因為這樣的話,如果變量改變了它的值,那么其它指向這個值的變量的值也會一起改變。

      如果字符串是可變的,那么會引起很嚴重的安全問題。譬如,數據庫的用戶名、密碼都是以字符串的形式傳入來獲得數據庫的連接,或者在socket編程中,主機名和端口都是以字符串的形式傳入。因為字符串是不可變的,所以它的值是不可改變的,否則黑客們可以鉆到空子,改變字符串指向的對象的值,造成安全漏洞。

      因為字符串是不可變的,所以是多線程安全的,同一個字符串實例可以被多個線程共享。這樣便不用因為線程安全問題而使用同步。字符串自己便是線程安全的。

      因為字符串是不可變的,所以在它創建的時候HashCode就被緩存了,不需要重新計算。這就使得字符串很適合作為Map 中的鍵,字符串的處理速度要快過其它的鍵對象。這就是HashMap中的鍵往往都使用字符串。

      二、集合

      1.掌握Collection和Map的繼承體系。

      鏈表

      鏈表是一種物理上非連續,非順序的存儲結構,數據元素之間的順序是通過每個元素的指針關聯的

      鏈表有一系列節點組成,每個節點一般至少會包含兩部分的信息:

      1.元素數據

      2.指向下一個元素的指針

      鏈表分類:

      1.單向鏈表和雙向鏈表

      2.靜態鏈表(數組實現),動態鏈表(指針)

      鏈表的操作:創建,插入,刪除,輸出

      鏈表的特點:

      1.物理空間不連續,空間開銷更大

      2.在運行時可以動態添加

      3.查找元素需要順序查找

      2. 常見面試題

      (1)ArrayList和 LinkList的區別

      ArrayList(數組結構)︰

      優點: get和set調用花費常數時間,也就是查詢的速度快;

      缺點∶新項的插入和現有項的刪除代價昂貴,也就是添加刪除的速度慢

      LinkedList(鏈表結構)︰

      優點:新項的插入和和現有項的刪除開銷很小,即添加和刪除的速度快

      缺點:對get和set的調用花費昂貴,不適合做查詢

      面試中經常問到一些深入的東西,比如︰

      1.是否保證線程安全:ArrayList和 LinkedList 都是不同步的,也就是不保證線程安全;

      2.? ?底層數據結構:Arraylist底層使用的是Object 數組; LinkedList底層使用的是雙向循環鏈表數據結構;

      3.? ?插入和刪除是否受元素位置的影響:

      ①. ArrayList 采用數組存儲.所以插入和刪除元素的時間復雜度受元素位置的影響。比如:執行add(E e)方法的時候, ArrayList 會默認在將指定的元素追加到此列表的末尾,這種情況時間復雜度就是O(1)。但是如果要在指定位置i插入和刪除元素的話(add(int index,E element))時間復雜度就為O(n-i)。因為在進行上述操作的時候集合中第 i 和第 i 個元素之后的(n-i)個元素都要執行向后位/向前移一位的操作。

      ②.LinkedList采用鏈表存儲,所以插入,刪除元素時間復雜度不受元素位置的影響,都是近似 O(1)而數組為近似 O(n)。

      4.是否支持快速隨機訪問:LinkedList 不支持高效的隨機元素訪問,而ArrayList實現了RandmoAccess接口,所以有隨機訪問功能??焖匐S機訪問就是通過元素的序號快速獲取元素對象(對應于get(int index)方法)。

      5.內存空間占用:ArrayList 的空間浪費主要體現在在list列表的結尾會預留一定的容量空間,而LinkedList的空間花費則體現在它的每一個元素都需要消耗比 ArrayList 更多的空間(因為要存放直接后繼和直接前驅以及數據)。

      (2)HashMap底層原理

      HashMap實際上是一個"鏈表散列"的數據結構,即數組和鏈表的結合體。這是 jdk8之前的實現方式,但是在JDK8后對 HashMap進行了底層優化,改為了由數組+鏈表+紅黑樹實現,主要的目的是提高查找效率。

      HashMap的主結構類似于一個數組,添加值時通過key確定儲存位置.

      每個位置是一個Entry的數據結構,該結構可組成鏈表.當發生沖突時,相同hash值的鍵值對會組成鏈表.這種數組+鏈表的組合形式大部分情況下都能有不錯的性能效果,Java6、7就是這樣設計的.

      然而,在極端情況下,一組(比如經過精心設計的)鍵值對都發生了沖突,這時的哈希結構就會退化成一個鏈表,使HashMap 性能急劇下降.

      所以在Java8中,HashMap的結構實現變為數組+鏈表+紅黑樹

      可以看出,HashMap底層就是一個數組結構。

      數組中的每一項又是一個鏈表.當新建一個HashMap時,就會初始化一個數組.

      簡單地說,HashMap 在底層將key-value 當成一個整體進行處理,這個整體就是一個Entry對象。HsahMap底層采用一個Entry[] 數組來保存所有的 key-value 對,當需要存儲一個 Entry 對象時,會根據hash 算法來決定其在數組的存儲位置,在根據equals 方法決定其在該數組位置上的鏈表中的存儲位置;當需要取出一個Entry 時,也會根據hash算法找到其在數組中的存儲位置,再根據equals 方法從該位置上的鏈表中取出該 Entry。

      3)HashMap 的put()和get()原理

      1)java7及以前:

      get()方法

      首先判斷輸入的key是否為空,如果為空,從hashmap數組下標為0的位置獲取值返回。如果不為空,根據key的值,從 hashmap數組中獲取對應的entry對象,判斷這個對象是否為空,為空返回null,不為空返回對應的value值,獲取 value的方法中 key為空和不為空時的方法里都先判斷數組中的元素是否為0 ,如果不為0,才繼續查找

      put()方法

      調用put方法的時候首先判斷hashmap 數組是否為空數組

      如果為空,進行初始化,判斷 key的值是否是null,如果是null,把對應的 value值存進數組中下標為0的位置,計算key的 hash值,并計算出下標,遍歷下標對應的鏈表,匹配 hash值和key 的值,如果存在,則覆蓋,返回舊值,如果不存在,新添加一個,返回null

      最后判斷數組大小,是否擴容

      2) java8 get()方法

      對輸入的key的值計算hash值,

      首先判斷hashmap中的數組是否為空和數組的長度是否為0,如果為空和為0,則直接放回null

      如果不為空和0,計算 key對應的數組下標,判斷對應位置上的第一個node是否滿足條件,如果滿足條件,直接返回

      如果不滿足條件,判斷當前node是否是最后一個,如果是,說明不存在key,則返回null如果不是最后一個,判斷是否是紅黑樹,如果是紅黑樹,則使用紅黑樹的方式獲取對應的key,如果不是紅黑樹,遍歷鏈表是否有滿足條件的,如果有,直接放回,否則返回null

      put()方法

      首先計算key的hash值,獲取hashmap 中的數組和數組長度,如果數組為空,初始化計算key的下標

      數組對應下標的位置是否為空,如果為空,則先添加一個,放在這個下標位置,然后判斷數組內元素是否大于閾值,如果大于,則進行擴容

      如果數組對應下標不為空,則先獲取對應鏈表的第一個值,判斷 hash和key是否相同,如果相同,新value替換舊value,返回舊value

      如果第一個值key不相同,判斷當前鏈表是否是紅黑樹,如果是紅黑樹,調用紅黑樹鏈表put的方法

      如果也不是紅黑樹,遍歷鏈表,判斷當前node是否是最后一個,如果是,說明鏈表中沒有新添加的key,則在最后面新添加一個,然后判斷是否超過閾值(8-1),如果超過,則轉換成紅黑樹

      如果不是最后一個,說明在中間已經存在key了,把新值賦值給舊值,并返回舊值,判斷是否需要擴容。

      注:哈希碰撞:當兩個不同的鍵對象的hashcode 相同時,它們會儲存在同一個bucket位置的鏈表中。鍵對象的equals()方法用來找到鍵值對()。

      (4)HashMap 的resize過程是什么樣的(擴容)?

      HashMap在put 的時候會先檢查當前數組的length,如果插入新的值的時候使得length > 0.75f * size (f 為加載因子,可以在創建 hashMap時指定)的話,會將數組進行擴容為當前容量的2倍。擴容之后必定要將原有hashMap中的值拷貝到新容量的hashMap里面,HashMap 默認的容量為16,加載因子為0.75,也就是說當HashMap中 Entry的個數超過16*0.75=12時,會將容量擴充為16*2=32,然后重新計算元素在數組中的位置,這是一個非常耗時的操作,所以我們在使用HashMap的時候如果能預先知道Map中元素的大小,預設其大小能夠提升其性能。

      resize 代碼:

      //HashMap 數組擴容

      void resize(int newCapacity) {

      Entry[]oldTable = table;

      int oldCapacity = oldTable.length;

      如果當前的數組長度已經達到最大值,則不在進行調整

      if (oldCapacity == MAXIMUM_CAPACITY){

      threshold = Integer.MAX_VALUE;

      return;

      }

      //根據傳入參數的長度定義新的數組

      Entry[]newTable = new Entry[newCapacity];

      //按照新的規則,將舊數組中的元素轉移到新數組中

      transfer(newTable);

      table = newTable;//更新臨界值

      threshold = (int)(newCapacity * loadFactor);

      }

      舊數組中元素往新數組中遷移

      void transfer(Entry[]newTable) {

      //舊數組

      Entry[]src = table;//新數組長度

      int newCapacity = newTable.length; //遍歷舊數組

      for (int j = o; j < src.length; j++){

      Entry e = src[];

      src[i] = null;

      do {

      Entry next = e.next;

      int i = indexFor(e.hash, newCapacity);//放在新數組中的index位置

      e.next = newTable[li];//實現鏈表結構,新加入的放在鏈頭,之前的的數據放在鏈尾

      newTable[i]= e;

      e = next;

      }while (e != null);

      }

      }

      }

      這是1.7版本中的代碼,1.8中引入了紅黑樹的概念,代碼會相對復雜一些。

      通俗的講,如果數組中元素的容量超過閾值0.75,會觸發擴容,擴容是先把源數組放進一個臨時數組中,獲取老數組的長度,通過老數組長度乘⒉獲取新數組長度,并創建新數組,把臨時數組中的數據通過重新計算下表,存進擴容后的數組中.

      (5)HashMap在擴容的時候為什么容量都是原來的2倍,即容量為2^n

      HashMap 在計算數組中key的位置時,使用的算法為︰

      /* * Returns index for hash code h.*/

      static int indexFor(int h, int length){

      // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2"; return h & (length-1);}

      即對key 的 hashcode 與當前數組容量-1進行與操作我們假設有一個容量為分別為15和16 的hashMap ,有兩個key的 hashcode分別為4和5,進行indexFor操作之后:

      H & (length -1) hash & table.length-1 4&(15-1)0100 & 1110=01005 & ( 15-1 ) 0101 & 1110=01004 & (16- 1)0100 & 1111 =0100 5&( 16-1 )? 0101 &1111= 0101

      我們能夠看到在容量為16時進行indexFor操作之后獲得相同結果的幾率要比容量為15時的幾率要小,這樣能夠減少出現hash沖突的幾率,從而提高查詢效率。2^n是一個非常神奇的數字。

      (6)HashMap你經常用在那個地方?

      因為HashMap 的好處非常多,我曾經在電子商務的應用中使用HashMap作為緩存。因為金融領域非常多的運用Java,也出于性能的考慮,我們會經常用到HashMap和ConcurrentHashMap。另外, controller層向前臺返回數據可以用map封裝數據,還有mybatis中的map可以作為參數或者封裝結果集

      (7)HashMap和 Hashtable的區別?

      1.hashMap去掉了HashTable 的contains方法,但是加上了containsValue ()和containsKey ()方法。

      2.hashTable同步的,而 HashMap是非同步的,效率上逼hashTable 要高。

      3.hashMap 允許空鍵值,而hashTable 不允許。一般用hashmap 代替 hashtable

      注意:

      TreeMap︰非線程安全基于紅黑樹實現。TreeMap沒有調優選項,因為該樹總處于平衡狀態。

      Treemap:適用于按自然順序或自定義順序遍歷鍵(key)。

      (8)List、Map、Set三個接口,存取元素時,各有什么特點?

      List以特定次序來持有元素,可有重復元素;

      Set無法擁有重復元素,內部排序(無序);Map 保存key-value 值, value 可多值。

      Hashmap 可以存重復鍵,重復值嗎?不能存重復鍵

      (9)HashSet是如何保證元素唯一性的呢?

      是通過元素的兩個方法, hashCode和 equals來完成。

      (10)TreeSet怎么對集合中的元素進行排序?

      TreeSet底層數據結構是二叉樹。保證元素唯一性的依據: compareTo方法return 0.

      1):讓元素自身具備比較性,需要元素對象實現 Comparable接口,覆蓋compareTo方法。

      2):讓集合自身具備比較性,需要定義一個實現了Comparator接口的比較器,并覆蓋compare方法

      (11)map集合的兩種取出方式?

      //第一種:普遍使用,二次取值。通過Map.keySe()t 遍歷key和value

      for (String key : map.keySet()) {

      System.out.println("key= "+ key + " and value= " + map.get(key));

      }

      //第二種:推薦,尤其是容量大時。通過Map.entrySet遍歷key和 value

      for (Map.Entry entry : map.entrySet()){

      Java SE 之 String 、 Object 類的基本方法 和 集合 丨【奔跑吧!JAVA】(java培訓學校)

      System.out.printIn("key= " + entry.getKey() + " and value= " + entry.getValue();

      }

      注意map集合不能直接使用增強for 循環,因為增強 for 循環底層是使用的迭代器,而map沒有實現迭代器接口,所以不能用迭代器直接遍歷或者增強for循環遍歷

      (12)Collection和 Collections的區別?

      Collection是集合類的上級接口,繼承與他的接口主要有Set和List.

      Collections是工具類,有很多對集合操作的方法

      (13)請問ArrayList、HashSet、HashMap是線程安全的嗎?如果不是我想要線程安全的集合怎么辦?

      我們都看過一些集合的源碼(如果沒有那就看看吧),每個方法都沒有加鎖,顯然都是線程不安全的。話又說過來,如果他們安全了也就沒第二問了。在集合中Vector和 HashTable倒是線程安全的。你打開源碼會發現其實就是把各自核心方法添加上了synchronized 關鍵字。

      Collections工具類提供了相關的 API,可以讓上面那3個不安全的集合變為安全的。

      1.//? ? collections.synchronizedcollection (c)

      2.// ? ?collections.synchronizedList (list)

      3.// ? ?collections.synchronizedMap (m)

      4.// ? ?collections.synchronizedset (s)

      上面幾個函數都有對應的返回值類型,傳入什么類型返回什么類型。打開源碼其實實現原理非常簡單,就是將集合的核心方法添加上了synchronized關鍵字。

      (14)ConcurrentHashMap 的工作原理及代碼實現

      HashTable里使用的是synchronized關鍵字,這其實是對對象加鎖,鎖住的都是對象整體,當Hashtable的大小增加到一定的時候,性能會急劇下降,因為迭代時需要被鎖定很長的時間。ConcurrentHashMap算是對上述問題的優化,其構造函數如下,默認傳入的是16,0.75,16。

      ConcurrentHashMap引入了分割(Segment),上 面代碼中的最后一行其實就可以理解為把一個大的Map拆分成N個小的HashTable,在put方法中,會根據hash(paramK.hashCode()來決定具體存放進哪個Segment,如果查看Segment的put操作,我們會發現內部使用的同步機制是基于lock操作的,這樣就可以對Map的一部分(Segment) 進行上鎖,這樣影響的只是將要放入同一個Segment的元素的put操作,保證同步的時候,鎖住的不是整個Map (HashTable 就是這么做的),相對于HashTable提高了多線程環境下的性能,因此HashTable已經被淘汰了。

      【奔跑吧!JAVA】有獎征文火熱進行中:https://bbs.huaweicloud.com/blogs/265241

      Java

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:word文檔特殊符號在哪里找?(word文檔怎么找特殊符號)
      下一篇:Excel2010合并工作表的方法步驟圖(Excel2010合并工作表)
      相關文章
      最新亚洲卡一卡二卡三新区| 亚洲AV成人影视在线观看| www.91亚洲| 国产成人亚洲综合无| 亚洲影院天堂中文av色| 亚洲mv国产精品mv日本mv| 亚洲国产美女精品久久| 亚洲福利一区二区三区| 亚洲欧洲尹人香蕉综合| 亚洲无圣光一区二区| 亚洲一区二区三区四区视频 | 国产精品亚洲а∨天堂2021| 亚洲成熟丰满熟妇高潮XXXXX| 亚洲国产精品免费在线观看| 亚洲国产片在线观看| 亚洲中文字幕一二三四区苍井空| 亚洲人成www在线播放| 亚洲最大中文字幕无码网站| 亚洲精品无码永久在线观看男男| 亚洲性色AV日韩在线观看| 亚洲国产欧美国产综合一区| 国产亚洲精品AAAA片APP| 亚洲?v无码国产在丝袜线观看| 亚洲精品国精品久久99热 | 91精品国产亚洲爽啪在线影院| 77777_亚洲午夜久久多人| 亚洲精品中文字幕无码AV| 亚洲国产av一区二区三区丶| 亚洲成_人网站图片| 亚洲国产欧洲综合997久久| 亚洲国产av一区二区三区| 伊人久久亚洲综合| 久久精品国产69国产精品亚洲| 久久91亚洲人成电影网站| 91亚洲国产成人精品下载| 亚洲fuli在线观看| 亚洲第一成年网站视频| 亚洲性日韩精品国产一区二区| 亚洲精品乱码久久久久久中文字幕| 婷婷精品国产亚洲AV麻豆不片| 亚洲欧洲在线播放|