java安全编程规范标准
java安全编程目录
一、方法规范
1. 实现compareTo()方法时遵守常规合约
简介
选择实现了Comparable
接口展示了一种责任,那就是对compareTo()
方法的实现维持了该方法的合约。库中的类,例如TreeSet
和TreeMap
,接受Comparabled
对象,并使用对应的compareTo()
方法来整理对象。然而,一个实现了compareTo()
方法的类可能会以一种意外的方式生成非期望的结果。
Java SE 6 API 规定了compareTo()
方法的使用合约(以下的叙述中,符号sgn
代表数学中的signum
函数,根据表达式的数值是负数、0或正数,这个函数返回-1、0 或 1):
-
必须确保对于所有的
x
和y
,sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
成立。(这意味着当y.compareTo(x)
抛出异常时,x.compareTo(y)
也必须抛出异常。) -
必须确保关联是可传递的:
(x.compareTo(y) > 0 && y.compareTo(z) >0)
意味着x.compareTo(z)> 0
; -
必须确保
x.compareTo(y)==0
暗示对所有的z
,存在sgn(x.compareTo(z))==sgn(y.compareTo(z))
。 -
强烈建议
(x.compareTo(y)==0)==x.equals(y)
。即是compareTo()
函数返回的两个对象相当,其equals()
方法返回的这两个对象也应该相等。一般来说,任何实现了comparable
接口并违反了这个条件的类应该明确指出这一点。建议使用这样的说法:“注意:此类有着和equals不一致的自身次序。”
需要说明的是,compareTo()
方法的实现一定不能违反前三个条件中的任何一条。实现应该尽可能的遵守第四个条件。
违反规则代码示例
public class Solution2 implements Comparable{
public enum Roshambo {ROCK,PAPER,SCISSORS};
private Roshambo value;
Solution2(Roshambo val){
this.value = val;
}
@Override
public int compareTo(Object o) {
if (!(o instanceof Solution2)) {
throw new ClassCastException();
}
Solution2 t = (Solution2)o;
return (value == t.value) ? 0
: (value == Roshambo.ROCK && t.value == Roshambo.PAPER) ? -1
: (value == Roshambo.PAPER && t.value == Roshambo.SCISSORS) ? -1
: (value == Roshambo.SCISSORS && t.value ==Roshambo.ROCK) ? -1
: 1;
}
}
问题说明
上面示例中,因为石头赢了剪刀,剪刀赢了布,而是石头确输给了布,所以这个游戏违反了要求中的传递特性。
修正办法
定义成非compare()
方法即可,此方法不需要满足既定的规则
2、确保比较中的关键码是可不变的
简介
用于有序集合和映射中关键码的对象应该是不可变的。当一些字段必须是可变的时候,equals()
,hashcode()
和compareTo()
方法在比较对象时必须只考虑不变的状态。违反了这条规则会造成集合有不一致的次序。java.util.Interface Set<E>
和java.util.Interface Map<K,V>
的文档中对此提出了警告
不符合规则的示例一
equals()方法中用于比较的字段是可被外界输入改变的。
修正办法
在比较的方法中使用定义为final 字段的声明,此声明在初始化后不会改变,符合规则。
不符合规则的实例二
许多程序员对由序列化引起的散列码易变性会感到惊讶。hashCode()
方法的合约没有要求散列码在应用程序的不同执行中保持不变。同样,当序列化一个对象并随后将其反序列化时,该对象反序列化后的散列码可能会和最初的散列码不一致。
如下的不符合规则的代码示例使用MyeKey
类作为Hashtable
的关键码。Mykey
类覆写了object.equals()
,但是使用了默认的hashCode()
方法。根据Java API中的描述:
为了成功的在散列表中存储和读取对象,作为关键码的对象必须实现hashCode
和equals
方法。
public class Solution1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Hashtable<Key,String> ht = new Hashtable<Key,String>();
Key key = new Key();
ht.put(key,"value");
System.out.println("Entry:" + ht.get(key));
FileOutputStream fos = new FileOutputStream(new File("hashtabel.ser"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(ht);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream(new File("hashtabel.ser"));
ObjectInputStream ois = new ObjectInputStream(fis);
Hashtable<Key,String> ht_in = (Hashtable<Key,String>)ois.readObject();
ois.close();
if(ht_in.contains("value")) {
System.out.println("value is fand in");
}
System.out.println("---------" );
System.out.println("object is found" + ht_in.get(key));
System.out.println("-----------" );
if(ht_in.get(key) == null) {
System.out.println("object is not found"); //正常输出
} else {
System.out.println("object is found" + ht_in.get(key)); //未输出
}
}
}
class Key implements Serializable {
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
}
修正办法
在Key类中覆写hashcode()方法,即可解决这个问题。
public class Solution1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Hashtable<Key,String> ht = new Hashtable<Key,String>();
Key key = new Key(5);
ht.put(key,"value");
System.out.println("Entry:" + ht.get(key));
FileOutputStream fos = new FileOutputStream(new File("hashtabel.ser"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(ht);
oos.flush();
oos.close();
FileInputStream fis = new FileInputStream(new File("hashtabel.ser"));
ObjectInputStream ois = new ObjectInputStream(fis);
Hashtable<Key,String> ht_in = (Hashtable<Key,String>)ois.readObject();
ois.close();
if(ht_in.contains("value")) {
System.out.println("value is fand in");
}
System.out.println("---------" );
System.out.println("object is found" + ht_in.get(key));
System.out.println("-----------" );
if(ht_in.get(key) == null) {
System.out.println("object is not found"); //未输出
} else {
System.out.println("object is found" + ht_in.get(key)); //正常输出
}
}
}
class Key implements Serializable {
//Does not override hashCode()
private int num;
Key(int num){
this.num = num;
}
@Override
public int hashCode() {
return 31 * num;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Key)){
return false;
}
return ((Key) obj).num == num;
}
}
3.定义了equals()方法的类必须定义hashCode()方法
简介
覆写了Object.equals()
方法的类必须同时覆写Object.hashCode()
方法。java.lang.Object
类要求当两个对象用equals()
方法进行比较并得到等同的结果时,对这两个对象调用hashCode()
方法时必须产生一样的整数结果。equal()
方法用于判定两个对象实例逻辑上的等同性。因此对于所有等同的对象,hashCode()
方法必须返回相同的数值。没能遵守这一合约是一个常见的产生错误地原因
错误示例一
public class Solution1 {
private final int number;
public Solution1(int number) {
this.number = number;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Solution1)) {
return false;
}
return ((Solution1) o).number == number;
}
public static void main(String[] args) {
Map<Solution1,String> m = new HashMap<Solution1, String>();
m.put(new Solution1(100),"sssssss");
System.out.println(m.get(new Solution1(100))); //输出为null
}
}
问题说明
造成这一错误行为的原因是CreditCrad
类覆写了equals()
方法,但没有覆写hashCode()
方法。默认的hashCode()
方法对不同的对象返回一个不同的数值。
修正办法
public class Solution1 {
private final int number;
public Solution1(int number) {
this.number = number;
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof Solution1)) {
return false;
}
return ((Solution1) o).number == number;
}
@Override
public int hashCode() {
int result = 17;
result = 30 * result + number;
return result;
}
public static void main(String[] args) {
Map<Solution1,String> m = new HashMap<Solution1, String>();
m.put(new Solution1(100),"sssssss");
System.out.println(m.get(new Solution1(100))); //输出为 sssssss
}
}
本文地址:https://blog.csdn.net/weixin_40563850/article/details/107620292