java安全編碼指南之:方法編寫指南
阿新 • • 發佈:2020-10-08
[toc]
# 簡介
java程式的邏輯是由一個個的方法組成的,而在編寫方法的過程中,我們也需要遵守一定的安全規則,比如方法的引數進行校驗,不要在assert中新增業務邏輯,不要使用廢棄或者過期的方法,做安全檢查的方法一定要設定為private等。
今天我們再來深入的探討一下,java方法的編寫過程中還有哪些要注意的地方。
# 不要在建構函式中呼叫可以被重寫的方法
一般來說在建構函式中只能呼叫static,final或者private的方法。為什麼呢?
如果父類在執行建構函式的時候呼叫了一個可以被重寫的方法,那麼在該方法中可能會使用到未初始化的資料,從而導致執行時異常或者意外結束。
另外,還可能到方法獲取到未初始化完畢的例項,從而導致資料不一致性。
舉個例子,我們定義了一個Person的父類:
~~~java
public class Person {
public void printValue(){
System.out.println("this is person!");
}
public Person(){
printValue();
}
}
~~~
然後定義了一個Boy的子類,但是在Boy子類中,重新了父類的printValue方法。
~~~java
public class Boy extends Person{
public void printValue(){
System.out.println("this is Boy!");
}
public Boy(){
super();
}
public static void main(String[] args) {
Person persion= new Person();
Boy boy= new Boy();
}
}
~~~
輸出結果:
~~~java
this is person!
this is Boy!
~~~
可以看到Boy呼叫了自己重寫過的printValue方法。
> 注意,這裡並不是說會產生語法錯誤,而是這樣會導致業務邏輯看起來非常混亂。
怎麼解決呢?簡單辦法就是將Person中的printValue置位final即可。
# 不要在clone()方法中呼叫可重寫的方法
同樣的,我們在定義clone方法的時候也不要呼叫可重寫的方法,否則也會產生意想不到的變化。
還是上面的例子,這次我們添加了clone方法到Person類:
~~~java
public Object clone() throws CloneNotSupportedException {
Person person= (Person)super.clone();
person.printValue();
return person;
}
~~~
接下來我們新增clone方法到Boy類:
~~~java
public Object clone() throws CloneNotSupportedException {
Boy clone = (Boy) super.clone();
clone.printValue();
return clone;
}
~~~
因為在clone方法中呼叫了可重寫的方法,從而讓系統邏輯變得混亂。不推薦這樣使用。
# 重寫equals()方法
考慮一下父類和子類的情況,如果在父類中我們定義了一個equals方法,這個方法是根據父類中的欄位來進行比較判斷,最終決定兩個物件是否相等。
如果子類添加了一些新的欄位,如果不重寫equals方法,而是使用父類的equals方法,那麼就會遺漏子類中新新增的欄位,最終導致equals返回意想不到的結果。
所以一般來說,子類需要重寫equals方法。
如果重新equals方法,需要滿足下面幾個特性:
1. reflexive反射性
對於一個Object a來說,a.equals(a)必須成立。
2. symmetric對稱性
對於一個Object a和Object b來說,如果a.equals(b)==true,那麼b.equals(a)==true一定成立。
3. transitive傳遞性
對於Object a,b,c來說,如果a.equals(b)==true,b.equals(c)==true,那麼a.equals(c)==true一定成立。
4. consistent一致性
對於Object a,b來說,如果a和b沒有發生任何變化,那麼a.equals(b)的結果也不能變。
5. 對於非空的引用a,a.equals(null) 一定要等於false
具體程式碼的例子,這裡就不寫了,大家可以自行練習一下。
# hashCode和equals
hashCode是Object中定義的一個native方法:
~~~java
@HotSpotIntrinsicCandidate
public native int hashCode();
~~~
根據Oracle的建議,如果兩個物件的equals方法返回的結果是true,那麼這兩個物件的hashCode一定要返回同樣的int值。
為什麼呢?
我們看下下面的一個例子:
~~~java
public class Girl {
private final int age;
public Girl(int age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Girl)) {
return false;
}
Girl cc = (Girl)o;
return cc.age == age;
}
public static void main(String[] args) {
HashMap hashMap= new HashMap<>();
hashMap.put(new Girl(20), 20);
System.out.println(hashMap.get(new Girl(20)));
}
}
~~~
上面的Girl中,我們定義了equals方法,但是並沒有重寫hashCode,最後返回的結果是null。
因為我們new了兩次Girl這個物件,最後導致native方法中兩個不同物件的hashCode是不一樣的。
我們可以給Girl類中新增一個hashCode方法:
~~~java
public int hashCode() {
return age;
}
~~~
這樣就可以返回正確的值。
# compareTo方法的實現
我們在實現可比較類的時候,通常需要實現Comparable介面。Comparable介面定義了一個compareTo方法,用來進行兩個物件的比較。
我們在實現compareTo方法的時候,要注意保證比較的通用規則,也就是說,如果x.compareTo(y) > 0 && y.compareTo(z) > 0 那麼表示 x.compareTo(z) > 0.
所以,我們不能使用compareTo來實現特殊的邏輯。
最近看了一個日本的電影,叫做dubo默示錄,裡面有一集就是石頭,剪刀,布來判斷輸贏。
當然,石頭,剪刀,布不滿足我們的通用compareTo方法,所以不能將邏輯定義在compareTo方法中。
本文的程式碼:
[learn-java-base-9-to-20/tree/master/security](https://github.com/ddean2009/learn-java-base-9-to-20/tree/master/security)
> 本文已收錄於 [http://www.flydean.com/java-security-code-line-method/](http://www.flydean.com/java-security-code-line-method/)
>
> 最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
>
> 歡迎關注我的公眾號:「程式那些事」,懂技術,更