摘要:在Java語言的日常程式設計中,也存在着容易被忽略的細節,這些細節可能會導緻程式出現各種Bug。
本文分享自華為雲社群《Java程式設計中容易忽略的細節總結丨【奔跑吧!JAVA】》,作者:jackwangcumt 。
Java語言建構的各類應用程式,在人類的日常生活中占用非常重要的地位,各大IT廠商幾乎都會使用它來建構自己的産品,為客戶提供服務。作為一個企業級應用開發語言,穩定和高效的運作,至關重要。在Java語言的日常程式設計中,也存在着容易被忽略的細節,這些細節可能會導緻程式出現各種Bug,下面就對這些細節進行一些總結:
在很多場景中,我們都需要判斷兩個對象是否相等,一般來說,判定兩個對象的是否相等,都是依據其值是否相等,如兩個字元串a和b的值都為"java",則我們認為二者相等。在Java中,有兩個操作可以判斷是否相當,即==和equals,但二者是有差別的,不可混用。下面給出示例:
字元串a和b的字面值都為"java",用a == b判斷則輸出false,即不相等,而a.equals(b)則輸出true,即相等。這是為什麼呢?在Java中,String是一個不可變的類型,一般來說,如果兩個String的值相等,預設情況下,會指定同一個記憶體位址,但這裡字元串String b用new String方法強制生成一個新的String對象,是以,二者記憶體位址不一緻。由于 == 需要判斷對象的記憶體位址是否一緻,是以傳回false,而equals預設(override後可能不一定)是根據字面值來判斷,即相等。
下面再給出一個示例:
這是由于Java中的Integer數值的範圍為-128到127,是以在這範圍内的對象的記憶體位址是一緻的,而超過這個範圍的數值對象的記憶體位址是不一緻的,是以300這個數值在 == 比較下,傳回false,但在equals比較下傳回true。
在很多場景中,我們需要根據輸入參數的範圍來分别進行處理,這裡除了可以使用if ... else ...語句外,還可以使用switch語句。在switch語句中,會羅列出多個分支條件,并進行分别處理,但如果稍有不注意,就可能丢失關鍵字break語句,進而出現預期外的值。下面給出示例:
如果我們使用如下語句進行調用:
則我們預期傳回"0",但是卻傳回"0" "1"。這是由于case 0 分支下缺少break關鍵字,則雖然程式比對了此分支,但是卻能穿透到下一個分支,即case 1分支,然後遇到break後傳回值。
字元串的拼接操作,是非常高頻的操作,但是如果涉及的拼接量很大,則如果直接用 + 符号進行字元串拼接,則效率非常低下,程式運作的速度很慢。下面給出示例:
上述示例分别在循環體中用 + 和 StringBuilder進行字元串拼接,并統計了運作的時間(毫秒),下面給出模拟電腦上的運作結果:
由此可見,使用StringBuilder建構字元串速度相比于 + 拼接,效率上高出太多。究其原因,就是因為Java語言中的字元串類型是不可變的,是以 + 操作後會建立一個新的字元串,這樣會涉及到大量的對象建立工作,也涉及到垃圾回收機制的介入,是以非常耗時。
有些情況下,我們需要從一個集合對象中删除掉特定的元素,如從一個程式設計語言清單中删除java語言,則就會涉及到此種場景,但是如果處理不當,則會抛出ConcurrentModificationException異常。下面給出示例:
運作上述方法,會抛出錯誤,此時可以用如下方法進行解決,即用疊代器iterator,具體如下所示:
在方法中,首先應該對參數的合法性進行驗證,第一需要驗證參數是否為null,然後再判斷參數是否是預期範圍的值。如果不首先進行null判斷,直接進行參數的比較或者方法的調用,則可能出現null引用的異常。下面給出示例:
如果此時我們用如下方法進行調用,則抛出異常:
這是由于假設了words不為null,則可以調用String對象的equals方法。下面可以稍微進行一些修改,如下所示:
則此時執行則可以正确運作:
前面提到,equals方法可以從字面值上來判斷兩個對象是否相等。一般來說,如果兩個對象相等,則其hash code相等,但是如果hash code相等,則兩個對象可能相等,也可能不相等。這是由于Object的equals方法和hashCode方法可以被Override。下面給出示例:
執行上述代碼,由于hashCode方法被Override,每次傳回随機的hash Code值,則意味着兩個對象的hash code不一緻,那麼equals判斷則傳回false,雖然二者的字面值都為"CUMT"。
我們知道,計算機的記憶體是有限的,如果Java建立的對象一直不能進行釋放,則新建立的對象會不斷占用剩餘的記憶體空間,最終導緻記憶體空間不足,抛出記憶體溢出的異常。記憶體異常基本的單元測試不容易發現,往往都是上線運作一定時間後才發現的。下面給出示例:
此示例中,有一個for無限循環,它會一直建立一個對象,并添加到properties容器中,如果MemoryLeakDemo類未給出自己的equals方法和hashCode方法,那麼這個對象會被一直添加到properties容器中,最終記憶體洩漏。但是如果定義了自己的equals方法和hashCode方法(被注釋的部分),那麼新建立的MemoryLeakDemo執行個體,由于key值一緻,則判定為已存在,則不會重複添加,此時則不會出現記憶體溢出。
點選關注,第一時間了解華為雲新鮮技術~